为什么icc无法以合理的方式处理编译时分支提示?
开发人员可以使用__builtin_expect
builtin来帮助编译器
understand分支可能走向哪个方向.
将来,为了这个目的,我们可能会得到一个standard attribute,但是截至今天,至少所有的clang,icc和gcc都支持非标准__builtin_expect. 但是,当您使用它时,icc似乎会产生奇怪的代码.也就是说,使用内置代码的代码严格比没有它的代码更差,无论预测的方向如何. 以下列玩具功能为例: int foo(int a,int b) { do { a *= 77; } while (b-- > 0); return a * 77; } 在三个编译器中,icc是唯一一个编译为optimal scalar loop的3个指令的编译器: foo(int,int): ..B1.2: # Preds ..B1.2 ..B1.1 imul edi,edi,77 #4.6 dec esi #5.12 jns ..B1.2 # Prob 82% #5.18 imul eax,77 #6.14 ret gcc和Clang都管理了轻松的解决方案,并使用了5条指令. 另一方面,当您使用可能或不太可能的宏在循环条件下,icc完全是脑力衰竭: #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) int foo(int a,int b) { do { a *= 77; } while (likely(b-- > 0)); return a * 77; } 这个循环在功能上等同于前一个循环(因为__builtin_expect只是返回其第一个参数),而icc produces some awful code: foo(int,int): mov eax,1 #9.12 ..B1.2: # Preds ..B1.2 ..B1.1 xor edx,edx #9.12 test esi,esi #9.12 cmovg edx,eax #9.12 dec esi #9.12 imul edi,77 #8.6 test edx,edx #9.12 jne ..B1.2 # Prob 95% #9.12 imul eax,77 #11.15 ret #11.15 该功能的大小增加到10个指令,而且(更糟糕的是),关键循环已经超过了一倍,达到了7个指令,长期的关键依赖链涉及到一个cmov和其他奇怪的东西. 如果您使用 当使用提示时,gcc和cl Ne都不会遭受任何退化. 怎么了? 至少在我尝试的第一个和后来的例子中. 解决方法
对我来说似乎是一个ICC错误.这段代码(
available on godbolt)
int c; do { a *= 77; c = b--; } while (likely(c > 0)); 只需使用辅助局部变量c,就可以产生没有edx = !!(esi> 0)模式的输出 foo(int,int): ..B1.2: mov eax,esi dec esi imul edi,77 test eax,eax jg ..B1.2 仍然不是最佳的(它可以没有eax),但是. 我不知道官方ICC policy about 这个问题似乎更适合Official ICC forum. 编辑我在英特尔论坛上得到了一个答案,他们在跟踪系统中记录了这个问题.像今天一样,这似乎是一个错误. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |