lle*_*edr 7 c optimization assembly gcc x86-64
考虑以下C程序:
void bar();
void baz();
void foo( int a ) {
    if ( a ) {
        bar();
    }
    else {
        baz();
    }
}
在我的基于x86-64的计算机上,GCC使用-O1优化级别生成的指令给出:
 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
什么是主要从改变JUMP等于给不跳平等.我知道直到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另一个分支都有 lavel 的线)。在第二个片段中,一个分支指令被排序retq,而其他分支序列具有单跳转(不比第一个片段中的差)。请注意2条retq说明。
因此,正如我所看到的,这不是块重新排序的问题,je而是jne块重新排序的问题,因此分支是线性指令序列,其中一个指令序列输入时没有任何jump和完整的预测块功率节省。
关于“为什么 GCC 更喜欢一个分支而不是另一个分支”...我在文档中看到这可能是静态分支预测的结果(基于翻译单元内部的调用?)。无论如何,我建议您玩一下__builtin_expect以获得更详细的答案。