CPU緩存機制 併發編程的基礎

CPU多核緩存架構java

 

一、多線程環境下存在的問題
緩存

   在多處理器系統中,每一個處理器都有本身的高速緩存,而它們又共享同一主內存(RAM)。基於高速緩存的存儲交互很好地解決了處理器與內存的速度矛盾,可是也引入了新的問題:緩存一致性(CacheCoherence)。多線程

  如有兩個線程 T1 和 T2 都去計算 x + 1的值(x初始值爲1),T1線程的由 CPU1去處理, T2 線程由CPU2去處理;CPU1和CPU2的高速緩存中的副本都是 x = 1,通過CPU加1操做後,再次放入的CPU一、CPU2的高速緩存中的數都爲 x = 2 ,當數據放入到主內存中的時候,就引起了緩存不一致性的問題;架構

  爲了解決一致性的問題,須要各個處理器訪問緩存時都遵循一些協議(緩存一致性協議),在讀寫時要根據協議來進行操做,這類協議有MSIMESI(IllinoisProtocol)、MOSI、Synapse、Firefly及DragonProtocol,等等。解決一致性的問題也可使用總線加鎖的方式,當CPU1讀主內存同一段邏輯空間時,給總線加一把鎖,CPU2讀寫的時候都沒有辦法進行,這樣也能夠解決該問題,可是這樣的效率過低,通常不使用這種方式優化

二、MESI緩存一致性協議  spa

CPU緩存的最小單位:緩存行(Cache line),有可能32字節、64字節、128字節,根據CPU來定;線程

(1)MESI的介紹blog

(2) MESI緩存一致性協議的工做原理排序

以上面多線程中計算 x 加1的操做爲例;內存

(1)T1 從主存中讀取 x = 1,放到 CPU1 的緩存,狀態標記爲獨佔(E),而且 CPU1 時刻監聽總線當中其餘CPU對這塊內存的操做(總線嗅探機制);

(2)若 T1 計算以後尚未會寫到主存中,此時 T2 由CPU2計算x加1的操做,從主存中讀取x = 1,放入CPU2的緩存中;

(3)當 T2 經過總線從 主存中讀取了 x = 1,T1的CPU1經過總線嗅探機制知道了T2從主存中讀取了同塊內存,則將 CPU1 中的狀態修改成共享(S)

CPU2 中的狀態標記爲共享(S),同時 CPU2 時刻監聽總線當中其餘CPU對這塊內存的操做;

(4)此時,T1經過 CPU1 計算完成了的結果:x = 2,須要將結果回寫到主存中;首先鎖住CPU1的緩存行,將CPU1 的緩存的狀態標記爲修改(M),接着發送消息給總線,其餘的CPU一直在嗅探,嗅探到消息以後將其餘的CPU的緩存狀態標記爲無效(I),此時CPU2的緩存狀態被修改的無效;

(5)接着,CPU1 將計算結果 x = 2 回寫到主存中(主存中的數據此時變爲 x = 2),接着CPU1 將緩存的狀態標記爲獨佔(E), 丟棄掉其餘CPU緩存狀態爲無效的數據;

(6)接着CPU2 從新從主存中讀取數據(x = 2),經過總線嗅探機制CPU1又嗅探到CPU2讀取了主存中的這塊內存區域,則將 CPU1的緩存狀態和CPU2的緩存狀態都設置爲共享(S)....

失效指的是緩存行的失效,

若兩個CUP同一個時間都去修改主存中 x 的值,則在一個指令週期內會進行裁決,一個認爲有效,另外一個認爲無效;有一個裁決爲無效了,不必定再次去主存中讀取了,取決於程序的寫法;

好比在java中,變量前面增長 volatile 關鍵字,則在底層就去要遵照緩存一致性協議;

(3)什麼狀況下緩存一致性協議會失效?

狀況一:

若是x 的存儲長度大於緩存行的存儲單元;數據存儲橫跨兩個以上的緩存行,沒有辦法去給緩存行去加鎖了,只能經過總線加鎖的方式;

狀況二:

CPU自己不支持緩存一致性協議,好比早期的奔騰系列CPU;

三、指令重排序問題
  爲了使得處理器內部的運算單元能儘可能被充分利用,處理器可能會對輸入代碼進行亂序執行(Out-Of-Order Execution)優化,處理器會在計算以後將亂序執行的結果重組,保證該結果與順序執行的結果是一致的,但並不保證程序中各個語句計算的前後順序與輸入代碼中的順序一致。所以,若是存在一個計算任務依賴另外一個計算任務的中間結果,那麼其順序性並不能靠代碼的前後順序來保證。與處理器的亂序執行優化相似,Java虛擬機的即時編譯器中也有相似的指令重排序(Instruction Reorder)優化。

相關文章
相關標籤/搜索