cpu cache line 原理

參考:
一個講解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) 頻繁更新的存儲和不頻繁更新的存儲分開.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章