CPU體系結構之cache小結html
1.What is cache?程序員
CPU緩存(Cache Memory)位於CPU與內存之間的臨時存儲器,它的容量比內存小但交換速度快。在緩存中的數據是內存中的一小部分,但這一小部分是短期內CPU即將訪問的,當CPU調用大量數據時,就可避開內存直接從緩存中調用,從而加快讀取速度。
在CPU中加入緩存是一種高效的解決方案,這樣整個內存儲器(緩存+內存)就變成了既有緩存的高速度,又有內存的大容量的存儲系統了。緩存對CPU的性能影響很大,主要是由於CPU的數據交換順序和CPU與緩存間的帶寬引發的。
下圖是一個典型的存儲器層次結構,咱們能夠看到一共使用了三級緩存算法
2. Why should I care about cache?
從延遲上看,作一次乘法通常只要三個週期,而作一次CPU的內存訪問須要167個cycle,若是須要提高程序性能,減小CPU的memory訪問相當重要。所以,須要採用容量小可是更快的存儲器(cache)。shell
3.爲何要有多級CPU Cache
隨着科技發展,熱點數據的體積愈來愈大,單純的增長一級緩存大小的性價比已經很低了
二級緩存就是一級緩存的緩衝器:一級緩存制形成本很高所以它的容量有限,二級緩存的做用就是存儲那些CPU處理時須要用到、一級緩存又沒法存儲的數據。
一樣道理,三級緩存和內存能夠看做是二級緩存的緩衝器,它們的容量遞增,但單位制形成本卻遞減。
另外須要注意的是,L3 Cache和L1,L2 Cache有着本質的區別。,L1和L2 Cache都是每一個CPU core獨立擁有一個,而L3 Cache是幾個Cores共享的,能夠認爲是一個更小可是更快的內存。
使用dmidecode命令查看cache size後端
4.cpu與cache 內存交互的過程
CPU接收到指令後,它會最早向CPU中的一級緩存(L1 Cache)去尋找相關的數據,然一級緩存是與CPU同頻運行的,可是因爲容量較小,因此不可能每次都命中。這時CPU會繼續向下一級的二級緩存(L2 Cache)尋找,一樣的道理,當所須要的數據在二級緩存中也沒有的話,會繼續轉向L3 Cache、內存(主存)和硬盤.
程序運行時可使用perf工具觀察cache-miss的rate.數組
5.什麼是cache line
Cache Line能夠簡單的理解爲CPU Cache中的最小緩存單位。
內存和高速緩存之間或高速緩存之間的數據移動不是以單個字節或甚至word完成的。
相反,移動的最小數據單位稱爲緩存行,有時稱爲緩存塊
目前主流的CPU Cache的Cache Line大小都是64Bytes。假設咱們有一個512字節的一級緩存,那麼按照64B的緩存單位大小來算,這個一級緩存所能存放的緩存個數就是512/64 = 8個。
查看cache line大小
cat /sys/devices/system/cpu/cpu1/cache/index0/coherency_line_size
cache line的影響:緩存
for (int i = 0; i < N; i+=k) arr[i] *= 3;
注意當步長在1到16範圍內,循環運行時間幾乎不變。但從16開始,每次步長加倍,運行時間減半。
因爲16個整型數佔用64字節(一個緩存行),for循環步長在1到16之間一定接觸到相同數目的緩存行:即數組中全部的緩存行。當步長爲32,咱們只有大約每兩個緩存行接觸一次,當步長爲64,只有每四個接觸一次。ide
6. cache寫機制
Cache寫機制分爲write through和write back兩種。
Write-through- Write is done synchronously both to the cache and to the backing store.
Write-back (or Write-behind) - Writing is done only to the cache. A modified cache block is written back to the store, just before it is replaced.
Write-through(直寫模式)在數據更新時,同時寫入緩存Cache和後端存儲。此模式的優勢是操做簡單;缺點是由於數據修改須要同時寫入存儲,數據寫入速度較慢。
Write-back(回寫模式)在數據更新時只寫入緩存Cache。只在數據被替換出緩存時,被修改的緩存數據纔會被寫到後端存儲。此模式的優勢是數據寫入速度快,由於不須要寫存儲;缺點是一旦更新後的數據未被寫入存儲時出現系統掉電的狀況,數據將沒法找回。函數
7.cache 一致性
多個處理器對某個內存塊同時讀寫,會引發衝突的問題,這也被稱爲Cache一致性問題。
Cache一致性問題出現的緣由是在一個多處理器系統中,多個處理器核心都可以獨立地執行計算機指令,從而有可能同時對某個內存塊進行讀寫操做,而且因爲咱們以前提到的回寫和直寫的Cache策略,致使一個內存塊同時可能有多個備份,有的已經寫回到內存中,有的在不一樣的處理器核心的一級、二級Cache中。因爲Cache緩存的緣由,咱們不知道數據寫入的時序性,於是也不知道哪一個備份是最新的。還有另一個一種可能,假設有兩個線程A和B共享一個變量,當線程A處理完一個數據以後,經過這個變量通知線程B,而後線程B對這個數據接着進行處理,若是兩個線程運行在不一樣的處理器核心上,那麼運行線程B的處理器就會不停地檢查這個變量,而這個變量存儲在本地的Cache中,所以就會發現這個值總也不會發生變化。
爲了正確性,一旦一個核心更新了內存中的內容,硬件就必需要保證其餘的核心可以讀到更新後的數據。目前大多數硬件採用的策略或協議是MESI或基於MESI的變種:
M表明更改(modified),表示緩存中的數據已經更改,在將來的某個時刻將會寫入內存;
E表明排除(exclusive),表示緩存的數據只被當前的核心所緩存;
S表明共享(shared),表示緩存的數據還被其餘核心緩存;
I表明無效(invalid),表示緩存中的數據已經失效,即其餘核心更改了數據。
8.cache的局部性
程序在一段時間內訪問的數據一般具備局部性,好比對一維數組來講,訪問了地址x上的元素,那麼之後訪問地址x+一、x+2上元素的可能性就比較高;如今訪問的數據,在不久以後再次被訪問的可能性也比較高。局部性分爲「時間局部性」和「空間局部性」,時間局部性是指當前被訪問的數據隨後有可能訪問到;空間局部性是指當前訪問地址附近的地址可能隨後被訪問。處理器經過在內存和核心之間增長緩存以利用局部性加強程序性能,這樣能夠用遠低於緩存的價格換取接近緩存的速度。
時間局部性:
代碼1:
工具
for (loop=0; loop<10; loop++) { for (i=0; i<N; i++) { ... = ... x[i] ... } }
代碼2:
for (i=0; i<N; i++) { for (loop=0; loop<10; loop++) { ... = ... x[i] ... } }
代碼二的性能優於代碼1,x的元素如今被重複使用,所以更有可能留在緩存中。 這個
從新排列的代碼在使用x[i]時顯示更好的時間局部性。
空間局部性:
一個矩陣乘法的例子:
代碼1:
for i=1..n for j=1..n for k=1..n c[i,j] += a[i,k]*b[k,j]
代碼2:
for i=1..n for k=1..n for j=1..n c[i,j] += a[i,k]*b[k,j]
代碼2的性能優於代碼一的性能。
二者實現上的差別:
代碼2的b[k,j]是按行訪問的,因此存在良好的空間局部性,cache line被充分利用。
代碼1中,b [k,j]由列訪問。 因爲行的存儲矩陣,所以對於每一個緩存行加載,只有一個元素用於遍歷。
9.cache替換策略
Cache工做原理要求它儘可能保存最新數據,當從主存向Cache傳送一個新塊,而Cache中可用位置已被佔滿時,就會產生Cache替換的問題。
經常使用的替換算法有下面三種。
(1) LFU
LFU(Least Frequently Used,最不常用)算法將一段時間內被訪問次數最少的那個塊替換出去。每塊設置一個計數器,從0開始計數,每訪問一次,被訪塊的計數器就增1。當須要替換時,將計數值最小的塊換出,同時將全部塊的計數器都清零。
這種算法將計數週期限定在對這些特定塊兩次替換之間的間隔時間內,不能嚴格反映近期訪問狀況,新調入的塊很容易被替換出去。
(2)LRU
LRU(Least Recently Used,近期最少使用)算法是把CPU近期最少使用的塊替換出去。這種替換方法須要隨時記錄Cache中各塊的使用狀況,以便肯定哪一個塊是近期最少使用的塊。每塊也設置一個計數器,Cache每命中一次,命中塊計數器清零,其餘各塊計數器增1。當須要替換時,將計數值最大的塊換出。
LRU算法相對合理,但實現起來比較複雜,系統開銷較大。這種算法保護了剛調入Cache的新數據塊,具備較高的命中率。LRU算法不能確定調出去的塊近期不會再被使用,因此這種替換算法不能算做最合理、最優秀的算法。可是研究代表,採用這種算法可以使Cache的命中率達到90%左右。
(3) 隨機替換
最簡單的替換算法是隨機替換。隨機替換算法徹底無論Cache的狀況,簡單地根據一個隨機數選擇一塊替換出去。隨機替換算法在硬件上容易實現,且速度也比前兩種算法快。缺點則是下降了命中率和Cache工做效率。
10.cache的映射
主存與cache的地址映射方式有全相聯方式、直接方式和組相聯方式三種。
直接映射
將一個主存塊存儲到惟一的一個Cache行。
1) 多對一的映射關係,但一個主存塊只能拷貝到cache的一個特定行位置上去。
cache的行號i和主存的塊號j有以下函數關係:i=j mod m(m爲cache中的總行數)
優勢:硬件簡單,容易實現
缺點:命中率低, Cache的存儲空間利用率低
2) 全相聯映射
能夠將一個主存塊存儲到任意一個Cache行。
主存的一個塊直接拷貝到cache中的任意一行上
優勢:命中率較高,Cache的存儲空間利用率高
缺點:線路複雜,成本高,速度低
組相聯映射
能夠將一個主存塊存儲到惟一的一個Cache組中任意一個行。
將cache分紅u組,每組v行,主存塊存放到哪一個組是固定的,至於存到該組哪一行是靈活的,即有以下函數關係:cache總行數m=u×v 組號q=j mod u
組間採用直接映射,組內爲全相聯
硬件較簡單,速度較快,命中率較高
Cache Miss
1.不要指望編譯器對你作任何優化
2.Cache(廣義內存)系統表明性的包括三種級別:
(1)第一級cache (L1)位於CPU芯片上而且運算於CPU工做頻率;
(2)第二級cache(L2)也位於芯片上比L1速度慢而體積大;
(3)第三級cache(L3)位於CPU外部,是速度最慢、體積最大的存儲器。
3.當運算器須要從存儲器中提取數據時,它首先在最高級的cache中尋找而後在次高級的cache中尋找。若是在cache中找到,則稱爲命中hit;反之,則稱爲不命中miss。
4.cache misses的種類:
(1)cold misses:不可避免。若K級cache空,則必發生cache miss,空的cache稱爲cold cache,這種cache misses稱爲compulsory misses或者cold misses。當cache已被warmed up則通常不會再發生cold misses。
(2)conflict misses:多個K+1級的blocks被映射到K級中同一個block。這一點關係到對於程序員而言可否寫出cache友好代碼。
(3)程序常會分階段執行(例如循環:內層、外層),每一個階段會取cache blocks的固定幾個塊,這幾個塊所構成的集合稱爲working set。 當working set超過cache大小時所發生的miss稱爲capacity misses。
5.從cache指令上作優化:簡化調用關係,減小冗餘代碼(即不是必須存在的的代碼),減少代碼量,減小沒必要要的調用;
6.從數據cache上作優化:即減小cache miss的次數,方法有很多,http://blog.chinaunix.net/uid-7319742-id-2059720.html 這篇文章有介紹
推薦連接:
http://bi.dataguru.cn/thread-163962-1-1.html
http://blog.chinaunix.net/uid-7319742-id-2059720.html
http://blog.csdn.net/wangjiaoyu250/article/details/9212863
http://coolshell.cn/articles/10249.html
https://blog.csdn.net/yhb1047818384/article/details/79604976