海湾合作委员会的__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语句测试总是为truefalse并且只有一个分支永远不能运行)。 因此,它同样适用于内联代码。

[你的评论显示你对条件表达式感兴趣 – 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),您肯定会得到预期的结果。