静态分支预测/ 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指令。

所以我可以看到这不是jejne的问题,而是块重新排序的问题,所以分支是线性指令序列,其中一个输入没有任何jump和完整的预测块功率节省。

关于“为什么GCC比另一个更喜欢一个分支”......我在文档中看到这可能是静态分支预测的结果(基于翻译单元内的调用?)。 无论如何,我建议玩__builtin_expect有更详细的答案。

链接地址: http://www.djcxy.com/p/73965.html

上一篇: Static branch prediction / GCC optimization

下一篇: P4 merge error, No target file(s) in both client and branch view