1. 程式人生 > 其它 >什麼是指令重排序?為什麼要重排序?

什麼是指令重排序?為什麼要重排序?

什麼是重排序

假設我們寫了一個 Java 程式,包含一系列的語句,我們會預設期望這些語句的實際執行順序和寫的程式碼順序一致。

但實際上,編譯器、JVM 或者 CPU 都有可能出於優化等目的,對於實際指令執行的順序進行調整,這就是重排序。

重排序的好處:提高處理速度

圖中左側是 3 行 Java 程式碼,右側是這 3 行程式碼可能被轉化成的指令。可以看出 a = 100 對應的是 Load a、Set to 100、Store a,意味著從主存中讀取 a 的值,然後把值設定為 100,並存儲回去,同理, b = 5 對應的是下面三行 Load b、Set to 5、Store b,最後的 a = a + 10,對應的是 Load a、Set to 110、Store a。如果你仔細觀察,會發現這裡有兩次“Load a”和兩次“Store a”,說明存在一定的重排序的優化空間。

經過重排序之後,情況如下圖所示:

重排序後, a 的兩次操作被放到一起,指令執行情況變為 Load a、Set to 100、Set to 110、 Store a。下面和 b 相關的指令不變,仍對應 Load b、Set to 5、Store b。

可以看出,重排序後 a 的相關指令發生了變化,節省了一次 Load a 和一次 Store a。重排序通過減少執行指令,從而提高整體的執行速度,這就是重排序帶來的優化和好處。

重排序的 3 種情況

下面我們來看一下重排序的 3 種情況。

(1)編譯器優化

編譯器(包括 JVM、JIT 編譯器等)出於優化的目的,例如當前有了資料 a,把對 a 的操作放到一起效率會更高,避免讀取 b 後又返回來重新讀取 a 的時間開銷,此時在編譯的過程中會進行一定程度的重排。不過重排序並不意味著可以任意排序,它需要需要保證重排序後,不改變單執行緒內的語義,否則如果能任意排序的話,程式早就邏輯混亂了。

(2)CPU 重排序

CPU 同樣會有優化行為,這裡的優化和編譯器優化類似,都是通過亂序執行的技術來提高整體的執行效率。所以即使之前編譯器不發生重排,CPU 也可能進行重排,我們在開發中,一定要考慮到重排序帶來的後果。

(3)記憶體的“重排序”

記憶體系統內不存在真正的重排序,但是記憶體會帶來看上去和重排序一樣的效果,所以這裡的“重排序”打了雙引號。由於記憶體有快取的存在,在 JMM 裡表現為主存和本地記憶體,而主存和本地記憶體的內容可能不一致,所以這也會導致程式表現出亂序的行為。

舉個例子,執行緒 1修改了a的值,但是修改後沒有來得及把新結果寫回主存或者執行緒 2 沒來得及讀到最新的值,所以執行緒 2 看不到剛才執行緒 1 對 a 的修改,此時執行緒 2 看到的 a 還是等於初始值。但是執行緒 2 卻可能看到執行緒 1 修改 a 之後的程式碼執行效果,表面上看起來像是發生了重順序。