静态分支预测/ GCC优化

时间:2013-09-01 16:41:09

标签: c gcc optimization assembly 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

主要是从跳跃等于跳跃不等于的变化。我知道,直到奔腾4,条件前向分支上的静态分支预测被认为是处理器没有采用(似乎静态预测在其他英特尔处理器上变得随机),因此我想这个优化正在解决这个问题。

假设并且引用 jne 优化版本,这意味着 else 块实际上被认为比 更可能执行 if < / em>阻止程序流程。

但这究竟是什么意思?由于编译器对 foo 函数中的 a 值没有假设,因此这种可能性仅取决于程序员的文字(事实上谁可以使用{{1}而不是if ( !a )和反向函数调用)。

这是否意味着将 if 条件块视为例外情况(而不是正常的执行流程)应该被视为一种好的做法?

那是:

if ( a )

而不是:

if ( !cond ) {
    // exceptional code
}
else {
    // normal continuation
}

(当然,人们可能更喜欢在相关块中使用return语句来限制缩进大小。)

1 个答案:

答案 0 :(得分:4)

我曾经在ARM(7,9)上进行过大量的性能优化操作。这是简单的C,愚蠢的编译器(SDT AFAIR)。保存一些CPU资源的方法之一是分析if分支并重写if条件,因此正常流不会破坏线性指令序列。这具有积极的影响,因为CPU预测阻止了更有效的使用,并且更有效的代码段内存缓存使用。

我认为在这里我们看到非常接近的优化。在第一个代码片段中,两个分支都导致正常序列被破坏(一个分支的行为6,另一个分行为12)。在第二个片段中,一个分支指令被排序到retq,而其他分支序列具有单个跳转(不比第一个片段中的更差)。请注意2 retq条说明。

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

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