可以使用`__builtin_expect`影响程序语义吗?

时间:2017-04-03 12:08:23

标签: c++ gcc clang

GCC(和Clang也),为人工辅助分支预测提供此__builtin_expect,如here所述。非正式地,人们通过以下方式解释其语义:"编译器只是无条件地处理指示的分支,如果条件应该与指示的不同,则会发生昂贵的回滚"。

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

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

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

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

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

1 个答案:

答案 0 :(得分:4)

不,builtin_expect不会影响无竞赛计划的语义。

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

对于您的具体示例:

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

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

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

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

即使我们发现50%的请求是非法的并且会被拒绝,我们仍然可能会决定标记“快乐路径”,因为我们不关心减慢拒绝。