1. 程式人生 > >多線程之 Cache Line 與偽共享

多線程之 Cache Line 與偽共享

多級 概率 tps 語言 本質 redis log spa 好的

Cache 簡介 Cache,即緩存。緩存能提升讀取性能,其原理是用性能更好的存儲介質存儲一部分高頻訪問的內容,獲得總體概率上的速度提升。 在開發中,我們口中的緩存可以是一個變量,或者是 redis。在計算機 CPU 內部,往往指的是 CPU 的各級 cache。 Cache 的一致性 由於是高頻訪問的內容被重復存儲到了好幾處地方,必然要考慮一致性。你需要及時清除或者更新緩存中過期內容。在程序設計中,使用緩存的架構通常是給定一個過期時間。而對於 CPU Cache,情況就復雜很多。 CPU Cache 原理 緩存的工作原理是當 CPU 要讀取一個數據時,首先從CPU緩存中查找,找到就立即讀取並送給 CPU 處理;沒有找到,就從速率相對較慢的內存中讀取並送給 CPU 處理,同時把這個數據所在的數據塊調入緩存中,可以使得以後對整塊數據的讀取都從緩存中進行,不必再調用內存。正是這樣的讀取機制使 CPU 讀取緩存的命中率非常高(大多數 CPU 可達 90% 左右),也就是說 CPU 下一次要讀取的數據 90% 都在 CPU 緩存中,只有大約 10% 需要從內存讀取。這大大節省了 CPU 直接讀取內存的時間,也使 CPU 讀取數據時基本無需等待。總的來說,CPU 讀取數據的順序是先緩存後內存。(摘自百科)
將模型簡化以後,如果 CPU 想訪問內存裏的內容: CPU Core1 --> L1 Cache --> L2 Cache --> L3 Cache --> RAM CPU Core2 --> L1 Cache --> L2 Cache --> L3 Cache --> RAM 需要說明的是,每個 CPU 核心都有自己的獨立的多級緩存,常見的有三級。訪問速度上,L1 > L2 > L3, 容量通常與速度成反比。通俗點說,你在某處聲明的變量 int foo = 1;在有緩存情況下,CPU 是從 L1~L3 中獲取 foo 的值,多級緩存無命中才去內存中取。
值得一提的是,現如今 Intel 比較新的 CPU 型號,其緩存不再是彼此獨立的設計了,雙核會共享二級緩存,即“Smart cache” 共享緩存技術。 CPU Cache 也需要考慮一致性問題,在變量被賦值後, Cache 中的數據就臟了,會被清除。 在多線程環境下,不同 Core 中的 Cache 臟數據會更頻繁產生,擦除臟數據的成本開銷就會顯得很大。 另外值得一提的是,多線程不加鎖有可能造成 Cache 臟數據不被及時擦除。 Cache Line Cache Line 是 Cache 的最小單位,通常是 64 bytes。如果 L1 緩存是 6400 bytes, 那他可以分成 100 個 Cache Line。在 C 語言中,你能感知到的內存最小單位應該是變量, int,long long 等,他們通常只有 4 字節或者 8 字節。CPU 的緩存為了性能,一般是以 Cache Line 為單位進行一口氣緩存一大塊內存。一個 Cache Line 中就會緩存很多個變量的值。如果 Cache Line 有了臟數據,也是以它為單位整塊更新。

MESI(Modified Exclusive Shared Or Invalid)

摘自 https://www.cnblogs.com/shangxiaofei/p/5688296.html
(也稱為伊利諾斯協議,是因為該協議由伊利諾斯州立大學提出)是一種廣泛使用的支持寫回策略的緩存一致性協議,該協議被應用在Intel奔騰系列的CPU中。

MESI協議中的狀態

CPU中每個緩存行(caceh line)使用4種狀態進行標記(使用額外的兩位(bit)表示):

M: 被修改(Modified)

該緩存行只被緩存在該CPU的緩存中,並且是被修改過的(dirty),即與主存中的數據不一致,該緩存行中的內存需要在未來的某個時間點(允許其它CPU讀取請主存中相應內存之前)寫回(write back)主存。

當被寫回主存之後,該緩存行的狀態會變成獨享(exclusive)狀態。

E: 獨享的(Exclusive)

該緩存行只被緩存在該CPU的緩存中,它是未被修改過的(clean),與主存中數據一致。該狀態可以在任何時刻當有其它CPU讀取該內存時變成共享狀態(shared)。

同樣地,當CPU修改該緩存行中內容時,該狀態可以變成Modified狀態。

S: 共享的(Shared)

該狀態意味著該緩存行可能被多個CPU緩存,並且各個緩存中的數據與主存數據一致(clean),當有一個CPU修改該緩存行中,

其它CPU中該緩存行可以被作廢(變成無效狀態(Invalid))。

I: 無效的(Invalid)

該緩存是無效的(可能有其它CPU修改了該緩存行)。

False Sharing 偽共享 偽共享即 MESI 中不健康的 Shared 狀態。考慮這樣一個場景。 struct { int thread1_data; int thread2_data; }; 同時有兩個線程(thread1 和 thread2)只去讀寫屬於他自己的那個變量。看似各玩各的互不影響,實際上由於兩個變量挨得很近,往往會被放到一個 Cache Line 中。 thread1 對 thread1_data 的讀寫,會造成 core2 核上對 thread2_data 的緩存被標記為無效 Invalid。我們知道清理 Invalid 狀態是很費時的,如果過高頻繁地觸發,會造成性能下降。 在多線程讀寫數組上,尤其要註意這個偽共享問題。 偽共享的本質是,高等語言的概念上,看似變量間是獨立的,但是在 CPU Cache 層面, 兩個變量地址挨得太近(在一個 Cache Line 範圍中)就只能作為整體來看。

多線程之 Cache Line 與偽共享