使用"__builtin_expect"会影响程序语义吗?

Can using `__builtin_expect` affect program semantics?

本文关键字:quot 语义 程序 expect builtin 使用 影响      更新时间:2023-10-16

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

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

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

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

我的问题是,如果我使用__builtin_expect我仍然可以保证我的防御性检查有效吗?如果是这样,如果我在防御性检查中使用__builtin_expect作为上述方法,我是否会获得任何运行时好处?

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

不,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% 的请求是非法的并且会被拒绝,我们仍然可能决定将"快乐路径"标记为可能采取,因为我们不在乎减慢拒绝的速度。