你读过 GCC 文档吗?
内置函数:long __builtin_expect (long exp, long c)
您可以使用 __builtin_expect 为编译器提供分支预测信息。一般来说,您应该更喜欢为此使用实际的配置文件反馈(-fprofile-arcs),因为程序员在预测他们的程序实际执行方式方面出了名的差。但是,有些应用程序很难收集这些数据。
返回值是exp的值,应该是一个整数表达式。内置的语义是期望 exp == c。例如:
if (__builtin_expect (x, 0))
foo ();
表示我们不希望调用 foo,因为我们希望 x 为零。由于您仅限于 exp 的整数表达式,因此您应该使用诸如
if (__builtin_expect (ptr != NULL, 1))
foo (*ptr);
在测试指针或浮点值时。
稍微解释一下... __builtin_expect 对于传达您认为程序可能采用的分支特别有用。你问编译器如何使用这种洞察力——好吧,考虑一下这段代码:
if (x == 0)
return 10 * y;
else
return 39;
在机器代码中,通常可以要求 CPU “转到”另一行(这需要时间,并且取决于 CPU 可能会阻止其他执行优化 - 即低于机器代码级别 - 例如,请参阅http 下的分支标题: //en.wikipedia.org/wiki/Instruction_pipeline),或调用其他代码,但实际上并没有一个 if/else 概念,其中 true 和 false 代码是相等的......你必须分支才能找到代码非此即彼。完成的方式基本上是伪代码:
test whether x is 0
if it was goto else_return_39
return 10 * y
else_return_39:
return 39
鉴于大多数 CPU 跟随goto
down 到else_return_39:
标签比仅仅下降到更慢return 10 * y
,“true”分支的代码将比 false 分支更快地到达。当然,机器代码可以测试 x 是否不为0,将“假”代码 ( return 39
) 放在首位,从而反转性能特征。
这是 __builtin_expect 控制的 - 您可以告诉编译器将真或假分支放在需要较少分支才能到达它的地方,从而获得微小的性能提升。
但这适用于 a) 内联 ifs、b) 变量和 c) 0 和 1 以外的值吗?
a) 周围的函数是否内联并不会改变对if
出现语句的分支的需求(除非优化器看到if
语句测试总是存在的条件,true
或者false
只有一个分支永远不会运行)。因此,它同样适用于内联代码。
[您的评论表明您对条件表达式感兴趣a ? b : c
--我不确定-在我是否可以在 C 中使用 GCC 的 __builtin_expect() 和三元运算符时对该问题有争议的答案,这可能证明以一种或另一种方式很有见地,或者进一步探索的基础]
b)变量 - 你假设:
int x = __builtin_expect( t / 10, 7 );
if( x == 7 ) {
那是行不通的——编译器没有义务将这些期望与变量相关联,并在下次if
看到 an 时记住它们。您可以使用生成汇编语言输出来验证这一点(就像我对 gcc 3.4.4 所做的那样)gcc -S
:无论预期值如何,程序集都不会改变。
c) 0 和 1 以外的值
它适用于整数 ( long
) 值,所以是的。上面引用的文档的最后一段解决了这个问题,特别是:
您应该使用诸如
if (__builtin_expect (ptr != NULL, 1))
foo (*ptr);
在测试指针或浮点值时。
为什么?好吧,如果指针类型大于long
,那么调用__builtin_conversion(long, long)
将有效地切断一些不太重要的位,并且无法将其余位合并到测试中。同样,浮点值可能大于 long,并且转换不会产生您期望的结果。通过使用诸如ptr != NULL
(given true
converts to 1L and false
to 0) 之类的布尔表达式,您一定会得到预期的结果。