possible(x)和__builtin_expect((x),1)

我知道内核使用了likelyunlikely宏。 宏的文档位于内置函数:long __builtin_expect(long exp,long c) 。 但他们并没有真正讨论细节。

编译器究竟如何处理likely(x)__builtin_expect((x),1)

它是由代码生成器还是优化器处理的?

它取决于优化级别吗?

代码生成的示例是什么?

我刚刚在gcc上测试了一个简单的例子。

对于x86,这似乎由优化器处理并依赖于优化级别。 虽然我猜这里的正确答案是“它取决于编译器”。

生成的代码取决于CPU。 有些cpus(sparc64立刻出现在我的脑海中,但我确定还有其他的)在条件分支指令上有标志告诉CPU如何预测它,因此编译器会根据构建的内容生成“预测真/预测假”指令在编译器中的规则和代码中的提示(如__builtin_expect )。

英特尔在此处记录了他们的行为: https : //software.intel.com/en-us/articles/branch-and-loop-reorganization-to-prevent-mispredicts 。 简而言之,英特尔CPU的行为是,如果CPU没有关于分支的先前信息,它将预测前向分支不太可能被采用,而可能采取向后分支(考虑循环与error handling)。

这是一些示例代码:

 int bar(int); int foo(int x) { if (__builtin_expect(x>10, PREDICTION)) return bar(10); return 42; } 

编译(我使用omit-frame-pointer使输出更具可读性,但我仍然在下面清理它):

 $ cc -S -fomit-frame-pointer -O0 -DPREDICTION=0 -o 00.s foo.c $ cc -S -fomit-frame-pointer -O0 -DPREDICTION=1 -o 01.s foo.c $ cc -S -fomit-frame-pointer -O2 -DPREDICTION=0 -o 20.s foo.c $ cc -S -fomit-frame-pointer -O2 -DPREDICTION=1 -o 21.s foo.c 

00.s和01.s之间没有区别,因此这表明这取决于优化(至少对于gcc)。

这是20.s的(清理过的)生成代码:

 foo: cmpl $10, %edi jg .L2 movl $42, %eax ret .L2: movl $10, %edi jmp bar 

这是21.s:

 foo: cmpl $10, %edi jle .L6 movl $10, %edi jmp bar .L6: movl $42, %eax ret 

正如预期的那样,编译器重新安排了代码,以便我们不希望采用的分支在前向分支中完成。