簡體   English   中英

x86 上是否需要 std::memory_order_acquire 柵欄?

[英]std::memory_order_acquire fence necessary on x86?

鑒於 x86 具有強大的內存模型,是否需要std::memory_order_acquire柵欄( 不是操作)?

例如,如果我有這個代碼:

uint32_t read_shm(const uint64_t offset) {
   // m_head_memory_location is char* pointing to the beginning of a mmap-ed named shared memory segment
   // a different process on different core will write to it.
   return *(uint32_t*)(m_head_memory_location + offset);
}
....
int main() {
     uint32_t val = 0;
     while (0 != (val = shm.read(some location)));
     .... // use val
}

在 return 語句之前我真的需要std::atomic_thread_fence(std::memory_order_acquire)嗎?

我覺得沒有必要,因為上面代碼的目標是嘗試從m_head_memory_location + offset讀取前 4 個字節,因此柵欄重新排序后的任何內存操作都不會影響結果。

或者有一些副作用使獲取柵欄變得必要?

在 x86 上是否需要獲取柵欄(不是操作)?

歡迎任何意見。

return *(uint32_t*)(m_head_memory_location + offset);

您轉換為非atomic非易失volatile uint32_t*並取消引用!!!

允許編譯器假設這個uint32_t對象不是由其他任何東西寫入的(即假設沒有數據競爭 UB),因此它可以並將負載提升到循環之外,有效地將其轉換為類似if((val=load) == 0) infinite_loop(); .

GCC 內存屏障將強制重新加載,但這是std::atomic_thread_fence(std::memory_order_acquire)的實現細節。 對於 x86,該屏障只需要阻止編譯時重新排序,因此 GCC 的典型實現可能是asm("" ::: "memory")

執行任何操作的不是獲取排序,而是阻止 GCC 假設另一個讀取將讀取相同內容的內存破壞。 這不是 ISO C++ std::atomic_thread_fence(std::memory_order_acquire)對非原子變量暗示的東西。 (而且它總是暗示原子和易失性)。 所以就像我說的,這可以在 GCC 中工作,但只能作為一個實現細節。


如果此內存曾被除此char*以外的其他類型訪問,或者如果底層內存被聲明為char[]數組,則它也是嚴格別名 UB。 如果你從mmap或其他東西得到一個char* ,那你就沒事了。

除非已知offset是 4 的倍數,否則 UB 也可能未對齊。(盡管除非 GCC 選擇 auto-vectorize ,但這在 x86 上實際上不會咬你。)

您可以使用typedef uint32_t unaligned_u32 __attribute((may_alias, aligned(1)));為 GNU C 解決這兩個問題typedef uint32_t unaligned_u32 __attribute((may_alias, aligned(1))); 但是您仍然需要volatileatomic<T>才能在循環中讀取才能工作。


一般來說

使用std::atomic_thread_fence(std::memory_order_acquire); 根據 C++ 內存模型的要求; 這就是在編譯時管理重新排序的原因。

編譯x86時,不會變成任何asm指令; 在 asm 中,這是一個空操作。 但是如果你不告訴編譯器它不能重新排序某些東西,你的代碼可能會根據編譯器優化級別而中斷。

您可能很幸運,並讓編譯器在原子mo_relaxed加載之后執行非原子加載,或者如果您不告訴它不這樣做,它可能會更早執行非原子加載。

暫無
暫無

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

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