CPU缓存一致性协议MESI
吹劍出自《莊子》:“夫吹管也,猶有也;吹劍首者,而已矣。” 吹劍只能發出小聲,以示自謙。“并發吹劍錄”,表達的是筆者斗膽講一些并發編程有關的知識,由于涉及計算機體系架構,筆者才疏學淺,恐有錯漏,望諸位不吝賜教,吾定當改之。
CPU架構
緩存與主存
解讀緩存一致性(Cache Coherency),先看一下CPU的架構
圖示一個4核CPU,有三個級別的緩存,分為是L1 Cache(一級緩存)、L2 Cache(二級緩存)、L3 Cache(三級緩存)
其中一級緩存有兩部分組成:L1I Cache(一級指令緩存)和L1D Cache(一級數據緩存)。
越靠近CPU的緩存速度越快,單價也更昂貴。其中一級和二級如今都屬于片內緩存(在CPU核內,早期L2緩存是片外的)獨立歸屬給各個CPU,而三級緩存是CPU間共享的。
查詢緩存的時候也是由近及遠,優先從一級緩存去查找,找到就結束查找,找不到則再去二級緩存查找。二級緩存找不到去三級緩存查找。三級緩存還找不到就去主存(Main Memory)查找。這里說的主存,就是我們平常說的內存。
內存是DRAM(Dynamic RAM),緩存是SRAM(Static RAM)。
緩存行
CPU操作緩存的單位是”緩存行“(cacheline),也就是說如果CPU要讀一個變量x,那么其實是讀變量x所在的整個緩存行。
緩存行大小
好了,既然我們知道了CPU讀寫緩存的單位是緩存行,那么緩存行的大小是多少呢?
查看機器緩存行大小的方法有很多,在Linux上你可以查看如下文件確認緩存行大小:
# L1D Cache cat /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size# L1I Cache cat /sys/devices/system/cpu/cpu0/cache/index1/coherency_line_size# L2 Cache cat /sys/devices/system/cpu/cpu0/cache/index2/coherency_line_size# L3 Cache cat /sys/devices/system/cpu/cpu0/cache/index3/coherency_line_size或者用getconf命令:
# L1D Cache getconf LEVEL1_DCACHE_LINESIZE# L1I Cache getconf LEVEL1_ICACHE_LINESIZE# L2 Cache getconf LEVEL2_CACHE_LINESIZE# L3 Cache getconf LEVEL3_CACHE_LINESIZE一般會看到:64。表示的是64字節。
注意,單核CPU上,可能沒有L3緩存。比如我在騰訊云買的最低配云主機……
MESI
并發場景下(比如多線程)如果操作相同變量,如何保證每個核中緩存的變量是正確的值,這涉及到一些”緩存一致性“的協議。其中應用最廣的就是MESI協議(當然這并不是唯一的緩存一致性協議)。
狀態介紹
在緩存行的元信息中有一個Flag字段,它會表示4種狀態,分為對應如下所說的M、E、S、I狀態。【知乎的表格是真的丑!】
| M(Modified) | 代表該緩存行中的內容被修改了,并且該緩存行只被緩存在該CPU中。這個狀態的緩存行中的數據和內存中的不一樣,在未來的某個時刻它會被寫入到內存中(當其他CPU要讀取該緩存行的內容時。或者其他CPU要修改該緩存對應的內存中的內容時 |
| E(Exclusive) | 代表該緩存行對應內存中的內容只被該CPU緩存,其他CPU沒有緩存該緩存對應內存行中的內容。這個狀態的緩存行中的內容和內存中的內容一致。該緩存可以在任何其他CPU讀取該緩存對應內存中的內容時變成S狀態。或者本地處理器寫該緩存就會變成M狀態 |
| S(Shared) | 該狀態意味著數據不止存在本地CPU緩存中,還存在別的CPU的緩存中。這個狀態的數據和內存中的數據是一致的。當其他CPU修改該緩存行對應的內存的內容時會使該緩存行變成 I 狀態 |
| I(Invalid) | 代表該緩存行中的內容是無效的 |
總線嗅探機制
CPU和內存通過總線(BUS)互通消息。
CPU感知其他CPU的行為(比如讀、寫某個緩存行)就是是通過嗅探(Snoop)線性中其他CPU發出的請求消息完成的,有時CPU也需要針對總線中的某些請求消息進行響應。這被稱為”總線嗅探機制“。
這些消息類型分為請求消息和響應消息兩大類,細分為6小類。
【知乎的表格是真的丑!】
| Read | 請求 | 通知其他處理器和內存,當前處理器準備讀取某個數據。該消息內包含待讀取數據的內存地址 |
| Read Response | 響應 | 該消息內包含了被請求讀取的數據。該消息可能是主內存返回的,也可能是其他高速緩存嗅探到Read 消息返回的 |
| Invalidate | 請求 | 通知其他處理器刪除指定內存地址的數據副本(緩存行中的數據)。所謂“刪除”,其實就是更新下緩存行對應的FLAG(MESI那個) |
| Invalidate Acknowledge | 響應 | 接收到Invalidate消息的處理器必須回復此消息,表示已經刪除了其高速緩存內對應的數據副本 |
| Read Invalidate | 請求 | 此消息為Read 和 Invalidate消息組成的復合消息,主要是用于通知其他處理器當前處理器準備更新一個數據了,并請求其他處理器刪除其高速緩存內對應的數據副本。接收到該消息的處理器必須回復Read Response 和 Invalidate Acknowledge消息 |
| Writeback | 響應 | 消息包含了需要寫入內存的數據和其對應的內存地址 |
狀態流轉
記憶要點
有些眼花繚亂,個人總結了一些要點,方便記憶。
- I 狀態有5條外出的線(local read有兩種可能的狀態轉移)
- 當其他CPU沒有這個緩存行時,當前CPU從內存取緩存行更新到Cache,并把狀態設置為E
- 當其他CPU有這份數據時:
- 如果其他CPU是M狀態,則同步其緩存到主存,然后兩個CPU狀態再變為S
- 如果其他CPU是S或E,則兩個CPU狀態都變為S
?
- MSE三個狀態都是有4條外出的線(對應4種操作,只會流轉到一個狀態)
- 而想從其他狀態流轉到達E狀態,比較刁鉆。只能從?I 狀態進行local read,并且其他CPU沒有該緩存行數據時,三個限定條件(加粗部分)缺一不可。
舉例
假設CPU0、CPU1、CPU2、CPU3中有一個緩存行(包含變量x)都是S狀態。
此時CPU1要對變量x進行寫操作,這時候通過總線嗅探機制,CPU0、CPU2、CPU3中的緩存行會置為I狀態(無效),然后給CPU1發響應(Invalidate Acknowledge),收到全部響應后CPU1會完成對于變量x的寫操作,更新了CPU1內的緩存行為M狀態,但不會同步到內存中。
接著CPU0想要對變量x執行讀操作,卻發現本地緩存行是I狀態,就會觸發CPU1去把緩存行寫入(回寫)到內存中,然后CPU0再去主存中同步最新的值。
Store Buffer
當然前面的描述隱藏了一些細節,比如實際CPU1在執行寫操作,更新緩存行的時候,其實并不會等待其他CPU的狀態都置為I狀態,才去做些操作,這是一個同步行為,效率很低。當前的CPU都引入了Store Buffer(寫緩存器)技術,也就是在CPU和cache之間又加了一層buffer,在CPU執行寫操作時直接寫StoreBuffer,然后就忙其他事去了,等其他CPU都置為I之后,CPU1才把buffer中的數據寫入到緩存行中。
Invalidate Queue
看前面的描述,執行寫操作的CPU1很聰明啦,引入了store buffer不等待其他CPU中的對應緩存行失效就忙別的去了。而其他CPU也不傻,實際上他們也不會真的把緩存行置為I后,才給CPU0發響應。他們會寫入一個Invalidate Queue(無效化隊列),還沒把緩存置為I狀態就發送響應了。
后續CPU會異步掃描Invalidate Queue,將緩存置為I狀態。和Store Buffer不同的是,在CPU1后續讀變量x的時候,會先查Store Buffer,再查緩存。而CPU0要讀變量x時,則不會掃描Invalidate Queue,所以存在臟讀可能。
L3 Cache在MESI中的角色
L3 緩存是所有CPU共享的一個緩存。縱觀剛才描述的MESI,好像涉及的都是CPU內的緩存更新,不涉及L3緩存,那么L3緩存在MESI中扮演什么角色呢?
其實在常見的MESI的狀態流程描述中(我上文也是),所有提到”內存“的地方都是值得商榷的。比如我上一節舉的例子中,CPU0中某緩存行是I,CPU1 中是M。當CPU0想到執行local read操作時,就會觸發CPU1中的緩存寫入到內存中,然后CPU0從內存中取最新的緩存行。其實準確來講這里是不準確的,因為由于L3緩存的存在,這里其實是直接從L3緩存讀取緩存行,而不直接訪問內存。
個人猜測是如果描述MESI狀態流轉的時候引入L3緩存,會造成描述會極其復雜。所以一般的描述都好似有意地忽略了L3緩存。
尾聲
從CPU的底層視角切入并發編程,其實還有很多可以介紹,比如偽共享、內存屏障又或者SIMD。然而技術文章畢竟應該做到主次分明,不能貪多。試圖大包大攬,反而讓讀者難以明辨綱要。這類文章讀起來也過于枯燥,讀者大多以收藏代替閱讀……
所以更多相關文章請關注后續啦!當然前提是有人樂意閱讀的話,我會繼續更新的,感謝支持!
你也可以關注我的公眾號:編程往事。和我交個朋友!
?
參考資料
https://stackoverflow.com/questions/54282246/whats-l3-role-part-in-mesi-protocal
https://www.cnblogs.com/jiagoujishu/p/13799459.html
https://www.sohu.com/a/407346190_120647979
編輯于 02-20
總結
以上是生活随笔為你收集整理的CPU缓存一致性协议MESI的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 本文把 TCP/IP 讲绝了
- 下一篇: @Transactional 注解的失效