使用"__builtin_expect"会影响程序语义吗?
Can using `__builtin_expect` affect program semantics?
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% 的请求是非法的并且会被拒绝,我们仍然可能决定将"快乐路径"标记为可能采取,因为我们不在乎减慢拒绝的速度。
- 何时在引用或唯一指针上使用移动语义
- 如何从具有移动语义的类对象中生成共享指针
- Boost Spirit,获取迭代器内部语义动作
- 可以使用移动语义更改或改进此C++代码吗?
- c++在使用指针时移动语义
- 在C++17中,引用const字符串的语义应该是什么
- Xcode 语义问题引用或以前定义的代码
- 使用移动和复制语义时函数匹配如何工作?
- 将向量从 N1 缩小到 N2 项,而不触发默认构造函数并仅使用 move 语义
- 移动语义和深层/浅层复制之间有什么关系?
- 了解构造函数在移动、复制、赋值语义中的行为
- std::unique_lock移动语义
- 移动语义和运算符 + 重载
- C++ 移动语义是否在任何情况下都能节省资源?
- 移动语义在这里如何工作?
- 使用移动语义:右值引用作为方法参数
- 是否可以/希望创建不可复制的共享指针模拟(以启用weak_ptr跟踪/借用类型语义)?
- 在C++中使用移动语义的正确方法是什么?
- C++ 价值语义、不可变性和继承性
- 移动语义 c++ 单链表