slab 學習



cpu cache line 原理

分類: linux memory 6375人閱讀 評論(0) 收藏 舉報
參考:
一個講解Direct Mapped Cache非常深入淺出的文章:

CPU cache


=============================================

總體認識, 
cpu的cache通常較大, 比如 128KB, 被劃分爲多個有固定大小的cache line, cache line通常是32Byte或64Byte.

CPU內部的cache種類, 至少有三種
1) 指令cache
2) 數據cache 通常有多級 multi-level
3) TLB 加速虛擬地址2物理地址轉換


cache entry (cache條目)
包含如下部分
1) cache line : 從主存一次copy的數據大小)
2) tag : 標記cache line對應的主存的地址
3) falg : 標記當前cache line是否invalid, 如果是數據cache, 還有是否dirty


cpu訪問主存的規律
1) cpu從來都不直接訪問主存, 都是通過cache間接訪問主存
2) 每次需要訪問主存時, 遍歷一遍全部cache line, 查找主存的地址是否在某個cache line中.
3) 如果cache中沒有找到, 則分配一個新的cache entry, 把主存的內存copy到cache line中, 再從cache line中讀取.


cache中包含的cache entry條目有限, 所以, 必須有合適的cache淘汰策略
一般使用的是LRU策略.
將一些主存區域標記爲non-cacheble, 可以提高cache命中率, 降低沒用的cache


回寫策略
cache中的數據更新後,需要回寫到主存, 回寫的時機有多種
1) 每次更新都回寫. write-through cache
2) 更新後不回寫,標記爲dirty, 僅當cache entry被evict時纔回寫
3) 更新後, 把cache entry送如回寫隊列, 待隊列收集到多個entry時批量回寫.


cache一致性問題
有兩種情況可能導致cache中的數據過期
1) DMA, 有其他設備直接更新主存的數據
2) SMP, 同一個cache line存在多個CPU各自的cache中. 其中一個CPU對其進行了更新.


cpu stall cpu失速
指的是當cache miss時(特別是read cache miss), cpu在等待數據從內存讀進去cache中期間, 沒事可做.
解決此問題的方法有
1) 超線程技術. CPU在硬件層面, 把一個CPU模擬成兩個CPU, 在上層看來是兩個CPU. 併發的執行兩個線程. 這樣當一個線程因cache miss在等待時, 另一個線程可以執行.


主存的一個地址, 需要被映射進哪個cache line? (術語:Associativity)
根據映射策略的不同而不同


1) 最笨的, 一個地址可被映射進任意cache line (fully associative)
   帶來的問題是, 當尋找一個地址是否已經被cache時, 需要遍歷每一個cache line來尋找, 這個代價不可接受.
   就像停車位可以大家隨便停一樣, 停的時候簡單的, 找車的時候需要一個一個停車位的找了.
   你想下, cpu想知道一個地址是否已經在cache中了, 需要把全部cache line找一邊, 那該有多慢?


2) Direct Mapped Cache  (相當於1-way associative)
   這個就是相當於hash了, 每個地址能被映射到的cache line是固定的. 
   每個人的停車位是固定分配好的. 可以直接找到.
   缺點是, 因爲人多車少, 很可能幾個人爭用同一個車位, 導致cache 淘汰頻繁. 需要頻繁的從主存讀取數據到cache, 這個代價也較高.
   由於cache中cache line的個數都是2的指數個. 那麼, hash算法就很簡單了, 不用取模, 直接把內存地址的某幾個bit位拿出來即可. 比如cache line有128(2^7)個, cache line的大小是32(2^5)字節, 
   那麼一個32位地址的 0~4位作爲cache line內部偏移, 5~11位作爲cache line的索引即可. 剩下的bit12~31作爲當前cache line的tag. tag的作用時, 當有另外一個地址也映射到同一個cache line時, tag用來比較兩個地址是不是同一個地址. 畢竟同一個cache-line可以對應的內存的位置非常多個的.


3) 2-way associative
   是fully associative和Direct Mapped Cache的折中.
   2-way, 每一個人可以有兩個停車位, 這樣當一個停車位被佔了的時候, 還有機會尋找另外一個. 雖然人數衆多, 但同時來找停車位的人並不多. (相當於很多人的車在外面,沒有開回來)
   所以, 2-way associative近似的相當於有了2倍大小的cache, 使用Direct Mapped Cache策略.

cache-hash.jpg
注意, 這圖只統計了cache miss率, 很顯然full-associative是做好的. 但是full-associative導致的判斷一個地址是否在cache中的代價是非常昂貴的.所以, 生產環境一般都是2-way associative
======================================================

多線程變成中避免以及識別錯誤的共享變量方式 主要解決在SMP環境下cache line被頻繁刷新的的問題
Avoiding and Identifying False Sharing Among Threads

舉例:
// 如下代碼在SMP環境下存在cache頻繁刷新問題
double sum=0.0, sum_local[NUM_THREADS];
#pragma omp parallel num_threads(NUM_THREADS)
{
 int me = omp_get_thread_num();
 sum_local[me] = 0.0;

 #pragma omp for
 for (i = 0; i < N; i++)
 sum_local[me] += x[i] * y[i];

 #pragma omp atomic
 sum += sum_local[me];
}
因爲sum_local數組是個全局變量, 多個線程都會訪問, 並且, 各個線程訪問的地方很接近, 會導致一個線程更新, 其他CPU的cache line失效.
smp.jpg

解決該問題的方法是
1) 不同線程之間儘量少的訪問全局變量, 儘量使用線程局部變量.
2) 如果一定要訪問, 儘量讓各個線程自己訪問的區域cacheline對齊.
3) 頻繁更新的存儲和不頻繁更新的存儲分開.


slab 着色的理解

之前對slab着色一直不怎麼理解。
後來查了不少資料,包括之前轉的一篇blog,通過學習之後,總算大致瞭解了。

這兒也寫點總結,按照自己的思路來,以抵抗忘性。

先來看看slab着色的目的。
slab中傾向於把大小相同的對象放在同一個硬件cache line中。爲什麼呢?方便對齊,方便尋址。
但這樣會帶來一個問題。
假如有兩個對象,A,B,它們size一樣,都是18個字節。
這樣,如果交替訪問這兩個對象時,就會造成這兩個對象不停地從cache line中換入/換出到RAM中,而其他的cache line很有可能閒着沒事幹。
怎麼解決這個問題呢?
slab着色就上場了。

slab着色是怎麼回事呢?
其實就是利用slab中的空餘空間去做不同的偏移,這樣就可以根據不同的偏移來區分size相同的對象了。
爲什麼slab中會有剩餘空間?因爲slab是以空間換時間。
做偏移的時候,也要考慮到cache line中的對齊。
假如slab中有14個字節的剩餘空間,cache line以4字節對齊,我們來看看有多少種可能的偏移,也就是有多少種可能的顏色。
第一種,偏移爲0,也就是不偏移,那麼剩餘的14個字節在哪兒呢?放到結尾處,作爲偏移補償。
第二種,偏移4字節,此時偏移補償爲10字節。
第三種,偏移8字節,此時偏移補償爲6字節。
第四種,偏移12字節,此時偏移補償爲2字節。
再繼續,就只能迴歸不偏移了,因爲上一種的偏移補償爲2字節,已經不夠對齊用了。
來總結一下看看有幾種顏色。
第一種無偏移,後面是剩餘空間 free 能滿足多少次對齊 align ,就有多少種,總數: free/align +1 。
如果 free 小於 align ,slab着色就起不到作用,因爲顏色只有一種,即不偏移的情況。
如果size相同的對象很多,但 free 不夠大,或者 free/align 不夠大,效果也不好。
因爲顏色用完了,會從頭再來。
繼續上面的例子,如果有五個相同的對象,第五個對象的顏色與第一個相同,偏移爲0.

原來着色就是這麼回事!
被這個絢麗的名字唬住了好久。

http://blog.csdn.net/njuitjf/article/details/18314807


學習LKD的時候,在內存管理一章的slab小節中,對於slab的着色只是一筆帶過,並沒有詳細敘述,只好翻看了很多資料,稍微有了點兒概念,其實關鍵在於分清所謂的cache(高速緩存,包含多個slab塊)和硬件高速緩存的概念。
        slab的設計原理和主體代碼不難理解,相應的內存管理效率提升原理也不難理解,問題在於slab着色的原理和用途。我們都知道slab中,相同大小的對象傾向於存放在硬件高速緩存內部相同的cache line中,由此產生的問題是,不同slab中,相同大小的對象很可能最終映射到相同的cache line中,當進行鍼對這兩個對象的讀操作時,就出現了兩個對象在cache line和RAM之間來回不停切換的現象,更糟糕的是,剩下的一些cache line可能正在無所事事,着色的主要目的就是避免類似的現象發生。
     由於slab是採用空間換時間的方式提高分配效率,因此在slab塊中會存在沒有用處的字節,我們稱作free,另外,由於要和硬件緩存內部的cache line對齊,還存在一個對齊因子的概念,稱作aln,能夠使用的顏色數據爲free / aln+1,free個無用字節被劃分爲着色域和着色補償域兩部分,分別位於slab塊的頭和尾,着色域後面緊跟着slab描述符+對象描述符,接下來就是每個對象了,對於對象大小相同的不同slab塊,着色域和着色補償域的大小分別爲col x aln和free-col x aln,其中col表示當前slab塊的顏色數,當col x aln=free時,代表顏色數已經分配完,新的對象大小相同的slab生成時,col數目從0開始新一輪的循環。這樣,就使得對象大小相同的不同slab塊中的對象擁有不同的位移量,確保它們不會被映射到硬件緩存內部相同的cache line中。以上是理想情況,當free < aln時,顏色數只有1個,依然無法避免衝突。
     小小總結一下,slab着色功能的高效性是建立在顏色數很多的情況下,但細心的人也會想到,當對象大小相同的slab數目遠遠超過顏色數時,着色仍然避免不了出現衝突。基於這一點考慮,再加上slab的複雜結構和諸如緩存隊列等複雜的層次結構帶來的高內存佔用,Linux內核小組在2.6.23以及之後的內核版本中,採用slub算法替代了slab算法,按照Linux內核小組人員的說法,slub較slab提升了5%~10%的性能並減少了50%的內核緩存佔用,也就是說,不僅僅從時間,而且從空間上都較slab算法有了改善,slub完全兼容slab的接口,內核的其他模塊無需修改代碼就可以從新的內核緩存分配算法中收益。

 


     同一硬件高速緩存行可以映射 RAM 中多個不同的塊,相同大小的對象傾向於存 放在高速緩存內相同的偏移量處。在不同 slab 內具有相同偏移量的對象最終很可能映射到 同一高速緩存行中。而使用 slab 分配器的對象通常是頻繁使用的小對象,高速 緩存的硬件可能因此而花費內存週期在同一高速緩存行與 RAM 內存單元之間來來往往的傳送兩個對象。

 

如下例:假設 cache 行爲 32Bytes , CPU 包含 512 個 cache 行(緩存大小 16K )。

 

     假設對象 A,B 均爲 32B ,且 A 的地址從 0 開始, B 的地址從 16K 開始,則根據組相聯或直接相聯映射方式 (全相聯方式很少使用), A,B 對象很可能映射到 cache 的第 0 行 ,此時,如果 CPU 交替的訪問 A,B 各 50 次,每一次訪問 cache 第 0 行都失效,從而需要從內存傳送數據。而 slab 着色就是爲解決該問題產生的,不同的顏色 代表了不同的起始對象偏移量,對於 B 對象,如果將其位置偏移向右偏移 32B ,則其可能會被映射到 cache 的第 1 行上,這樣交替的訪問 A,B 各 50 次,只需要 2 次內存訪問即可。

     這裏的偏移量就代表了 slab 着色中的一種顏色,不同的顏色代表了不同 的偏移量,儘量使得不同的對象的對應到不同的硬件高速緩存行上,以最大限度的提高效率。實際的情況比上面的例子要複雜得多, slab 的着色還要考慮內存對齊等因素,以及 slab內未用字節的大小,只有當未用字節數足夠 大時,着色才起作用。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章