【问题标题】:Can using `__builtin_expect` affect program semantics?使用 __builtin_expect 会影响程序语义吗?
【发布时间】:2017-08-28 07:10:51
【问题描述】:

GCC(以及 Clang),提供此 __builtin_expect 用于人工辅助分支预测,如 here 所述。非正式地,人们用以下方式解释它的语义:“编译器只是无条件地处理指定的分支,如果条件与指定的不同,就会发生代价高昂的回滚”。

但如果我有一段代码如下:

if (__builtin_expect(p != 0, 1)) // line 1
  p->access_object();            // line 2

如果我按字面意思处理上面的非正式解释,编译器可以直接执行第 2 行,而无需等待第 1 行中条件的计算,因此如果指针碰巧为空,则会导致未定义的行为(空指针取消引用) .

我的问题是,如果我使用__builtin_expect,我还能得到保证,我的防御检查有效吗?如果是这样,如果我在上述防御检查中使用__builtin_expect,是否可以获得任何运行时优势?

(注意:我像这样使用__builtin_expect 的目标是在p 为非空的情况下获得最大性能,但代价是减慢(即使是数量级)@ 987654329@ 为空;即使后一种情况经常出现。)

【问题讨论】:

  • IMO 这是一个糟糕的非正式描述,afaik 它只是针对您告诉它的任何分支进行优化。而这种信念似乎得到了answers to a related question 的支持
  • @Borgleader:所以这表示无论如何都要先执行检查。唯一的问题是执行是否可以在检查后立即继续,或者在检查后一段时间。我做对了吗?
  • 我会说编译器不会“无条件地处理指定的分支”。它开始无条件地处理它,但如果选择了错误的分支,它会丢弃任何结果。这种丢弃发生在非常低​​的水平上。在检查条件之前,这些无条件计算的结果甚至不会出现在实际 RAM 中。此外,据我所知,即使没有__builtin_expect,在检查条件之前总是会选择其中一个分支,但它会由分支预测器而不是您选择。

标签: c++ gcc clang


【解决方案1】:

不,builtin_expect 不会影响无竞争程序的语义。

特别是,如果代码具有无法撤消的副作用,编译器不得发出将执行if 块主体的代码。除了性能之外,代码必须“好像”builtin_expect 未被使用。

对于您的具体示例:

if (__builtin_expect(p != 0, 1)) // line 1
  p->access_object();            // line 2

p 如果为 null,则无法取消引用。那么在这种情况下builtin_expect 有什么意义呢?它最多只能告诉编译器“p 可能不为空,所以access_object() 可能会被调用。”如果access_object() 的定义是inline,编译器可能会尝试内联它,而如果您说“p 可能为空”,编译器可能会认为最好不要内联access_object() 的代码在这个调用站点,因为它不太可能被使用。

事实上,这会导致在实践中对builtin_expect 的非直观使用:您可以用它来表示“这段代码是慢速路径”,而不管它有多“可能”。举个简单的例子,服务器程序可能会这样做:

if (__builtin_expect(is_allowed(user, request), 1))
    process(request);
else
    reject(request);

即使我们发现 50% 的请求是非法的并且将被拒绝,我们仍可能决定将“愉快的路径”标记为可能采用,因为我们并不关心减缓拒绝速度。

【讨论】:

  • 是的,其实这是我的动机:不是显示哪个分支出现的频率更高,而是显示我想要优化哪个分支。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-19
  • 1970-01-01
相关资源
最近更新 更多