多核心、一致性與執行緒層級平行

記憶體屏障(memory barrier)

在放鬆記憶體的機器上,硬體為了快,可以自由地打亂你的記憶體操作對其他核心變得可見的順序。通常這沒問題,但有時順序就是一切——你必須先寫完資料,才能豎起「就緒」旗,否則讀者會抓到垃圾。記憶體屏障(memory barrier)就是那條在沙地上劃下界線的指令:屏障之前的操作必須在它之後的任何操作之前變得可見。它是那個煞車踏板,強制硬體本來會樂於打亂的順序。

具體地說,屏障(或柵欄,fence)是程式設計師或編譯器插在兩個記憶體操作之間的一條特殊指令。它告訴核心:別讓這個點之前的記憶體操作被重排到它之後,也別讓較晚的操作越到它之前——至少就這種屏障所涵蓋的順序而言。它有不同種類——儲存屏障為寫入定序、載入屏障為讀取定序、完整屏障為一切定序——而確切的保證取決於該架構的記憶體模型。經典用法是生產者型態:寫資料、然後一道儲存屏障、再設就緒旗,使任何讀者都不可能在資料還過時時就看到就緒被設定。

屏障是那個明確的工具,找回放鬆記憶體模型本來扣住不給的順序保證。誠實的陷阱兩面都割人。漏掉一道需要的屏障,你就得到一隻海森堡臭蟲——測試時好好的、卻在真實硬體上偶爾失敗的程式碼,因為那個壞的重排只是有時才發生。加太多,你又扼殺了當初買下放鬆模型所為的那份效能,因為每道屏障都會卡住管線和儲存緩衝區。實務上大多數程式設計師不親手寫屏障:他們用鎖和語言層級的原子操作,這些會在幕後以明確的定序插入正確的屏障。

生產者:store buffer[0..n] = data;儲存屏障;store flag = 1。消費者:while(flag == 0) 等待;載入屏障;read buffer。儲存屏障保證所有資料寫入在 flag = 1 之前可見;載入屏障保證消費者對 buffer 的讀取發生在它看到旗標之後。拿掉任一道屏障,消費者就可能讀到過時的 buffer 資料。

屏障釘死了放鬆模型本來會打亂的記憶體操作順序——生產者-消費者的交握就靠它們。

記憶體屏障為操作定序;它本身並不使操作變成原子的,也不提供互斥。屏障太少會造成罕見、依硬體而定的臭蟲;太多則浪費了放鬆記憶體本欲買下的效能。

又称
fencememory fencemembar記憶體柵欄記憶體圍欄