jww*_*jww 6 c macros optimization code-generation built-in
我知道内核非常使用likely
和unlikely
宏.宏的文档位于内置函数: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)
正如预期的那样,编译器重新安排了代码,以便我们不希望采用的分支在前向分支中完成.