1. 程式人生 > >Java併發——快取一致性

Java併發——快取一致性

I. CPU多級快取

CPU的時鐘頻率非常的快,跑起來的速度遠遠超過了記憶體、硬碟。《碼農翻身》形象的比喻CPU為阿甘,跑的速度是記憶體的100倍,硬碟的1000多萬倍。如果直接靠CPU直接和記憶體打交道,那麼CPU要等待太久,浪費資源。

我們平時編寫的程式中,包含著很多連續建立的陣列、物件,各種迴圈、遞迴、呼叫同一函式等,其實本質上符合了區域性性原理區域性性原理具體是指在CPU訪問儲存裝置時,無論是存取資料還是存取指令,都趨於聚集在一片連續的區域中,主要包含時間與空間上的區域性性。

  • 時間區域性性 (Temporal Locality):如果一個數據被訪問,那麼在近期它很可能還會被再次訪問。
  • 空間區域性性 (Spatial Locality):如果一個數據被訪問,那麼與它相鄰的資料也很快會被訪問。

有了這樣的特性,就可以在CPU和記憶體之間新增快取記憶體儲存CPU經常訪問的資料,如下圖所示,緩解CPU和記憶體速度嚴重差異的問題。

快取記憶體

CPU和cache之間進行資料/指令存取,而記憶體(主存)和快取記憶體則通過系統匯流排來實現通訊。程式設計師編寫的程式以及資料被載入到記憶體中後,隨後被載入到快取記憶體,CPU執行完指令處理完資料將結果寫回快取記憶體中,最後快取記憶體再寫回記憶體。

然而,由於CPU的運算速度不斷提升,很快超越了一級快取的資料 I\O 能力,CPU廠商們又開始引入了多級的快取結構。多核的CPU至少擁有著不止一個一級快取,當快取記憶體中拷貝了記憶體中同一個資料多個副本時,CPU操作的是每一個副本,而如何保證副本與副本之間,以及副本與主存的資料一致呢?

多級快取

主要利用的是快取一致性的原則。

II. 快取一致性

MESI協議中的快取狀態

狀態 含義 監聽任務
M 被修改
Modified
因為快取行剛被修改,資料應是獨一無二的,與主存中的資料不一致,與其他CPU的快取中對應資料也不相同。但是一定會在將來某個時間點寫回主存中,這個時間點一定是在其他CPU讀取自己的(主存相應的)快取之前。 被修改的快取行必須時刻監聽所有想讀該快取行對應的主存/其他CPU快取的操作,因為要確保在CPU讀取操作之前把被修改的快取行寫回主存並將狀態變為 S
E 獨享的
Exclusive
被修改狀態的快取行要將資料寫回主存,此時可以認為是獨享的狀態。只有自己的快取和主存中資料一致,其他CPU對應的快取行還沒有更新。但是一定會在將來其他CPU讀取對應的快取行之前變為共享狀態。 獨享的快取行也必須監聽其他CPU快取讀取主存中對應快取行的操作,一旦有了這種操作,該快取行需要變成 S 狀態。
S 共享的
Shared
該狀態意味該快取行可能被多個CPU快取,並且各個快取中的資料與主存中的資料是一致的。當有一個CPU修改了該快取行中的資料,該快取行變為被修改狀態,其他CPU中對應的快取作廢,變為無效狀態 I 對於該快取行來說,要監聽其他快取使該快取行無效。
I 無效的
Invalid
快取行作廢,無效。 \

快取行狀態變化

本地快取當前狀態 事件 行為 本地快取狀態變化 觸發快取行狀態變化 其他快取行狀態變化
M 本地快取讀取本地快取資料 從本地快取讀取資料,狀態不變。觸發快取就是本身。其他快取則因為本地快取是M,早就是I了。 M->M M->M I
本地快取寫入本地快取資料 本地快取行狀態為M,繼續修改資料,狀態還是M。觸發快取就是本身。其他快取則因為本地快取是M,早就是I了。 M->M M->M I
觸發快取讀取本地快取對應資料 觸發快取行和其他快取行狀態都為I,觸發快取行想要讀取對應資料,必然得從主存走。本地快取行中資料將回寫到主存,本地狀態變為E。等到觸發快取從主存中獲取到資料,所有快取行都同步,狀態變為S M->E->S I->S I->S
觸發快取寫入本地快取對應資料 觸發快取行想要先寫對應快取,發現本地快取資料還沒同步,所有先走觸發快取讀取本地快取對應資料的流程。由於觸發快取要寫入資料,所以本地快取和其他快取會變為I,此時觸發快取行狀態相當於E,等觸發快取寫入成功最後會變為M,。 M->E->S->I I->S->E->M I->S->I
E 本地快取讀取本地快取資料 從本地快取讀取資料,狀態不變。觸發快取就是本身。其他快取則因為本地快取是E,早就是I了。 E->E E->E I
本地快取寫入本地快取資料 本地快取行已經回寫到主存,又繼續修改,狀態又變為M,其他快取行繼續I。觸發快取就是本身。 E->M E->M I
觸發快取讀取本地快取對應資料 本地快取行已經回寫到主存,觸發快取讀取資料之前需要先進行本地快取行資料的同步,所有快取行資料變為S。同步完成,觸發快取行也就正好讀完。 E->S I->S I->S
觸發快取寫入本地快取對應資料 觸發快取行想要寫對應資料,先經歷觸發快取讀取本地快取對應資料的過程。當共享完成,觸發快取行對資料進行更新,其他快取行都變為I,隨後自身變為E再變為M。 E->S->I I->S->E->M I->S->I
S 本地快取讀取本地快取資料 共享狀態讀取不影響。 S S S
本地快取寫入本地快取資料 共享狀態下,本地快取進行修改,其他快取行將先變成I,此時相當於本地快取行為E,修改後再變成M。觸發快取就是本地快取。 S->E->M S->E->M S->I
觸發快取讀取本地快取對應資料 共享狀態讀取不影響。 S S S
觸發快取寫入本地快取對應資料 共享狀態下,觸發快取想要修改,則本地快取和其他快取先變為I,觸發快取則變成E。修改完成後,觸發快取繼續變成M S->I S->E->M S->I
I 本地快取讀取本地快取資料 1. 如果其他快取沒有對應資料,狀態為I,本地快取從主存讀取資料,快取行變成E
2. 如果其他快取有對應資料,且狀態為M,則先將資料更新到主存,本地快取再從主存中讀取,兩個快取對應的快取行都變為S
3. 如果其他快取有對應資料,且狀態為S/E,意味著主存已經更新,可以直接讀取,這時兩個快取對應的快取行都變為S
I->E
I->S
I->S
I->E
I->S
I->S
I->I
M->E->S
E->S/S->S
本地快取寫入本地快取資料 1. 如果其他快取沒有對應資料,一開始所有快取行都為無效,所以需要先從主存中取資料同步,寫入所有快取,快取行狀態變為E。隨後進行本地快取更新,狀態變為M
2. 如果其他快取有資料但是快取行狀態為E,本地快取行為無效,所以需要先從主存中取資料同步,寫入本地快取,快取行狀態變為E。隨後進行本地快取更新,狀態變為M
3. 如果其他快取中有對應資料,且狀態為M,則先將其他快取中的資料更新到主存,本地快取再從主存中讀取,所有快取行都為S。隨後進行本地快取更新,本地快取行狀態變為M,其他快取變為I
I->S->E->I
I->S->E->I
I->S->E->I
I->S->E->I
I->S->E->I
I->S->E->I
I->S->I
E->S->I
M->E->S->I
觸發快取讀取本地快取對應資料 既然是本地快取行是I,觸發快取行的操作與它無關。 I \ \
觸發快取寫入本地快取對應資料 既然是本地快取行是I,觸發快取行的操作與它無關。 I \ \

參考文章