【问题标题】:Static branch prediction / GCC optimization静态分支预测/GCC优化
【发布时间】:2013-09-04 19:34:29
【问题描述】:

考虑以下 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 equalsjump not equals的变化。我知道直到 Pentium 4,条件前向分支上的静态分支预测被认为没有被处理器采用(似乎静态预测在更多的英特尔处理器上变得随机),因此我想这种优化正在处理这个问题。

假设并参考 jne 优化版本,这意味着 else 块实际上被认为比 if 程序流中的块。

但这究竟是什么意思呢?由于编译器没有对 foo 函数中的 a 值进行假设,因此这种概率仅依赖于程序员的著作(实际上他们可能使用了if ( !a ) if ( a ) 和反向函数调用)。

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

即:

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

代替:

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

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

【问题讨论】:

    标签: c gcc optimization assembly x86-64


    【解决方案1】:

    我曾经在 ARM(7,9) 上进行过大量的性能优化操作。它是纯 C 语言,足够愚蠢的编译器(SDT AFAIR)。节省一些 CPU 资源的方法之一是分析if 分支并重写if 条件,以便正常流程不会破坏线性指令序列。这产生了积极的影响,因为 CPU 预测块更有效的使用和更有效的代码段内存缓存使用。

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

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

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

    【讨论】:

    • 是的...但是不同的返回点也可以在je 版本上实现。并且 GCC 有意识地 执行 if/else 块重新排序:在初始程序中将 if ( a ) 更改为 if ( !a ) 编译为完全相反:从 jne(非优化)版本到je(分支顺序优化)版本。我不敢相信 GCC 做这个改变只是为了取笑我! :)
    • 我认为无论如何 __builtin_expect 应该对此有所帮助;-)。 blog.man7.org/2012/10/how-much-do-builtinexpect-likely-and.html
    猜你喜欢
    • 2017-06-12
    • 1970-01-01
    • 2015-11-24
    • 2012-07-02
    • 2018-07-02
    • 2011-09-07
    • 2011-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多