c++ GCC的__builtin_能走多远?

yh2wf1be  于 2023-02-26  发布在  其他
关注(0)|答案(2)|浏览(133)

在回答另一个问题时,我对此感到好奇。我很清楚

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
}

有什么影响吗?所有这些都取决于目标体系结构吗?

nc1teljy

nc1teljy1#

你读过GCC文档吗?
内置功能:long__内置_expect(长表达式,长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向下到else_return_39:标签比仅仅落入return 10 * y慢,则到达"真"分支的代码将比到达假分支的代码快。当然,机器代码可以测试x是否为 * not * 0,将"假"代码(return 39)放在第一位,从而反转性能特性。
这就是__builtin_expect所控制的--您可以告诉编译器将true或false分支放在需要较少分支才能到达的地方,从而获得微小的性能提升。
但是这对a)内联ifs,b)变量和c)除了0和1之外的值有效吗?
a)无论周围的函数是否内联,都不会改变if语句出现的分支需求(除非优化器看到if语句测试的条件总是truefalse,并且只有一个分支永远不会运行)。
[您的评论表明您对条件表达式a ? b : c感兴趣--我不确定--www.example.com上对该问题有一个有争议的答案,它可能以某种方式证明是有见地的,或者是进一步探索的基础]https://stackoverflow.com/questions/14784481/can-i-use-gccs-builtin-expect-with-ternary-operator-in-c that might prove insightful one way or the other, or the basis for further exploration ]
b)变量-您假设:

int x = __builtin_expect( t / 10, 7 );
if( x == 7 ) {

这是行不通的--编译器没有义务把这样的期望与变量关联起来,并在下次看到if时记住它们,你可以使用gcc -S生成汇编语言输出来验证这一点(就像我在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转换为1,false转换为0),您肯定会得到预期的结果。

tjjdgumg

tjjdgumg2#

但是这对a)内联ifs,b)变量和c)除了0和1之外的值有效吗?
它适用于用于确定分支的表达式上下文。
所以,a)是。b)不是。c)是。
所有这些都取决于目标体系结构吗?
好耶!
它利用使用instruction pipelining的架构,允许CPU在当前指令完成之前开始处理即将到来的指令。
(if任何人都可以澄清魔术,这也将是伟大的)。
("分支预测"使这个描述复杂化,所以我有意省略它)
任何类似if语句的代码都意味着表达式可能导致CPU跳转到程序中的不同位置,这些跳转使CPU指令流水线中的内容无效。
__builtin_expect允许(没有保证)gcc尝试汇编代码,因此 * 可能 * 的情况比备选方案涉及更少的跳转。

相关问题