2016-11-17 93 views
1
#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

回答

0

通常,當編譯器無法在編譯時證明排序參數已知時,它將不會有機會並假定爲最壞情況。

如果my_order是非const全局變量,編譯器無法知道什麼時候執行store,因此將使用std::memory_order_seq_cst的實際值是什麼。 如果變量被聲明爲const,那麼排序參數將生效並且mfence指令消失。