静态分支预测/ GCC优化
考虑下面的C程序:
void bar();
void baz();
void foo( int a ) {
if ( a ) {
bar();
}
else {
baz();
}
}
在我的基于x86-64的计算机上,具有-O1优化级别的GCC生成的指令给出:
0: sub $0x8,%rsp
4: test %edi,%edi
6: je 14 <foo+0x14>
8: mov $0x0,%eax
d: callq 12 <foo+0x12> # relocation to bar
12: jmp 1e <foo+0x1e>
14: mov $0x0,%eax
19: callq 1e <foo+0x1e> # relocation to baz
1e: add $0x8,%rsp
22: retq
而添加-freorder-blocks优化参数(包含在-O2中)则会将代码转换为:
0: sub $0x8,%rsp
4: test %edi,%edi
6: jne 17 <foo+0x17>
8: mov $0x0,%eax
d: callq 12 <foo+0x12> # relocation to baz
12: add $0x8,%rsp
16: retq
17: mov $0x0,%eax
1c: callq 21 <foo+0x21> # relocation to bar
21: add $0x8,%rsp
25: retq
主要是跳跃等于跳跃不等于变化。 我知道,直到Pentium 4,静态分支预测在条件前向分支上被认为没有被处理器采用(似乎静态预测在更进一步的英特尔处理器上变得随机),因此我想这种优化正在处理这个问题。
假设并参考jne优化版本,这意味着else块实际上被认为比程序流中的if块更可能被执行。
但是它究竟意味着什么? 由于编译器对foo函数中的某个值没有任何假设,因此这种可能性仅依赖于程序员的着作(实际上, if ( !a )
而不是if ( a )
和反向函数调用)可能会使用这种可能性。
这是否意味着将条件块视为例外情况(而不是正常执行流程)来处理它应该被视为一种良好的做法?
那是:
if ( !cond ) {
// exceptional code
}
else {
// normal continuation
}
代替:
if ( cond ) {
// normal continuation
}
else {
// exceptional code
}
(当然,可以使用相关块中的return语句来限制缩进大小)。
我曾经在ARM(7,9)上进行过大量的性能优化操作。 这是简单的C,足够愚蠢的编译器(SDT AFAIR)。 节省一些CPU资源的方法之一是分析if
分支并重写if
条件如此正常流量不破坏线性指令序列。 这有积极的影响,因为CPU预测模块更有效的使用和更有效的代码段内存缓存使用。
我认为在这里我们看到非常接近的优化。 在第一个代码片段中,两个分支都导致正常序列被破坏(一个分支与第6
相连,另一个分支与第12
相连)。 在第二个片段中,一个分支指令被排序到retq
而其他分支序列具有单个跳转(不比第一个片段差)。 请注意2 retq
指令。
所以我可以看到这不是je
或jne
的问题,而是块重新排序的问题,所以分支是线性指令序列,其中一个输入没有任何jump
和完整的预测块功率节省。
关于“为什么GCC比另一个更喜欢一个分支”......我在文档中看到这可能是静态分支预测的结果(基于翻译单元内的调用?)。 无论如何,我建议玩__builtin_expect
有更详细的答案。
上一篇: Static branch prediction / GCC optimization
下一篇: P4 merge error, No target file(s) in both client and branch view