內(nèi)存屏障,也稱內(nèi)存柵欄,內(nèi)存柵障,屏障指令等, 是一類同步屏障指令,是CPU或編譯器在對內(nèi)存隨機訪問的操作中的一個同步點,使得此點之前的所有讀寫操作都執(zhí)行后才可以開始執(zhí)行此點之后的操作。
簡介大多數(shù)現(xiàn)代計算機為了提高性能而采取亂序執(zhí)行,這使得內(nèi)存屏障成為必須。
語義上,內(nèi)存屏障之前的所有寫操作都要寫入內(nèi)存;內(nèi)存屏障之后的讀操作都可以獲得同步屏障之前的寫操作的結果。因此,對于敏感的程序塊,寫操作之后、讀操作之前可以插入內(nèi)存屏障。
底層體系結構相關的原語大多數(shù)處理器提供了內(nèi)存屏障指令:
完全內(nèi)存屏障(full memory barrier)保障了早于屏障的內(nèi)存讀寫操作的結果提交到內(nèi)存之后,再執(zhí)行晚于屏障的讀寫操作。
內(nèi)存讀屏障(read memory barrier)僅確保了內(nèi)存讀操作;
內(nèi)存寫屏障(write memory barrier)僅保證了內(nèi)存寫操作。
內(nèi)存屏障是底層原語,是內(nèi)存排序的一部分,在不同體系結構下變化很大而不適合推廣。需要認真研讀硬件的手冊以確定內(nèi)存屏障的辦法。x86指令集中的內(nèi)存屏障指令是:
lfence (asm), void _mm_lfence (void) 讀操作屏障sfence (asm), void _mm_sfence (void)[1] 寫操作屏障mfence (asm), void _mm_mfence (void)[2] 讀寫操作屏障常見的x86/x64,通常使用lock指令前綴加上一個空操作來實現(xiàn),注意當然不能真的是nop指令,但是可以用來實現(xiàn)空操作的指令其實是很多的,比如Linux中采用的
addl $0, 0 (%esp)存儲器也提供了另一套語義的內(nèi)存屏障指令:
acquire semantics: 該操作結果可利用要早于代碼中后續(xù)的所有操作的結果。
release semantics: 該操作結果可利用要晚于代碼中之前的所有操作的結果。
fence semantics: acquire與release兩種語義的共同有效。即該操作結果可利用要晚于代碼中之前的所有操作的結果,且該操作結果可利用要早于代碼中后續(xù)的所有操作的結果。
Intel Itanium處理器,具有內(nèi)存屏障mf的指令,具有下述modifiers1:
acq (acquire)
rel (release).
Windows API的內(nèi)存屏障實現(xiàn)下述同步函數(shù)使用適當?shù)钠琳蟻泶_保內(nèi)存有序:
進出臨界區(qū)(critical section)的函數(shù)
觸發(fā)(signaled)同步對象的函數(shù)
等待函數(shù)(Wait function)
互鎖函數(shù)(Interlocked function)1
多線程編程與內(nèi)存可見性多線程程序通常使用高層程序設計語言中的同步原語,如Java與.NET Framework,或者API如pthread或Windows API。因此一般不需要明確使用內(nèi)存屏障。
內(nèi)存可見性問題,主要是高速緩存與內(nèi)存的一致性問題。一個處理器上的線程修改了某數(shù)據(jù),而在另一處理器上的線程可能仍然使用著該數(shù)據(jù)在專用cache中的老值,這就是可見性出了問題。解決辦法是令該數(shù)據(jù)為volatile屬性,或者讀該數(shù)據(jù)之前執(zhí)行內(nèi)存屏障。2
亂序執(zhí)行與編譯器重排序優(yōu)化的比較C與C++語言中,volatile關鍵字意圖允許內(nèi)存映射的I/O操作。這要求編譯器對此的數(shù)據(jù)讀寫按照程序中的先后順序執(zhí)行,不能對volatile內(nèi)存的讀寫重排序。因此關鍵字volatile并不保證是一個內(nèi)存屏障。
對于Visual Studio 2003,編譯器保證對volatile的操作是有序的,但是不能保證處理器的亂序執(zhí)行。因此,可以使用InterlockedCompareExchange或InterlockedExchange函數(shù)。
對于Visual Studio 2005及以后版本,編譯器對volatile變量的讀操作使用acquire semantics,對寫操作使用release semantics。1
編譯器內(nèi)存屏障編譯器會對生成的可執(zhí)行代碼做一定優(yōu)化,造成亂序執(zhí)行甚至省略(不執(zhí)行)。gcc編譯器在遇到內(nèi)嵌匯編語句:
asm volatile("" ::: "memory");將以此作為一條內(nèi)存屏障,重排序內(nèi)存操作。即此語句之前的各種編譯優(yōu)化將不會持續(xù)到此語句之后。也可用內(nèi)建的__sync_synchronize
Microsoft Visual C++的編譯器內(nèi)存屏障為:
_ReadWriteBarrier() MemoryBarrier()Intel C++編譯器的內(nèi)存屏障為1:
__memory_barrier()本詞條內(nèi)容貢獻者為:
王沛 - 副教授、副研究員 - 中國科學院工程熱物理研究所