從JVM併發看CPU記憶體指令重排序

2021-07-12 07:31:55 字數 2583 閱讀 4205

這兩天,我拜讀了 dennis byrne 寫的一片博文memory barriers and jvm concurrency (中譯文記憶體屏障與jvm併發)。

文中提到:

對主存的一次訪問一般花費硬體的數百次時鐘週期。處理器通過快取(caching)能夠從數量級上降低記憶體延遲的成本這些快取為了效能重新排列待定記憶體操作的順序。也就是說,程式的讀寫操作不一定會按照它要求處理器的順序執行。

這段話是作者對記憶體屏障重要性的定義。通過cache降低記憶體延遲,這句話很好理解。但後面那句「為了效能重排序記憶體操作順序」,讓沒學好微機原理的我倍感疑惑。

cpu為何要重排序記憶體訪問指令?在哪種場景下會觸發重排序?作者在文中並未提及。

為了解答疑問,我在網上查閱了一些資料,在這裡跟大家分享一下。

我們知道現代cpu的主頻越來越高,與cache的互動次數也越來越多。當cpu的計算速度遠遠超過訪問cache時,會產生cache wait,過多的cache ?wait就會造成效能瓶頸。

針對這種情況,多數架構(包括x86)採用了一種將cache分片的解決方案,即將一塊cache劃分成互不關聯地多個 slots (邏輯儲存單元,又名 memory bank 或 cache bank),cpu可以自行選擇在多個 idle bank 中進行訪問。這種 smp 的設計,顯著提高了cpu的並行處理能力,也迴避了cache訪問瓶頸。

memory bank的劃分

一般 memory bank 是按cache address來劃分的。比如 偶數adress 0×12345000?分到 bank 0, 奇數address 0×12345100?分到 bank1。

重排序的種類

編譯期重排。編譯源**時,編譯器依據對上下文的分析,對指令進行重排序,以之更適合於cpu的並行執行。

執行期重排,cpu在執行過程中,動態分析依賴部件的效能,對指令做重排序優化。

為了方便理解,我們先來看一張cpu內部結構圖。

從圖中可以看到,這是一台配備雙cpu的計算機,cache 按位址被分成了兩塊 cache banks,分別是?cache bank0和 cache bank1。

理想的記憶體訪問指令順序:

1,cpu0往?cache address 0×12345000 寫入乙個數字 1。因為address 0×12345000是偶數,所以值被寫入 bank0.

2,cpu1讀取 bank0 address 0×12345000 的值,即數字1。

3,cpu0往 cache 位址 0×12345100 ?寫入乙個數字 2。因為address 0×12345100是奇數,所以值被寫入 bank1.

4,cpu1讀取 bank1 address ?0×12345100 的值,即數字2。

重排序後的記憶體訪問指令順序:

1,cpu0 準備往 bank0 address 0×12345000 寫入數字 1。

2,cpu0檢查 bank0 的可用性。發現 bank0 處於 busy 狀態。

3, cpu0 為了防止 cache等待,發揮最大效能,將記憶體訪問指令重排序。即先執行後面的 bank1 address 0×12345100 數字2的寫入請求。

4,cpu0檢查 bank1 可用性,發現bank1處於 idle 狀態。

5,cpu0 將數字2寫入 bank 1 address 0×12345100。

6,cpu1來讀取 ?0×12345000,未讀到 數字1,出錯。

7, cpu0 繼續檢查 bank0 的可用性,發現這次?bank0 可用了,然後將數字1寫入 0×12345000。

8, cpu1 讀取 0×12345100,讀到數字2,正確。

從上述觸發步驟中,可以看到第 3 步發生了指令重排序,並導致第 6步讀到錯誤的資料。

通過對指令重排,cpu可以獲得更快地響應速度,但也給編寫併發程式的程式設計師帶來了諸多挑戰。

記憶體屏障是用來防止cpu出現指令重排序的利器之一。

通過這個例項,不知道你對指令重排理解了沒有?

從圖中,可以看到,x86僅在stores after loadsincoherent instruction cache pipeline中會觸發重排。

stores after loads的含義是在對同乙個位址進行讀寫操作時,寫入在讀取後面,允許重排序。即滿足弱一致性(weak consistency),這是最可被接受的型別,不會造成太大的影響。

incoherent instruction cache pipeline是跟jit相關的型別,作用是在執行self-modifying code 時預防jit沒有flush指令快取。我不知道該型別跟指令排序有什麼關係,既然不在本文涉及範圍內,就不做深入**了。

從程式的執行看硬碟 記憶體 CPU的關係

首先先看乙個程式執行大概的過程。我們所看到的 是人可以閱讀的高階語言 但是計算機無法識別。當乙個程式被載入並執行執行的時候,檔案就會被編譯成計算機可讀取的二進位制檔案。這時記憶體會去讀取硬碟上的資料和指令並儲存在記憶體中。由於cpu無法直接讀取硬碟上的資料,大部分cpu的時間用於等待磁碟去讀取資料。...

CPU 記憶體 硬碟 指令簡述

cpu是central processing unit 處理器 的縮寫,內部有數百萬至數億個電晶體構成。cpu部件,稱為ic integrated circuit,積體電路 cpu內部由暫存器 控制器 運算器和時鐘四個部分構成,各部分間由電流訊號連通。暫存器,用來暫存指令 資料等處理物件,可看作是記...

我從內部看記憶體

1.記憶體分類 記憶體由於具備訪問速度快,訪問方式簡單等優點,成為了c或者是嵌入式硬體平台上不可或缺的元件。在開始學習如何使用記憶體之前,非常有必要先了解一下記憶體的分類 dram 它的基本原件是小電容,電容可以在兩個極板上保留電荷,但是需要定期的充電 重新整理 否則資料會丟失。缺點 由於要定期重新...