重排序

在執行程序時,爲了提高性能,編譯器和處理器常常會對指令做重排序。重排序分3種類型。

1)編譯器優化的重排序。編譯器在不改變單線程程序語義的前提下,可以重新安排語句的執行順序。

2)指令級並行的重排。現代處理器採用了指令級並行技術(Instruction-Level Parallelism,ILP)來將多條指令重疊執行。如果不存在數據依賴性,處理器可以改變語句對應機器指令的執行順序。

3)內存系統的重排序。由於處理器使用緩存和讀/寫緩衝區,這使得加載和存儲操作看上去可能是在亂序執行。

​ 對於編譯器,JMM的編譯器重排序規則會禁止特定類型的編譯器重排序(不是所有的編譯器重排序都要禁止)。對於處理器重排序,JMM的處理器重排序規則會要求Java編譯器在生成指令序列時,插入特定類型的內存屏障(Memory Barriers,Intel稱之爲Memory Fence)指令,通過內存屏障指令來禁止特定類型的處理器重排序。

​ JMM屬於語言級的內存模型,它確保在不同的編譯器和不同的處理器平臺之上,通過禁止特定類型的編譯器重排序和處理器重排序,爲程序員提供一致的內存可見性保證。

​ 現代的處理器使用寫緩衝區臨時保存向內存寫入的數據。寫緩衝區可以保證指令流水線持續運行,它可以避免由於處理器停頓下來等待向內存寫入數據而產生的延遲。同時,通過以批處理的方式刷新寫緩衝區,以及合併寫緩衝區中對同一內存地址的多次寫,減少對內存總線的佔用。雖然寫緩衝區有這麼多好處,但每個處理器上的寫緩衝區,僅僅對它所在的處理器可見。這個特性會對內存操作的執行順序產生重要的影響:處理器對內存的讀/寫操作的執行順序,不一定與內存實際發生的讀/寫操作順序一致。

​ 爲了保證內存可見性,Java編譯器在生成指令序列的適當位置會插入內存屏障指令來禁止特定類型的處理器重排序。JMM把內存屏障指令分爲4類:

在這裏插入圖片描述

StoreLoad Barriers是一個“全能型”的屏障,它同時具有其他3個屏障的效果

重排序是指編譯器和處理器爲了優化程序性能而對指令序列進行重新排序的一種手段。

1. 數據依賴性

​ 如果兩個操作訪問同一個變量,且這兩個操作中有一個爲寫操作,此時這兩個操作之間就存在數據依賴性。數據依賴分爲下列3種類型:寫後讀、寫後寫、讀後寫。只要重排序兩個操作的執行順序,程序的執行結果就會被改變。

​ 編譯器和處理器可能會對操作做重排序。編譯器和處理器在重排序時,會遵守數據依賴性,編譯器和處理器不會改變存在數據依賴關係的兩個操作的執行順序。

​ 這裏所說的數據依賴性僅針對單個處理器中執行的指令序列和單個線程中執行的操作,不同處理器之間和不同線程之間的數據依賴性不被編譯器和處理器考慮。

2. as-if-serial語義

as-if-serial語義的意思是:不管怎麼重排序(編譯器和處理器爲了提高並行度),(單線程)程序的執行結果不能被改變。編譯器、runtime和處理器都必須遵守as-if-serial語義。

爲了遵守as-if-serial語義,編譯器和處理器不會對存在數據依賴關係的操作做重排序,因爲這種重排序會改變執行結果。

3. 程序順序規則

1)A happens-before B。

2)B happens-before C。

3)A happens-before C。

這裏的第3個happens-before關係,是根據happens-before的傳遞性推導出來的。

發佈了311 篇原創文章 · 獲贊 114 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章