海湾合作委员会的__builtin_expect走了多远?
在回答另一个问题时,我对此感到好奇。 我很清楚
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 }
有什么影响? 所有这些都取决于目标架构吗?
但这是否适用于a)内联ifs,b)变量和c)除0和1以外的值?
它适用于用于确定分支的表达式上下文。
所以,a)是的。 b)否.c)是。
所有这些都取决于目标架构吗?
是的!
它利用了使用指令流水线的架构,允许CPU在当前指令完成之前开始处理即将发生的指令。
(如果有人能澄清那个也很棒的魔法)。
(“分支预测”使这种描述复杂化,所以我有意省略它)
任何类似于if语句的代码都意味着表达式可能导致CPU跳转到程序中的其他位置。 这些跳转使CPU的指令管道中的内容无效。
__builtin_expect
允许(不保证)gcc尝试汇编代码,因此可能的场景涉及的跳转次数少于备用场景。
你读过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
语句测试总是为true
或false
并且只有一个分支永远不能运行)。 因此,它同样适用于内联代码。
[你的评论显示你对条件表达式感兴趣 – a ? b : c
a ? b : c
– 我不确定 – 对于这个问题有一个有争议的答案我可以使用GCC的__builtin_expect()和C中的三元运算符,这可能certificate这种或那种方式具有洞察力,或者是进一步探索的基础]
b)变量 – 你假设:
int x = __builtin_expect( t / 10, 7 ); if( x == 7 ) {
这是行不通的 – 编译器没有义务将这些期望与变量联系起来,并在下次看到if
时记住它们。 您可以使用gcc -S
来validation这一点(就像我对gcc 3.4.4所做的那样)来生成汇编语言输出:无论期望值如何,程序集都不会更改。
c)0和1以外的值
它适用于整数( long
)值,所以是的。 上面引用的文档的最后一段解决了这个问题,特别是:
你应该使用诸如的结构
if (__builtin_expect (ptr != NULL, 1)) foo (*ptr);
测试指针或浮点值时。
为什么? 好吧,如果指针类型大于long
,那么调用__builtin_conversion(long, long)
将有效地切断一些不太重要的位,并且无法将其余部分合并到测试中。 同样,浮点值可能大于long,转换不会产生您期望的结果。 通过使用布尔表达式,如ptr != NULL
(给定true
转换为1L, false
为0),您肯定会得到预期的结果。