[英]Does the semantics of `std::memory_order_acquire` requires processor instructions on x86/x86_64?
[英]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)));
但是您仍然需要volatile
或atomic<T>
才能在循環中讀取才能工作。
使用std::atomic_thread_fence(std::memory_order_acquire);
根據 C++ 內存模型的要求; 這就是在編譯時管理重新排序的原因。
編譯x86時,不會變成任何asm指令; 在 asm 中,這是一個空操作。 但是如果你不告訴編譯器它不能重新排序某些東西,你的代碼可能會根據編譯器優化級別而中斷。
您可能很幸運,並讓編譯器在原子mo_relaxed
加載之后執行非原子加載,或者如果您不告訴它不這樣做,它可能會更早執行非原子加載。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.