当非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,这是表达顺序一致性的铿锵样的方式。
有什么可以解释的区别?

+2

我认为在第二种情况下,编译器可能决定[在目标体系结构上]简单地发出围栏比检查使用的内存顺序更有效,然后有条件地发出围栅。 – Brian

+0

@Brian +1。如果'my_order'将被声明为静态和非常量,mfence可能会消失。 –

+0

@Oleg这是一个很好的观点。编译器需要一个编译时间常量才能对排序做出决定,而非静态的非常量全局可能不符合条件 – LWimsey

通常,当编译器无法在编译时证明排序参数已知时,它将不会有机会并假定为最坏情况。

如果my_order是非const全局变量,编译器无法知道什么时候执行store,因此将使用std::memory_order_seq_cst的实际值是什么。 如果变量被声明为const,那么排序参数将生效并且mfence指令消失。