記憶體屏障

記憶體屏障,也稱記憶體柵欄,記憶體柵障,屏障指令等, 是一類同步屏障指令,是CPU或編譯器在對記憶體隨機訪問的操作中的一個同步點,使得此點之前的所有讀寫操作都執行後才可以開始執行此點之後的操作。

基本介紹

  • 中文名:記憶體屏障
  • 別稱:記憶體柵欄,記憶體柵障
  • 性質同步屏障指令
  • 條件:現代計算機為了提高性能
簡介,底層體系結構相關的原語,Windows API的記憶體屏障實現,多執行緒編程與記憶體可見性,亂序執行與編譯器重排序最佳化的比較,編譯器記憶體屏障,

簡介

大多數現代計算機為了提高性能而採取亂序執行,這使得記憶體屏障成為必須。
語義上,記憶體屏障之前的所有寫操作都要寫入記憶體;記憶體屏障之後的讀操作都可以獲得同步屏障之前的寫操作的結果。因此,對於敏感的程式塊,寫操作之後、讀操作之前可以插入記憶體屏障。

底層體系結構相關的原語

大多數處理器提供了記憶體屏障指令:
  • 完全記憶體屏障(full memory barrier)保障了早於屏障的記憶體讀寫操作的結果提交到記憶體之後,再執行晚於屏障的讀寫操作。
  • 記憶體讀屏障(read memory barrier)僅確保了記憶體讀操作;
  • 記憶體寫屏障(write memory barrier)僅保證了記憶體寫操作。
記憶體屏障是底層原語,是記憶體排序的一部分,在不同體系結構下變化很大而不適合推廣。需要認真研讀硬體的手冊以確定記憶體屏障的辦法。x86指令集中的記憶體屏障指令是:
lfence (asm), void _mm_lfence (void) 讀操作屏障sfence (asm), void _mm_sfence (void)[1] 寫操作屏障mfence (asm), void _mm_mfence (void)[2] 讀寫操作屏障
常見的x86/x64,通常使用lock指令前綴加上一個空操作來實現,注意當然不能真的是nop指令,但是可以用來實現空操作的指令其實是很多的,比如Linux中採用的
addl $0, 0 (%esp)
存儲器也提供了另一套語義的記憶體屏障指令:
  • acquire semantics: 該操作結果可利用要早於代碼中後續的所有操作的結果。
  • release semantics: 該操作結果可利用要晚於代碼中之前的所有操作的結果。
  • fence semantics: acquire與release兩種語義的共同有效。即該操作結果可利用要晚於代碼中之前的所有操作的結果,且該操作結果可利用要早於代碼中後續的所有操作的結果。
Intel Itanium處理器,具有記憶體屏障mf的指令,具有下述modifiers:
  • acq (acquire)
  • rel (release).

Windows API的記憶體屏障實現

下述同步函式使用適當的屏障來確保記憶體有序:
  • 進出臨界區(critical section)的函式
  • 觸發(signaled)同步對象的函式
  • 等待函式(Wait function)
  • 互鎖函式(Interlocked function)

多執行緒編程與記憶體可見性

多執行緒程式通常使用高層程式設計語言中的同步原語,如Java.NET Framework,或者APIpthreadWindows API。因此一般不需要明確使用記憶體屏障。
記憶體可見性問題,主要是高速快取與記憶體的一致性問題。一個處理器上的執行緒修改了某數據,而在另一處理器上的執行緒可能仍然使用著該數據在專用cache中的老值,這就是可見性出了問題。解決辦法是令該數據為volatile屬性,或者讀該數據之前執行記憶體屏障。

亂序執行與編譯器重排序最佳化的比較

C與C++語言中,volatile關鍵字意圖允許記憶體映射的I/O操作。這要求編譯器對此的數據讀寫按照程式中的先後順序執行,不能對volatile記憶體的讀寫重排序。因此關鍵字volatile並不保證是一個記憶體屏障。
對於Visual Studio 2003,編譯器保證對volatile的操作是有序的,但是不能保證處理器的亂序執行。因此,可以使用InterlockedCompareExchange或InterlockedExchange函式。
對於Visual Studio 2005及以後版本,編譯器對volatile變數的讀操作使用acquire semantics,對寫操作使用release semantics。

編譯器記憶體屏障

編譯器會對生成的可執行代碼做一定最佳化,造成亂序執行甚至省略(不執行)。gcc編譯器在遇到內嵌彙編語句:
asm volatile("" ::: "memory");
將以此作為一條記憶體屏障,重排序記憶體操作。即此語句之前的各種編譯最佳化將不會持續到此語句之後。也可用內建的__sync_synchronize
Microsoft Visual C++的編譯器記憶體屏障為:
_ReadWriteBarrier() MemoryBarrier()
Intel C++編譯器的記憶體屏障為:
__memory_barrier()

相關詞條

熱門詞條

聯絡我們