你读过GCC文档吗?
Built-in Function: long __builtin_expect (long exp, long c)
You may use __builtin_expect to provide the compiler with branch
prediction information. In general, you should prefer to use actual
profile feedback for this (-fprofile-arcs), as programmers are
notoriously bad at predicting how their programs actually perform.
However, there are applications in which this data is hard to collect.
The return value is the value of exp, which should be an integral
expression. The semantics of the built-in are that it is expected that
exp == c. For example:
if (__builtin_expect (x, 0))
foo ();
indicates that we do not expect to call foo, since we expect x to be zero. Since you are limited to integral expressions for exp, you should use constructions such as
if (__builtin_expect (ptr != NULL, 1))
foo (*ptr);
when testing pointer or floating-point values.
为了解释一下,__builtin_expect 特别有用于表达程序可能采取的分支。你问编译器如何利用这个见解 - 好吧,考虑一下这段代码:
if (x == 0)
return 10 * y;
else
return 39;
在机器码中,CPU通常可以被要求“转到”另一行(这需要时间,并且根据CPU的不同可能会防止其他执行优化——即低于机器码级别——例如,请参见
http://en.wikipedia.org/wiki/Instruction_pipeline下面的分支标题),或者调用其他代码,但没有真正的if/else概念,其中真和假的代码是相等的...你必须分支离开以找到一个或另一个的代码。这样做的方式基本上是,伪代码如下:
test whether x is 0
if it was goto else_return_39
return 10 * y
else_return_39:
return 39
大多数CPU在跳转到
else_return_39:
标签之后比直接掉到
return 10 * y
更慢,所以“真”分支的代码将比假分支更快到达。当然,机器码可以测试x是否不等于0,将“false”代码(
return 39
)放在第一位,从而颠倒性能特征。
这就是__builtin_expect的控制方式——您可以告诉编译器将真或假分支放在需要较少分支才能到达的位置,从而获得微小的性能提升。
但是,这对a)内联ifs、b)变量和c)值为0和1以外的值是否有效呢?
a)周围函数是否内联化并不改变需要在
if
语句出现的分支处进行分支的需要(除非优化器看到
if
语句测试的条件始终为
true
或
false
,只有一个分支永远无法运行)。因此,它同样适用于内联代码。
[ 你的评论显示你对条件表达式 - a ? b : c
- 感兴趣。我不确定 - 关于这个问题有一个有争议的答案,可以在 https://dev59.com/W2Uq5IYBdhLWcg3wHcv5 找到可能会提供一些见解,或者是进一步探索的基础 ]
b) 变量 - 你假设:
int x = __builtin_expect( t / 10, 7 );
if( x == 7 ) {
那样行不通 - 编译器没有义务将这些期望与变量关联并在下次看到
if
时记住它们。您可以使用
gcc -S
生成汇编语言输出进行验证(就像我为gcc 3.4.4所做的那样):无论预期值如何,汇编都不会改变。
c) 非0和1的值
对于整数(
long
)值有效,因此是的。上面引用的文档的最后一段专门解决了这个问题:
you should use constructions such as
if (__builtin_expect (ptr != NULL, 1))
foo (*ptr);
when testing pointer or floating-point values.
为什么?如果指针类型大于long
,那么调用__builtin_conversion(long, long)
将会有效地切掉一些不太重要的位进行测试,并且无法将(高阶)剩余部分纳入测试中。同样,浮点数值可能比长整型更大,转换可能无法产生您期望的结果。通过使用布尔表达式,例如ptr != NULL
(给定true
转换为1,false
转换为0),您可以确保获得预期的结果。
syntax
(它有更好的名称吗?很多人使用三元运算符,但那只是它的形式,不是名称)。另外,对于(c),我指的是其他long
值,而不是其他数据类型(正如我在示例中所提到的)。实际上,我想知道为什么它首先是一个长整型,而不是一个int
、char
或_Bool
。 - Dave?
,一般指“条件表达式”。关于(c)——我重新阅读了你的示例,并理解了你的担忧。虽然我对(a,3)
/a==2
情景没有比你更多的信息,但我可以想象参数为“long”的原因只是为了避免添加机器代码指令以缩短或转换参数:例如,如果是bool,那么编译器可能需要确保非零值被改变为1。同样,对于“char”,它可能需要与255进行“&”操作。 - Tony Delroy