在回答另一个问题时,我对此感到好奇。我很清楚
if( __builtin_expect( !!a, 0 ) ) {
// not likely
} else {
// quite likely
}
通过做一些暗示处理器/更改汇编代码顺序/某种魔术的东西,可以更快地(通常)使“非常可能”分支更快。 (如果有人能澄清那种也很棒的魔法)。
但这是否适用于a)内联ifs,b)变量和c)除0和1以外的值?即将
__builtin_expect( !!a, 0 ) ? /* unlikely */ : /* likely */;
或
int x = __builtin_expect( t / 10, 7 );
if( x == 7 ) {
// likely
} else {
// unlikely
}
或
if( __builtin_expect( a, 3 ) ) {
// likely
// uh-oh, what happens if a is 2?
} else {
// unlikely
}
有什么影响吗?所有这些都取决于目标架构吗?
答案 0 :(得分:18)
您是否阅读过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概念,其中真假代码都是相等的......你必须分开去寻找其中一个或另一个的代码。完成的方式基本上是伪代码:
test whether x is 0
if it was goto else_return_39
return 10 * y
else_return_39:
return 39
鉴于大多数CPU在goto
下降到else_return_39:
标签后速度慢于直到return 10 * y
,因此“真正”分支的代码将比假枝。当然,机器代码可以测试x是否不 0,首先放置“假”代码(return 39
),从而扭转性能特征。
这就是__builtin_expect所控制的 - 你可以告诉编译器将真分支或假分支放在需要较少分支的位置,从而获得微小的性能提升。
但这是否适用于a)内联ifs,b)变量和c)除0和1以外的值?
a)周围函数是否内联并不会改变出现if
语句的分支需求(除非优化器看到if
语句测试的条件总是{{1或true
只有一个分支永远不会运行)。因此,它同样适用于内联代码。
[您的评论显示您对条件表达式感兴趣 - false
- 我不确定 - 在Can I use GCC's __builtin_expect() with ternary operator in C处对该问题提出的争议答案可能会以某种方式证明其具有洞察力,或者进一步探索的基础]
b)变量 - 你假设:
a ? b : c
这不起作用 - 编译器没有义务将这些期望与变量联系起来,并在下次看到int x = __builtin_expect( t / 10, 7 );
if( x == 7 ) {
时记住它们。您可以使用if
来验证这一点(就像我对gcc 3.4.4所做的那样)来生成汇编语言输出:无论期望值如何,程序集都不会改变。
c)0和1以外的值
它适用于整数(gcc -S
)值,所以是的。上面引用的文档的最后一段解决了这个问题,特别是:
你应该使用诸如
之类的结构long
测试指针或浮点值时。
为什么呢?好吧,如果指针类型大于if (__builtin_expect (ptr != NULL, 1))
foo (*ptr);
,那么调用long
将有效地切断一些不太重要的位,并且无法将其余部分合并到测试中。同样,浮点值可能大于long,转换不会产生您期望的结果。通过使用布尔表达式,例如__builtin_conversion(long, long)
(给定ptr != NULL
转换为1L而true
转换为0),您肯定会得到预期的结果。
答案 1 :(得分:11)
但这是否适用于a)内联ifs,b)变量和c)除0和1以外的值?
它适用于用于确定分支的表达式上下文。
所以,a)是的。 b)否.c)是。
所有这些都取决于目标架构吗?
是的!
它利用使用instruction pipelining的架构,允许CPU在当前指令完成之前开始处理即将发出的指令。
(如果有人能澄清那种也很棒的魔法)。
(“分支预测”使这种描述复杂化,所以我有意省略它)
任何类似于if语句的代码都意味着表达式可能导致CPU跳转到程序中的其他位置。这些跳转使CPU的指令管道中的内容无效。
__builtin_expect
允许(不保证)gcc尝试汇编代码,因此可能场景的跳转次数少于备用场景。