大家都知道,計算機在執行程式時,每條指令都是在cpu中執行的,而執行指令過程中,勢必涉及到資料的讀取和寫入。由於程式執行過程中的臨時資料是存放在主存(物理記憶體)當中的,這時就存在乙個問題,由於cpu執行速度很快,而從記憶體讀取資料和向記憶體寫入資料的過程跟cpu執行指令的速度比起來要慢的多,因此如果任何時候對資料的操作都要通過和記憶體的互動來進行,會大大降低指令執行的速度。因此在cpu裡面就有了快取記憶體(cache).
也就是,當程式在執行過程中,會將運算需要的資料從主存複製乙份到cpu的快取記憶體當中,那麼cpu進行計算時就可以直接從它的快取記憶體讀取資料和向其中寫入資料,當運算結束之後,再將快取記憶體中的資料重新整理到主存當中。舉個簡單的例子,比如下面的這段**:
i = i+1;當執行緒執行這個語句時,會先從主存當中讀取i的值,然後複製乙份到快取記憶體當中,然後cpu執行指令對i進行加1操作,然後將資料寫入快取記憶體,最後將快取記憶體中i最新的值重新整理到主存當中。
這個**在單執行緒中執行是沒有任何問題的,但是在多執行緒中執行就會有問題了。在多核cpu中,每條執行緒可能執行於不同的cpu中,因此每個執行緒執行時有自己的快取記憶體(對單核cpu來說,其實也會出現這種問題,只不過是以執行緒排程的形式來分別執行的)。本文我們以多核cpu為例。
比如同時有2個執行緒執行這段**,假如初始時i的值為0,那麼我們希望兩個執行緒執行完之後i的值變為2。但是事實會是這樣嗎?
可能存在下面一種情況:初始時,兩個執行緒分別讀取i的值存入各自所在的cpu的快取記憶體當中,然後執行緒1進行加1操作,然後把i的最新值1寫入到記憶體(主存)。此時執行緒2的快取記憶體當中i的值還是0,進行加1操作之後,i的值為1,然後執行緒2把i的值寫入記憶體(主存)。
最終結果i的值是1,而不是2。這就是著名的快取一致性問題。通常稱這種被多個執行緒訪問的變數為共享變數。
也就是說,如果乙個變數在多個cpu中都存在快取(一般在多執行緒程式設計時才會出現),那麼就可能存在快取不一致的問題。
為了解決快取不一致性問題,通常來說有以下2種解決方法:
1)通過在匯流排加lock#鎖的方式
2)通過快取一致性協議
這2種方式都是硬體層面上提供的方式。
在早期的cpu當中,是通過在匯流排上加lock#鎖的形式來解決快取不一致的問題。因為cpu和其他部件進行通訊都是通過匯流排來進行的,如果對匯流排加lock#鎖的話,也就是說阻塞了其他cpu對其他部件訪問(如記憶體),從而使得只能有乙個cpu能使用這個變數的記憶體。比如上面例子中 如果乙個執行緒在執行 i = i +1,如果在執行這段**的過程中,在匯流排上發出了lcok#鎖的訊號,那麼只有等待這段**完全執行完畢之後,其他cpu才能從變數i所在的記憶體讀取變數,然後進行相應的操作。這樣就解決了快取不一致的問題。
但是上面的方式會有乙個問題,由於在鎖住匯流排期間,其他cpu無法訪問記憶體,導致效率低下。效率,我要的是效率!!!
所以就出現了快取一致性協議。最出名的就是intel 的mesi協議,mesi協議保證了每個快取中使用的共享變數的副本是一致的。它核心的思想是:當cpu寫資料時,
如果發現操作的變數是共享變數,即在其他cpu中也存在該變數的副本,會發出訊號通知其他cpu將該變數的快取行置為無效狀態
,因此當其他cpu需要讀取這個變數時,發現自己快取中快取該變數的快取行是無效的,那麼它就會從記憶體重新讀取。
文章**
快取一致性協議MESI
處理器上有一套完整的協議,來保證cache一致性。比較經典的cache一致性協議當屬mesi協議,奔騰處理器有使用它,很多其他的處理器都是使用它的變種。單核cache中每個cache line有2個標誌 dirty和valid標誌,它們很好的描述了cache和memory 記憶體 之間的資料關係 資...
MESI(快取一致性協議)
現在的處理器都是多核處理器,並且每個核都帶有多個快取 指令快取和資料快取,見下圖 為什麼需要快取呢,這是因為cpu訪問記憶體的速度比較慢,所以在cpu和記憶體之間加了個快取以提高訪問速度。既然每個核都有快取,那麼假設兩個核或者多個核同時訪問同乙個變數時這些快取是如何進行同步的呢 快取細分為乙個個快取...
CPU 一致性快取協議MESI
計算機在執行指令的時候都是通過cpu進行逐條執行而在執行指令的過程中勢必涉及對資料的讀寫,而資料基本從磁碟載入到記憶體中cpu直接使用記憶體中的資料 由於cpu計算速度遠大於對記憶體的讀寫速度如果任何資料的讀寫都通過記憶體cpu的效率將會大打折扣,因此在cpu中引入暫存器作為快取記憶體提高系統效率例...