【问题标题】:Is the speedup from branch prediction dependent on the predicate's complexity?分支预测的加速是否取决于谓词的复杂性?
【发布时间】:2015-04-08 16:48:11
【问题描述】:

假设我有一些函数IsTrue(),它返回一个bool。如果程序员决定IsTrue() 通常会返回true,她可能会使用GCC 的__builtin_expect 或类似的方法来加速那些典型情况下的指令。

举以下2个例子:

// example 1
const bool result = IsTrue();
if (__builtin_expect(result, true))
  // do something
  ;

// example 2
if (__builtin_expect(IsTrue(), true))
  // do something
  ;

假设IsTrue 是不平凡的,它们之间有什么区别吗?将IsTrue() 放在__builtin_expect 参数列表中是否会导致在IsTrue 的结果已知之前评估指令缓存,或者分支预测是否仅适用于之后 IsTrue的结果是计算出来的?

【问题讨论】:

  • 附带说明:如果谓词是 complex(可能涉及其自己的分支),则原始分支的相对成本无论如何都会减少。
  • 我认为您混淆了 CPU 上的分支预测和编译器上的预测提示。分支预测仅在 CPU 看到分支指令并决定我们是否应该实际接受它时才会发生。预测提示告诉编译器有一个理想的分支代码安排,以便在运行时更好地预测行为。您的代码示例实际上与在这两种情况下编译器必须评估函数并将结果存储在某种变量中相同。第一个示例只是使其明确。

标签: c++ branch-prediction


【解决方案1】:

所有现代处理器都采用超标量管道,在实际执行的指令之后预先计算指令。通过使用__builtin_expect,编译器重新排序指令,以便预期(可能)路径不会使用导致超标量管道丢失的跳转,从而使所有预先计算的结果无用。

编辑:这当然是简化的。现代处理器也具有分支预测功能,因此它们会尝试预测代码将遵循的路径,但仍然首选没有跳转的路径(缓存也更好,因为执行的代码路径很紧凑)。另请注意,这些速度变化通常是非常微小的,如果您的代码不是非常频繁地执行或确实需要尽可能高的速度,您无需担心。

【讨论】:

  • 我更喜欢消除分支的优化,而不是控制流的特定提示。最快的分支是您不必采取的分支。
【解决方案2】:

正如 GCC 文档所说,与使用 __builtin_expect 编写相比,更喜欢使用配置文件引导优化(PGO,-fprofile-generate 然后运行测试用例并使用 -fprofile-use 进行重建)进行构建。

CPU 内置的分支预测将缓存程序“典型”执行路径中的大部分分支。静态分支预测主要在它提供关于所有分支的强数据时产生影响,因此当编译器可以选择先执行几个测试之一时,它可以安排最有效的序列。这不仅考虑了值解析为什么,还考虑了哪个else if 语句最常被选中。

__builtin_expect 很少会有所作为。对于分支程序,-fprofile-use 通常会在不进行任何编程工作的情况下立即提供 10-30% 的提升。

IsTrue() 放在__builtin_expect 参数列表中是否会导致在知道IsTrue 的结果之前评估指令缓存,还是仅在计算IsTrue 的结果之后才应用分支预测?

__builtin_expect 发生在编译时。指令缓存甚至还不存在。编译器会猜测IsTruetrue,然后在运行时,CPU 会做出另一个更明智的猜测。

【讨论】:

    猜你喜欢
    • 2020-10-23
    • 2019-04-14
    • 2020-07-24
    • 1970-01-01
    • 2014-07-31
    • 1970-01-01
    • 1970-01-01
    • 2010-11-28
    • 1970-01-01
    相关资源
    最近更新 更多