假设我有一个函数IsTrue()
,它返回一个bool
。如果程序员决定IsTrue()
通常会返回true
,那么在那些典型的情况下,她可能会使用GCC的__builtin_expect
或类似的方法来加速指令。
举以下两个例子:
// 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
的结果之后应用?
所有现代处理器都采用超标量流水线,它在实际执行的指令之后预计算指令。通过使用__builtin_expect
,编译器对指令进行重新排序,这样预期的(可能的)路径就不会使用导致超标量管道丢失的跳转,从而使所有预先计算的结果都变得无用。
编辑:这当然是简化了。现代处理器也有分支预测,因此它们试图预测代码将遵循哪条路径,但没有跳跃的路径仍然是首选(这对缓存也更好,因为执行的代码路径紧凑)。还要注意,这些速度上的变化通常是非常微小的,如果你的代码不是经常执行,或者确实需要尽可能多的速度,你就不需要麻烦了。
正如GCC文档所说,与使用__builtin_expect
进行编写相比,更喜欢使用概要文件引导的优化(PGO、-fprofile-generate
,然后运行测试用例并使用-fprofile-use
进行重建)进行构建。
CPU内置的分支预测将缓存程序"典型"执行路径中的大多数分支。静态分支预测主要在所有分支上提供强数据时起作用,因此当编译器可以在先进行几个测试中的一个测试之间进行选择时,它可以安排最有效的序列。这不仅考虑了解析为什么值,还考虑了最频繁地选择哪个else if
语句。
CCD_ 16很少有什么不同。对于分支程序,-fprofile-use
通常会在无需编程的情况下提供10-30%的即时提升。
将
IsTrue()
放置在__builtin_expect
参数列表中是否会导致在IsTrue
的结果已知之前对指令缓存进行评估,还是仅在计算出IsTrue
的结果之后才应用分支预测?
__builtin_expect
发生在编译时。指令缓存甚至还不存在。编译器会猜测IsTrue
是true
,然后在运行时,CPU会做出另一个更明智的猜测。