【重回基礎】理解CPU Cache及緩存一致性MESI

1、前言

原打算從新學習一下 volatile 的實現原理,其中涉及到指令調度重排和數據可見性保證,這二者的理解離不開對 CPU Cache的掌握,所以,先重溫一下CPU Cache,便有了本文。算法

2、爲什麼須要CPU Cache

CPU的發展呈現出摩爾定律(近期愈來愈多的聲音以爲結束了),發展速度迅猛,每18-24個月性能翻番。而內存的發展相較之下顯得十分緩慢,與CPU的性能差距愈來愈大。爲了緩衝二者的速度差,引入了採用SRAM作Cache的三級緩存(L一、L二、L3),以提升CPU的計算效率。固然了,其實內存並不是沒法提速,只是出於成本和容量的平衡。緩存

1965 年,英特爾聯合創始人戈登·摩爾提出以本身名字命名的「摩爾定律」,意指集成電路上可容納的元器件的數量每隔 18 至 24 個月就會增長一倍,性能也將提高一倍。多線程

在這裏插入圖片描述

這就好像我們去超市購物,所購買的東西常常就那麼幾樣,真正購物的時間很短,可是交通耗時、買單排隊耗時一般就已經佔據了大部分的時間。出於成本考慮,一個小區配備一個超市顯然不太可能。因而引入了住宅樓下的自動售賣機、社區的便利店等。如此一來,購物效率天然提升了。ide

3、L一、L2 、L3 Cache 三級緩存結構

三級緩存集成在CPU中,組成以下,每一個CPU核都擁有本身的L1 Cache、L2 Cache,而L3 Cache爲全部核心共享。其中,L1距離Execution Units計算單元距離最近,計算速度一般十分接近;L2 、L3分別次之。另外,L1 Cache 通常分爲 L1d 數據緩存 和 L1i 指令集緩存,用以減小CPU多核心/多線程競搶緩存引發的衝突。性能

讀取數據時,逐級訪問,即執行單元訪問L1,若不存在該數據,則L1訪問L2,L2若一樣沒有則訪問L3,最後L3訪問內存。學習

在這裏插入圖片描述

三級緩存的大小一般不大,以本機i5-8259u爲例:.net

i5-8259u:線程

L1 Data Cache :32.0 KB x 4code

L1 Instruction Cache :32.0 KB x 4cdn

L2 Cache :246 KB x 4

L3 Cache :6.00 MB

L4 Cache :0.00 B

Memory :16.0 GB 2133MHz LPDDR3

4、Cache Line:與內存數據交換的最小單位

一個Cache分爲N個Cache Line, 通常大小爲32byte或64byte,是和內存進行數據交換的最小單位。一個Cache Line 至少有valid、tag、block三個部分,其中block用以存儲數據,tag用於指示內存地址,valid則用於表示該數據的有效性。

在這裏插入圖片描述

CPU內核訪問數據時,發現該數據處於某個Cache Line中,且valid狀態爲有效,則成爲cache hit,不然,成爲cache miss。一般,緩存命中和未命中對於內核的效率影響相差幾百個時鐘。所以,爲了緩存命中率,採用合理有效的緩存數據設置和替換策略對於CPU的計算效率相當重要。就比如社區便利店根據居民的購物習慣,提供高頻消費的商品,而且,根據客戶喜愛的演變進行調整。

CPU的緩存數據設置主要根據空間局部性、時間局部性。

空間局部性:若一個存儲位置的數據被訪問,那麼它附近位置的數據很大可能也會被訪問。

時間局部性:若一個存儲位置的數據被訪問,那麼它在未來的時間很大可能被重複訪問。

而緩存置換策略通常有三種,FIFO 先進先出,LRU 最近最少使用,和 LFU 最不常使用:

FIFO:First In First Out,根據進入緩存時間,淘汰最先的。

LRU:Least Recently Used,對緩存數據進行使用量統計,淘汰最少使用的。該算法使用最多。

LFU:Least Frequently Used,一段時間內,根據使用量,淘汰最少使用的。

(LUR和LFU算法練習:Leetcode-LRULeetcode-LFU

5、MEIS:緩存一致性

引入多級緩存後,結合行之有效的數據設置和替換策略,大大提升了CPU的計算效率,可是同時也帶來了緩存一致性問題。

5.1 底層操做

爲了保證 Cache 一致性,CPU 底層提供了兩種操做:Write invalidate 和 Write update。

Write invalidate 操做指:當一個內核修改了一份數據,其它內核若是有這份數據,就把 valid 標識爲無效。

Write update 操做指:當一個內核修改了一份數據,其它內核若是有這份數據,就都更新爲新值。

Write invalidate 操做實現起來更爲簡單,加上其它內核後續並不須要使用到改數據。缺點在於一個valid標識對應個Cache Line,如此一來,其它本來有效的數據也被設置爲無效。Write update 操做會產生大量的更新操做,不過只須要更新修改的數據,而非一個Cache Line。大多數處理器採用的操做都是 Write invalide。

這兩個操做也是咱們碼農經常使用的緩存操做,修改緩存數據後,將緩存置爲無效,或者直接更新。固然,除此以外,對於實時性要求不高的緩存數據,咱們還常常採用按期時間,自動過時的策略。

5.2 MESI 協議

Write invalidate 提供了緩存一致性的簡單解決思路,具體的實施還須要一套完整的協議,其中比較經典,常做爲教材的就是MESI協議,後續許多協議都是基於MESI進行擴展。

與前文講到的 Cache Line 普通結構不一樣的是,MESI 協議中,Cache Line 用頭兩個 bit 來表示 MESI 的四個狀態:

在這裏插入圖片描述

這四個狀態分別爲:

M(Modified):數據被修改了,屬於有效狀態,可是數據只處於本Cache,和內存不一致。

E(Exclusive):數據獨佔,屬於有效狀態,數據僅在本Cache,和內存一致。

S(Shared):數據非獨佔,屬於有效狀態,數據存於多個Cache,和內存一致。

I(Invalid):數據無效。

下面用畫圖示意四個狀態,便於理解,圖片取自《大話處理器》:

在這裏插入圖片描述

須要注意的是:當狀態爲E/S時,數據才與緩存一致。而修改某內核Cache的數據後,並不會當即寫回內存,而是將該Cache Line標示爲M,其它內核的該份數據表示爲I,此時的數據是不一致的。

下面爲四個狀態的轉化示意圖:

在這裏插入圖片描述

從狀態轉化圖中,能夠注意到的是:不管當前Cache Line處於什麼狀態,對於兩個修改操做——Local Write 將本Cache Line 狀態變動爲 Modified,並將其它Cache Line統一設置爲 Invalid(若其它核處於S),等待觸發寫回內存;而 Remote Write 則將全部存有該份數據的 Cache Line 狀態統一變動爲 Invalid 失效,至關於從新構建該數據的緩存。

下面分別對四種狀態的轉化進行具體說明:

當狀態爲 Invalid 時:

當前狀態 事件 下個狀態 說明
Invalid Local Read Exclusive 在其它Cache中找不到該數據
Invalid Local Write Shared (1)若存有該數據的Cache Line處於M,則將該數據更新至內存;(2)若存有該數據的Cache Line處於E,則讀取數據,並將該兩個Cache Line都設置爲S;(3)若存有該數據的Cache Line處於S,則讀取該數據,將本Cache Line設置爲S
Invalid Remote Read Invalid 其它核不讀該數據
Invalid Remote Write Invalid 其它核不寫該數據

當狀態爲 Exclusive 時:

當前狀態 事件 下個狀態 說明
Exclusive Local Read Exclusive 讀本身獨佔的數據,狀態天然不變
Exclusive Local Write Modified 設置爲M,由於與內存數據不一致,等觸發回寫
Exclusive Remote Read Shared 其它核不讀該數據
Exclusive Remote Write Invalid 其它核不寫該數據

當狀態爲 Shared 時:

當前狀態 事件 下個狀態 說明
Shared Local Read Shared 讀共享數據,無變動數據,狀態天然不變
Shared Local Write Modified 設置爲M,由於與內存數據不一致,等觸發回寫
Shared Remote Read Shared 讀共享數據,無變動數據,狀態天然不變
Shared Remote Write Invalid 其它核修改不屬於本身的Cache,採用統一失效策略

當狀態爲 Modified 時:

當前狀態 事件 下個狀態 說明
Modified Local Read Modified 讀本身獨有的數據,狀態天然不變
Modified Local Write Modified 寫本身獨有的數據,狀態一樣不變
Modified Remote Read Shared 讀共享數據,無變動數據,狀態天然不變
Modified Remote Write Invalid 其它核修改不屬於本身的Cache,採用統一失效策略

參考

  1. 《大話處理器》Cache一致性協議之MESI —— 木兮清揚
相關文章
相關標籤/搜索