簡體   English   中英

改進 InterlockedCompareExchange() 的原子讀取

[英]improve atomic read from InterlockedCompareExchange()

假設架構是 ARM64 或 x86-64。

我想確定這兩個是否等效:

  1. a = _InterlockedCompareExchange64((__int64*)p, 0, 0);
  2. MyBarrier(); a = *(volatile __int64*)p; MyBarrier();

其中MyBarrier()是編譯器級別的內存屏障(提示),例如__asm__ __volatile__ ("" ::: "memory") 所以方法2應該比方法1快。

我聽說_Interlocked()函數也意味着編譯器和硬件級別的內存障礙。

我聽說讀取(正確對齊)內部數據在這些架構上是原子的,但我不確定方法 2 是否可以廣泛使用?

(ps。因為我認為CPU會自動處理數據依賴,所以這里沒有太多考慮硬件障礙。)

感謝您對此提出任何建議/更正


這是 Ivy Bridge(i5 筆記本電腦)上的一些基准測試。

(1E+006 循環: 27ms ):

; __int64 a = _InterlockedCompareExchange64((__int64*)p, 0, 0);
xor eax, eax
lock cmpxchg QWORD PTR val$[rsp], rbx

(1E+006 循環: 27ms ):

; __faststorefence(); __int64 a = *(volatile __int64*)p;
lock or DWORD PTR [rsp], 0
mov rcx, QWORD PTR val$[rsp]

(1E+006 循環: 7ms ):

; _mm_sfence(); __int64 a = *(volatile __int64*)p;
sfence
mov rcx, QWORD PTR val$[rsp]

(1E+006 循環: 1.26ms ,不同步?):

; __int64 a = *(volatile __int64*)p;
mov rcx, QWORD PTR val$[rsp]

要使第二個版本在功能上等效,您顯然需要原子 64 位讀取,這在您的平台上是正確的。

但是, _MemoryBarrier()不是“對編譯器的提示”。 x86 上的_MemoryBarrier()可防止編譯器和 CPU 重新排序,並確保寫入后的全局可見性。 您也可能只需要第一個_MemoryBarrier() ,第二個可以用_ReadWriteBarrier()替換,除非a也是共享變量 - 但您甚至不需要它,因為您正在讀取易失性指針,這將阻止MSVC 中的任何編譯器重新排序。

當你創建這個替換時,你基本上得到了幾乎相同的結果

// a = _InterlockedCompareExchange64((__int64*)&val, 0, 0);
xor eax, eax
lock cmpxchg QWORD PTR __int64 val, r8 ; val

// _MemoryBarrier(); a = *(volatile __int64*)&val;
lock or DWORD PTR [rsp], r8d
mov rax, QWORD PTR __int64 val ; val

在我的 i7 Ivy Bridge 筆記本電腦上循環運行這兩個,得到相同的結果,在 2-3% 之內。

然而,有兩個內存屏障,“優化版本”實際上慢了大約 2 倍。

所以更好的問題是:你為什么要使用_InterlockedCompareExchange64 如果您需要對變量進行原子訪問,請使用std::atomic ,優化編譯器應將其編譯為最適合您的架構的版本,並添加所有必要的障礙以防止重新排序並確保緩存一致性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM