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

jww*_*jww 6 c macros optimization code-generation built-in

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

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

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

它取决于优化级别吗?

代码生成的示例是什么?

Art*_*Art 10

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

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

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

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

这是一些示例代码:

int bar(int);
int
foo(int x)
{
    if (__builtin_expect(x>10, PREDICTION))
        return bar(10);
    return 42;
}
Run Code Online (Sandbox Code Playgroud)

编译(我使用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
Run Code Online (Sandbox Code Playgroud)

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

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

foo:
    cmpl    $10, %edi
    jg  .L2
    movl    $42, %eax
    ret
.L2:
    movl    $10, %edi
    jmp bar
Run Code Online (Sandbox Code Playgroud)

这是21.s:

foo:
    cmpl    $10, %edi
    jle .L6
    movl    $10, %edi
    jmp bar
.L6:
    movl    $42, %eax
    ret
Run Code Online (Sandbox Code Playgroud)

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