記憶體一致性模型(memory consistency model)
一致性回答的是關於單一位址的問題:所有核心最終是否對 X 的最新值達成一致?記憶體一致性模型(memory consistency model)回答的是一個關於多個位址、更難的問題:一個核心的寫入,被允許以什麼順序對另一個核心變得可見?想像你整理桌子的方式是:先豎起一面旗(「我做完了」),再把完成的報告放進托盤。一個在旁觀看的同事可能會驚訝地先看到旗豎起、報告才出現——因為這兩個動作在送出的路上可能被重新排序。一致性模型就是那份契約,明確規定硬體可以暴露哪些這樣的重排。
這為何微妙,原因在此。一個現代核心為了快,不一定按程式寫下的順序讓它的記憶體操作對別人可見——儲存緩衝區、快取、以及亂序執行,可能讓一個較晚的寫入在另一個核心看來比一個較早的更先出現,即使每個核心自己看到的仍是循序的。一致性模型就是「哪些重排被准許」的精確規格,也是並行程式碼必須據以撰寫的依據。最嚴格的模型——循序一致性(sequential consistency)——禁止一切令人驚訝的重排;放鬆或弱的模型(大多數真實硬體所用)准許許多重排以換取速度,並要求程式設計師在某個重排會破壞正確性之處插入記憶體屏障來禁止它。
這極其重要,也是運算中最不直覺的角落之一。無鎖程式碼、雙重檢查鎖定、以及許多巧妙的同步技巧,在某個一致性模型下正確、在另一個下卻悄悄壞掉——臭蟲只偶爾出現、只在某些硬體上出現,重現起來折磨人。誠實的結論:除非你明確地約束它,平行硬體不會表現得像程式步驟的簡單交錯。你必須懂你平台的記憶體模型,或使用把屏障替你封裝好的更高階原語(鎖、有明確定序的原子操作)。
核心 A:store data = 42;store ready = 1。核心 B:while(ready == 0) 等待;read data。在循序一致性下,當 B 看到 ready == 1 時,它必定也看到 data == 42。在放鬆的模型下,B 可能看到 ready == 1,而 data 仍是舊值——除非 A 在兩個儲存之間插入一道記憶體屏障來強制順序。
一致性模型決定了「資料先設、旗標後設」是否保證被以那個順序觀察到——這是正確無鎖程式碼的關鍵。
一致性(連貫性)與一致性模型是兩回事:前者談單一位置的副本達成一致;後者談跨不同位置的操作所允許的順序。一台連貫的機器仍可能以破壞天真並行程式碼的方式重排寫入。