每日一個知識點:Volatile 和 CAS 的弊端之匯流排風暴

架構技術專欄發表於2020-09-28

每日一個知識點系列的目的是針對某一個知識點進行概括性總結,可在一分鐘內完成知識點的閱讀理解,此處不涉及詳細的原理性解讀。

一、什麼是匯流排風暴

匯流排風暴,聽著真是一個帥氣的詞語,但如果發生在你的系統上那就不是很美麗了,廢話不多說,先看圖說結論。

img

什麼是匯流排風暴,先來看結論

在java中使用unsafe實現cas,而其底層由cpp呼叫匯編指令實現的,如果是多核cpu是使用lock cmpxchg指令,單核cpu 使用compxch指令。如果在短時間內產生大量的cas操作在加上 volatile的嗅探機制則會不斷地佔用匯流排頻寬,導致匯流排流量激增,就會產生匯流排風暴。 總之,就是因為volatile 和CAS 的操作導致BUS匯流排快取一致性流量激增所造成的影響。

img

二、一些需要的基礎知識

這裡有些基礎需要鋪墊下,瞭解過volatile和cas 的朋友都知道由於一個變數在多個快取記憶體中都存在,但由於快取記憶體間的資料是不共享的,所以勢必會有資料不一致的問題,為了解決這種問題處理器是通過匯流排鎖定快取鎖定這兩個機制來保證複雜記憶體操作的原子性的。

img

1、匯流排鎖

在早期處理器提供一個 LOCK# 訊號,CPU1在操作共享變數的時候會預先對匯流排加鎖,此時CPU2就不能通過匯流排來讀取記憶體中的資料了,但這無疑會大大降低CPU的執行效率。

2、快取一致性協議

由於匯流排鎖的效率太低所以就出現了快取一致性協議,Intel 的MESI協議就是其中一個佼佼者。MESI協議保證了每個快取變數中使用的共享變數的副本都是一致的。

3、MESI 的核心思想

modified(修改)、exclusive(互斥)、share(共享)、invalid(無效)

如上圖,CPU1使用共享資料時會先資料拷貝到CPU1快取中,然後置為獨佔狀態(E),這時CPU2也使用了共享資料,也會拷貝也到CPU2快取中。通過匯流排嗅探機制,當該CPU1監聽匯流排中其他CPU對記憶體進行操作,此時共享變數在CPU1和CPU2兩個快取中的狀態會被標記為共享狀態(S);

若CPU1將變數通過快取回寫到主存中,需要先鎖住快取行,此時狀態切換為(M),向匯流排發訊息告訴其他在嗅探的CPU該變數已經被CPU1改變並回寫到主存中。接收到訊息的其他CPU會將共享變數狀態從(S)改成無效狀態(I),快取行失效。若其他CPU需要再次操作共享變數則需要重新從記憶體讀取。

快取一致性協議失效的情況:

  • 共享變數大於快取行大小,MESI無法進行快取行加鎖;
  • CPU並不支援快取一致性協議

4、嗅探機制

每個處理器會通過嗅探器來監控匯流排上的資料來檢查自己快取內的資料是否過期,如果發現自己快取行對應的地址被修改了,就會將此快取行置為無效。當處理器對此資料進行操作時,就會重新從主記憶體中讀取資料到快取行。

5、快取一致性流量

通過前面都知道了快取一致性協議,比如MESI會觸發嗅探器進行資料傳播。當有大量的volatile 和cas 進行資料修改的時候就會產大量嗅探訊息。

三、總結性言論

通過上面一頓巴拉,大家應該對開局圖有一定的瞭解了,也大概知道了匯流排風暴的原因。這裡再做一下概括性的總結(當前內部還有很有詳細的機制,大家感興趣可以擼一波)

在多核處理器架構上,所有的處理器是共用一條匯流排的,都是靠此匯流排來和主記憶體進行資料互動。當主記憶體的資料同時存在於多個處理的快取記憶體中時,某一處理器更新了此共享資料後。會通過匯流排觸發嗅探機制來通知其他處理器將自己快取記憶體內的共享資料置為無效,在下次使用時重新從主記憶體載入最新資料。而這種通過匯流排來進行通訊則稱之為”快取一致性流量“。

因為匯流排是固定的,所有相應可以接受的通訊能力也就是固定的了,如果快取一致性流量突然激增,必然會使匯流排的處理能力受到影響。而恰好CAS和volatile 會導致快取一致性流量增大。如果很多執行緒都共享一個變數,當共享變數進行CAS等資料變更時,就有可能產生匯流排風暴。

img

往期推薦

每日一個知識點系列:volatile的可見性原理

(最新 9000字) Spring Boot 配置特性解析

何時用多執行緒?多執行緒需要加鎖嗎?執行緒數多少最合理?

Spring Boot 知識清單(一)SpringApplication

高併發系統,你需要知道的指標(RT...)

img

相關文章