快取一致性問題的提出
計算機在執行程式時,每條指令都是在CPU中執行的,而執行指令過程中會涉及到數據的讀取和寫入。由於程式運行過程中的臨時數據是存放在主存(物理記憶體)當中的,這時就存在一個問題,由於CPU執行速度很快,而從記憶體讀取數據和向記憶體寫入數據的過程跟CPU執行指令的速度比起來要慢的多,因此如果任何時候對數據的操作都要通過和記憶體的互動來進行,會大大降低指令執行的速度。因此在CPU裡面就有了高速快取(Cache)的概念。當程式在運行過程中,會將運算需要的數據從主存複製一份到CPU的高速快取當中,那么CPU進行計算時就可以直接從它的高速快取讀取數據和向其中寫入數據,當運算結束之後,再將高速快取中的數據刷新到主存當中。
這一過程在單執行緒運行是沒有問題的,但是在多執行緒中運行就會有問題了。在多核CPU中,每條執行緒可能運行於不同的CPU中,因此每個執行緒運行時有自己的高速快取(對單核CPU來說,其實也會出現這種問題,只不過是以執行緒調度的形式來分別執行的)。這時CPU快取中的值可能和快取中的值不一樣,這就是著名的 快取一致性問題 。
在一個系統中,當許多不同的設備共享一個共同存儲器資源,在高速快取中的數據不一致,就會產生問題。這個問題在有數個CPU的多處理機系統中特別容易出現。
快取一致性可以分為三個層級:
在進行每個寫入運算時都立刻採取措施保證數據一致性
每個獨立的運算,假如它造成數據值的改變,所有進程都可以看到一致的改變結果
在每次運算之後,不同的進程可能會看到不同的值(這也就是沒有一致性的行為)
1.在進行每個寫入運算時都立刻採取措施保證數據一致性
2.每個獨立的運算,假如它造成數據值的改變,所有進程都可以看到一致的改變結果
3.在每次運算之後,不同的進程可能會看到不同的值(這也就是沒有一致性的行為)
快取一致性的解決方案
為了解決快取不一致性問題,通常來說有以下2種解決方法:
1.通過在匯流排加LOCK#鎖的方式
2.通過快取一致性協定
這2種方式都是硬體層面上提供的方式。
在早期的CPU當中,是通過在匯流排上加LOCK#鎖的形式來解決快取不一致的問題。因為CPU和其他部件進行通信都是通過匯流排來進行的,如果對匯流排加LOCK#鎖的話,也就是說阻塞了其他CPU對其他部件訪問(如記憶體),從而使得只能有一個CPU能使用這個變數的記憶體。在匯流排上發出了LCOK#鎖的信號,那么只有等待這段代碼完全執行完畢之後,其他CPU才能從其記憶體讀取變數,然後進行相應的操作。這樣就解決了快取不一致的問題。
但是由於在鎖住匯流排期間,其他CPU無法訪問記憶體,會導致效率低下。因此出現了第二種解決方案,通過快取一致性協定 來解決快取一致性問題。。最出名的就是Intel 的MESI協定,MESI協定保證了每個快取中使用的共享變數的副本是一致的。它核心的思想是:當CPU寫數據時,如果發現操作的變數是共享變數,即在其他CPU中也存在該變數的副本,會發出信號通知其他CPU將該變數的快取行置為無效狀態,因此當其他CPU需要讀取這個變數時,發現自己快取中快取該變數的快取行是無效的,那么它就會從記憶體重新讀取。
MESI協定
單核Cache中每個Cache line有2個標誌:dirty和valid標誌,它們很好的描述了Cache和Memory(記憶體)之間的數據關係(數據是否有效,數據是否被修改),而在多核處理器中,多個核會共享一些數據,MESI協定就包含了描述共享的狀態。
在MESI協定中,每個Cache line有4個狀態,可用2個bit表示,它們分別是:
M(Modified):這行數據有效,數據被修改了,和記憶體中的數據不一致,數據只存在於本Cache中。
E(Exclusive):這行數據有效,數據和記憶體中的數據一致,數據只存在於本Cache中。
S(Shared):這行數據有效,數據和記憶體中的數據一致,數據存在於很多Cache中。
I(Invalid):這行數據無效。
在該協定的作用下,雖然各cache控制器隨時都在監聽系統匯流排,但能監聽到的只有讀未命中、寫未命中以及共享行寫命中三種情況。讀監聽命中的有效行都要進入S態並發出監聽命中指示,但M態行要搶先寫回主存;寫監聽命中的有效行都要進入I態,但收到RWITM時的M態行要搶先寫回主存。總之監控邏輯並不複雜,增添的系統匯流排傳輸開銷也不大,但MESI協定卻有力地保證了主存塊髒拷貝在多cache中的一致性,並能及時寫回,保證cache主存存取的正確性。