在有多個核的處理器的處理器中,每一個核都有本身的cache,而如何確保多個核的cache內容的一致則是一個很容易遇到的問題,MESI協議就是一個專門用來解決cache一致性的協議。不少處理器使用的都是MESI協議或者MESI協議的變體,而MESI協議其實也是MSI協議的變種。MESI協議採用了回寫(write-back)的策略來更新cache,使得其性能進一步提升,但也帶來了額外的風險,回寫帶來的問題能夠在編寫程序時使用內存屏障來規避。java
MESI協議名字的由來是由其描述的四個cache狀態組成的,分別是M(modified)、E(exclusive)、S(shared)和I(invalid)。各個狀態的描述具體以下:緩存
狀態 | 描述 |
---|---|
Modified | 當前cache的內容有效,數據已被修改並且與內存中的數據不一致,數據只在當前cache裏存在 |
Exclusive | 當前cache的內容有效,數據與內存中的數據一致,數據只在當前cache裏存在 |
Shared | 當前cache的內容有效,數據與內存中的數據一致,數據在多個cache裏存在 |
Invalid | 當前cache無效 |
MESI協議實際上是一個狀態機,cache的狀態會跟根據外部事件的刺激而發生轉移,具體的事件分爲兩類:處理器對cache的請求和總線對cache的請求:併發
而狀態之間的轉換以下圖:性能
初始狀態 | 操做 | 響應 |
---|---|---|
Invalid(I) | PrRd |
|
PrWr |
|
|
Exclusive(E) | PrRd |
|
PrWr |
|
|
Shared(S) | PrRd |
|
PrWr |
|
|
Modified(M) | PrRd |
|
PrWr |
|
初始狀態 | 操做 | 響應 |
---|---|---|
Invalid(I) | BusRd |
|
BusRdX/BusUpgr |
|
|
Exclusive(E) | BusRd |
|
BusRdX |
|
|
Shared(S) | BusRd |
|
BusRdX |
|
|
Modified(M) | BusRd |
|
BusRdX |
|
MESI的設計比較簡單直接,可是其中有兩個地方會致使性能降低:一是更新invalidate狀態的cache時,須要嘗試從其餘cpu甚至是內存獲取最新的數據;二是使一個cache變爲invalidate時須要等待其餘cpu的確認;這兩個操做都是比較耗時的,若是cpu在這兩個過程當中一直等待的話,就會造成浪費。優化
爲了下降寫入invalidate狀態的cache的延時,能夠引入store buffer。既然寫入操做不管如何必定會發生,那麼cpu就先發出信號通知其餘cpu這個cache已經失效,而後再將本次的寫操做更新到store buffer中,等到其餘cpu都確認收到信號後再將結果寫到內存中。spa
這樣就避免了更新cache時阻塞等待其餘cpu確認的耗時,可是也會致使cpu的更新並無及時寫入cache,因此當cpu須要讀取cache時,它須要先確認store buffer中是否有所需的數據,這個機制成爲store forwarding。值得注意的是,當cpu在讀寫本身的store buffer時,對應的數據變動其餘cpu是感知不到的。設計
當cpu收到使某個cache失效的消息時,預期的行爲是cpu立刻執行這個失效操做。但實際上cpu並不會立刻執行失效操做,而是先發送確認收到的消息,而後將失效操做加入到invalidate queue中,queue中的操做隨後會在適當的時刻執行(並不必定是立刻)。之因此須要invalidate queue一樣是由於invalidate操做開銷比較大,cpu爲了執行invalidate操做必須丟棄cache,致使cache命中率降低。這樣的好處是可以提升cpu的性能,但同時也致使cache中可能存在過時的數據。3d
針對store buffer和invalidate queue這兩個優化帶來的問題,咱們又提供了內存屏障做爲解決方案。內存屏障交給了編寫程序的人的手裏,利用它就能夠規避上面提到的問題。cdn
內存屏障分爲寫屏障和讀屏障,編寫程序時能夠在指望的地方加入內存屏障。寫屏障會強制cpu清空store buffer的內容,也就是將全部的變動都寫入cache,隨後變動也就寫入了內存,使其對其餘cpu可見;讀屏障會強制cpu執行invalidate queue中的全部invalidate操做,使自身的cache內容失效,從而使cpu從內存或者其餘cpu中獲取最新的cache數據。blog
MESI協議乍一看和java裏的內存模型以及volatile關鍵字有些類似,後續再詳細展開了。