当非const值左值时,memory_order更改为默认值
问题描述:
#include <atomic>
std::atomic<int> val{1};
const auto my_order = std::memory_order_relaxed; // const lvalue
int main()
{
val.store(42, my_order);
}
此代码没有相关性,但我注意到有关内存排序的奇怪事情。编译器产生以下组件为主要(x86_64的,克++ 6.2.1,与-O3编译):当非const值左值时,memory_order更改为默认值
0x00000000004004c0 <+0>: movl $0x2a,0x200b5a(%rip) # 0x601024 <val>
0x00000000004004ca <+10>: xor %eax,%eax
0x00000000004004cc <+12>: retq
没有特殊的CPU指令处理原子预计在x86与std::memory_order_relaxed
排序。
然而,当const
限定符从my_order
auto my_order = std::memory_order_relaxed; // non-const lvalue
除去编译器生成的组装变得:
0x00000000004004c0 <+0>: movl $0x2a,0x200b5a(%rip) # 0x601024 <val>
0x00000000004004ca <+10>: xor %eax,%eax
0x00000000004004cc <+12>: mfence
0x00000000004004cf <+15>: retq
的mfence
指令似乎表明std::memory_order_seq_cst
排序现在使用(缺省值)。这对我来说有点令人惊讶。即使my_order
是一个左值(非常规来指定内存排序),它是通过值传递(仍std::memory_order_relaxed
),我看不到非const
将如何更改结果。我无法在库头文件中找到特定的重载。
随着铿锵我看到类似的结果,除了它使用xchg
,这是表达顺序一致性的铿锵样的方式。
有什么可以解释的区别?
答
通常,当编译器无法在编译时证明排序参数已知时,它将不会有机会并假定为最坏情况。
如果my_order
是非const
全局变量,编译器无法知道什么时候执行store
,因此将使用std::memory_order_seq_cst
的实际值是什么。 如果变量被声明为const
,那么排序参数将生效并且mfence
指令消失。
我认为在第二种情况下,编译器可能决定[在目标体系结构上]简单地发出围栏比检查使用的内存顺序更有效,然后有条件地发出围栅。 – Brian
@Brian +1。如果'my_order'将被声明为静态和非常量,mfence可能会消失。 –
@Oleg这是一个很好的观点。编译器需要一个编译时间常量才能对排序做出决定,而非静态的非常量全局可能不符合条件 – LWimsey