将来,为了这个目的,我们可能会得到一个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和其他奇怪的东西.
如果您使用unlikely
hint以及godbolt支持的所有icc版本(13,14,17),也是如此.所以代码生成是严格的,不管提示,不管实际的运行时行为.
当使用提示时,gcc和cl Ne都不会遭受任何退化.
怎么了?
至少在我尝试的第一个和后来的例子中.
解决方法
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 __builtin_expect
is full support or just compatibility support.
这个问题似乎更适合Official ICC forum.
我试过posting this topic there,但我不知道我做得很好(我已经被SO弄坏了).
如果他们回答我,我会更新这个答案.
编辑我在英特尔论坛上得到了一个答案,他们在跟踪系统中记录了这个问题.像今天一样,这似乎是一个错误.