記憶體模型與原子操作

x86-64 TSO 相對於弱序(ARM/POWER)

/ TSO -> tee-ess-OH /

不同的中央處理器家族對「一個核心的記憶體操作如何對另一個核心顯現」承諾不同的事。這是「硬體」記憶體模型,與語言層的不同。最大的分野在強序晶片(x86-64)與弱序晶片(ARM/AArch64、POWER)之間。同一個「用原子寫對」的程式到處都能跑,但一個省略了正確排序的有臭蟲程式,可能在 x86 上「看起來」沒事,卻在 ARM 上粉碎——這正是只在 x86 上測試的人會出貨微妙臭蟲的原因。

x86-64 採用 TSO,總儲存順序。在 TSO 下,每個核心都同意所有儲存的單一全序,而硬體唯一允許的重排是:一個核心自己的儲存可在它的儲存緩衝區裡被延遲,使得一個「較晚」的載入(對不同位址)看似超車越過它(store-to-load 重排)。它從不重排兩個載入、從不重排兩個儲存、也從不讓儲存以不同順序對不同觀察者顯現。後果很驚人:一個普通的 x86 載入已經表現得像 acquire、普通的儲存像 release,所以 acquire/release 同步編譯成單純的 mov 指令、「不需要」額外屏障——x86「幾乎免費給你 acquire/release」。只有 seq_cst 儲存(或那個 store-load 情況)才需要一道真正的屏障(mfence 或一條 lock 指令)。

ARM/AArch64 與 POWER 是弱序的:硬體可能把載入和載入、儲存和儲存重排,甚至更多,而不同核心甚至能以不同順序觀察到儲存。免費的排序少得多,所以編譯器必須發出明確的屏障指令(ARM 的 dmb、POWER 的 lwsync/sync)來兌現你的 acquire/release/seq_cst 標籤。誠實的結論是:對著「語言」記憶體模型寫程式,而非對著 x86 偶然的強序寫;編譯器會替每個目標平台插入恰好所需的屏障,你的程式就保持正確且可移植。

儲存緩衝區試金石: T1:x=1; r1=y; T2:y=1; r2=x; (x、y 為原子,RELAXED) 在 x86 TSO 上,r1==0 && r2==0 這個結果「是」可觀察到的(儲存緩衝區轉送);在任何模型上都需要 seq_cst 或一道屏障才能禁止它。

即使是強序的 TSO 也允許 store-to-load 重排——這是 x86 仍會讓你意外的那一處。

別「在 x86 上測過就當作正確」:x86 的強序會遮住只在 ARM/POWER 上才爆發的缺屏障臭蟲;對著語言模型寫,讓編譯器發出各平台所需的屏障。

又称
total store orderstrong vs weak memory model總儲存順序