[toc]數據庫
緩存又叫高速緩存,是計算機存儲器中的一種,本質上和硬盤是同樣的,都是用來<font color=#0000FF size=3>存儲數據和指令的 </font>。它們最大的區別在於<font color=#0000FF size=3>讀取速度的不一樣。</font>程序通常是放在內存中的,當CPU執行程序的時候,執行完一條指令須要從內存中讀取下一條指令,讀取內存中的指令要花費100000個時鐘週期(緩存讀取速度爲200個時鐘週期,相差500倍),若是每次都從內存中取指令,CPU運行時將花費大量的時間在讀取指令上。這顯然是一種資源浪費。編程
如何解決這個問題呢?有人確定會問,<font color=#0000FF size=3>直接把程序存儲在緩存中不行嗎? </font>數組
答案是能夠的。可是,緩存的造價太貴了。具體以下圖所示。以2015年的售價爲例,1GB SRAM的價格大約爲327680美圓,而1GB 普通硬盤的價格僅僅爲0.03美圓。用緩存來存儲程序成本過高了,得不償失。瀏覽器
因而,有人就提出了這樣一種方法,<font color=#0000FF size=3>在CPU和內存之間添加一個高速內存, </font>這個高速內存容量小,只用來存儲CPU執行時經常使用的指令。既保證了硬件成本,又提升了CPU的訪問速度。這個高速內存就是緩存(高速緩存)。緩存
高速緩存是一個小而快速的<font color=#0000FF size=3>存儲設備 </font>,它做爲存儲在更大更慢的設 備中的數據對象的緩衝區域。<font color=#0000FF size=3>使用高速緩存的過程稱爲緩存 </font>。服務器
具體以下圖所示,主存能夠做爲一個存儲設備,L3是主存的緩衝區域,從L3存取數據的過程就叫作緩存。網絡
以下圖所示,數據老是以<font color=#0000FF size=3>塊爲單位 </font>在高速緩存和主存之間來回複製。性能
若是咱們的程序請求一個數據字,這個數據字存儲在編號爲10的塊中。將分如下幾種狀況考慮:spa
1. 高速緩存行中爲空,這叫作<font color=#0000FF size=3>冷不命中 </font>。翻譯
2.高速緩存中有數據塊,但沒有數據塊10,這叫作<font color=#0000FF size=3>緩存不命中 </font>。接下來緩存請求主存將該塊複製到高速緩存,高速緩存接收到以後將替換一個現有的數據塊,從而存儲新的數據塊在高速緩存中。最後,高速緩存將數據塊10返回給CPU。
3. 高速緩存中有數據,將內存中的數據塊放置到高速緩存中時,發生了衝突,這叫作<font color=#0000FF size=3>衝突不命中 </font>。
放置策略中最經常使用的是:第k+1層的塊i必須放在第k層的塊(i mod 4)中。好比,第k+1層的0,4,8,12會映射到第k層的塊0。塊1,5,9,13會映射到塊1。
4. 緩存中有數據塊10,則直接返回給CPU。這叫作<font color=#0000FF size=3>緩存命中 </font>。
高速緩存徹底由硬件管理,硬件邏輯必需要知道,如何查找緩存中的塊,並肯定是否包含特定塊。所以,必須以很是嚴格且簡單的方式去構建高速緩存。在計算機中,高速緩存模型以下圖所示。
咱們能夠將高速緩存存儲器視爲有$S = {2^s}$個高速緩存組的<font color=#0000FF size=3>數組 </font>。每一個組包含$E = {2^e}$個<font color=#0000FF size=3>高速緩存行 </font>。每一個行是由一個$B = {2^b}$字節的數據塊組成的。
通常而言,高速緩存的結構能夠用元組(S,E,B,m)來描述。高速緩存的大小(或容量)C指的是全部塊的大小的和。<font color=#0000FF size=3>標記位和有效位不包括在內 </font>。所以,C=S×E×B。
每一個高速緩存存儲器有m位,能夠組成$M = {2^m}$個不一樣的地址,$m = t + s + b$。每一個數據塊由如下三部分構成。
有效位:有效位爲t位,t通常爲1,指明這個行是否包含有效信息。
標記位:標記位爲s位。惟一的標識了存儲在高速緩存中的塊(數組索引)。
塊偏移:數據塊爲$B = {2^b}$字節。指明CPU請求的內容在數據塊中的偏移。
下面對以上內容出現的參數作個總結:
參數 | 描述 |
---|---|
$S = {2^s}$ | 組數 |
$E$ | 每一個組的行數 |
$B = {2^b}$ | 塊大小(字節) |
$m = {\log _2}(M)$ | 物理地址位數 |
$M = {2^m}$ | 內存地址的最大數量 |
$s = {\log _2}(S)$ | 組索引位數量 |
$b = {\log _2}(B)$ | 塊偏移位數量 |
$t = m - (s + b)$ | 標記位數量 |
$C = B \times E \times S$ | 不包括像有效位和標記位這樣開銷的高速緩存大小(字節) |
下表爲現代計算機中用到的各類緩存。
類型 | 緩存什麼 | 被緩存在何處 | 延遲(週期數) | 由誰管理 |
---|---|---|---|---|
CPU寄存器 | 4字節或8字節 | 芯片上的CPU寄存器 | 0 | 編譯器 |
TLB | 地址翻譯 | 芯片上的TLB | 0 | 硬件MMU |
L1高速緩存 | 64字節塊 | 芯片上的L1高速緩存 | 4 | 硬件 |
L2高速緩存 | 64字節塊 | 芯片上的L2高速緩存 | 10 | 硬件 |
L3高速緩存 | 64字節塊 | 芯片上的L3高速緩存 | 50 | 硬件 |
虛擬內存 | 4KB頁 | 主存 | 200 | 硬件 |
緩衝區緩存 | 部分文件 | 主存 | 200 | OS |
磁盤緩存 | 磁盤扇區 | 磁盤控制器 | 100000 | 控制器固件 |
網絡緩存 | 部分文件 | 本地磁盤 | 10000000 | NFS客戶 |
瀏覽器緩存 | Web頁 | 本地磁盤 | 10000000 | Web瀏覽器 |
Web緩存 | Web頁 | 遠程服務器磁盤 | 1000000000 | Web代理服務器 |
當一條加載指令指示CPU從主存地址A中讀取一個字w時,會將該主存地址A發送到高速緩存中,則高速緩存會根據如下步驟判斷地址A是否命中:
組選擇:根據地址劃分,將中間的s位表示爲無符號數做爲<font color=#0000FF size=3>組的索引 </font>,可獲得該地址對應的組。
行匹配:根據地址劃分,可獲得t位的標誌位,因爲組內的任意一行均可以包含任意映射到該組的數據塊,因此就要線性搜索組中的每一行,<font color=#0000FF size=3>判斷是否有和標誌位匹配且設置了有效位的行 </font>,若是存在,則緩存命中,不然緩衝不命中。
字抽取:若是找到了對應的高速緩存行,則能夠將b位表示爲無符號數做爲<font color=#0000FF size=3>塊偏移量 </font>,獲得對應位置的字。
當高速緩存命中時,會很快抽取出字w,並將其返回給CPU。若是緩存不命中,CPU會進行等待,高速緩存會向主存請求包含字w的數據塊,當請求的塊從主存到達時,高速緩存會將這個塊保存到它的一個高速緩存行中,而後從被存儲的塊中抽取出字w,將其返回給CPU。
上面咱們介紹了計算機中的高速緩存模型,咱們能夠根據每一個組的高速緩存行數E,將高速緩存分紅不一樣的類型。下面咱們看下直接映射高速緩存(E=1)的具體例子。
組選擇示意圖以下所示。假設有 S 組,每組由一行組成,緩存塊爲8字節。CPU發出地址要取數據字,高速緩存將該地址分解爲三部分,對於圖中的地址來講,<font color=#FF4500 size=3>塊偏移量爲4。組索引是 1 ,粉紅色的爲t位標記位。 </font>所以,高速緩存提取的組索引爲 1,即圖中第二行。
而後,檢查地址中的標記位與緩存行中的標記位是否匹配。若是匹配,將進行下一步字選擇。若是不匹配,則表示未命中。在未命中時,<font color=#0000FF size=3>高速緩存必須從內存中從新取數據塊, </font>在行中覆蓋此塊。
當標記位匹配時,表示命中,接着檢查地址中的塊偏移爲4,即要從緩存行數據塊的第5位開始取值,並返回給CPU。
下面,咱們模擬下直接映射高速緩存的過程,以便加深理解高速緩存是如何工做的。假設,內存地址爲<font color=#FF4500 size=3>4字節,S=4組,E=1行/組,B=2字節/塊。 </font>其結構圖以下所示。
咱們模擬CPU要從高速緩存中讀取地址爲0,1,7,8,0的數據。下面是具體的過程。
地址 | 二進制 | 是否命中 |
---|---|---|
0 | [${0000_2}$](t=0,s=00,b=0) | |
1 | [${0001_2}$](t=0,s=00,b=1) | |
7 | [${0111_2}$](t=0,s=11,b=1) | |
8 | [${1000_2}$](t=1,s=00,b=0) | |
0 | [${0000_2}$](t=00,s=0,b=0) |
1. 讀地址0的數據。<font color=#FF4500 size=3>標記位爲0,索引位爲00,偏移位爲0,塊號爲0。</font>緩存行中沒有數據,組0的有效位爲0,地址的標記位和組0的標記位不匹配,所以,未命中。而後,高速緩存從內存中取出塊0,塊1, 共2字節,並存儲在組0中。具體以下圖所示。
2. 讀地址1的數據。<font color=#FF4500 size=3>標記位爲0,索引位爲00,偏移位爲1,塊號1。 </font>緩存行中已有數據數據,組0的有效位爲1,地址1的標記位和組0的標記位匹配,所以,命中。具體以下圖所示。
3. 讀地址7的數據。<font color=#FF4500 size=3>標記位爲0,索引位爲11(3),偏移位爲1,塊號爲3。 </font>緩存行中有數據,組3的有效位爲0,地址的標記位和組0的標記位不匹配,所以,未命中。而後,高速緩存從內存中取出塊6,塊7, 共2字節,並存儲在組3中。具體以下圖所示。
4. 讀地址8的數據。<font color=#FF4500 size=3>標記位爲1,索引位爲00,偏移位爲0,塊號爲4。 </font>緩存行中有數據,組0的有效位爲1,地址的標記位和組0的標記位不匹配,所以,未命中。而後,高速緩存從內存中取出塊8,塊9, 共2字節,並存儲在組0中。具體以下圖所示。
5. 讀地址0的數據。<font color=#FF4500 size=3>標記位爲0,索引位爲00,偏移位爲0,塊號爲0。</font>緩存行中有數據,組0的有效位爲1,地址的標記位和組0的標記位不匹配,所以,未命中。而後,高速緩存從內存中取出塊0,塊1, 共2字節,並存儲在組0中。具體以下圖所示。
最終結果以下:緩存命中率爲20%。
地址 | 二進制 | 是否命中 |
---|---|---|
0 | [${0000_2}$](t=0,s=00,b=0) | 否 |
1 | [${0001_2}$](t=0,s=00,b=1) | 是 |
7 | [${0111_2}$](t=0,s=11,b=1) | 否 |
8 | [${1000_2}$](t=1,s=00,b=0) | 否 |
0 | [${0000_2}$](t=00,s=0,b=0) | 否 |
注意:塊大小爲2字節,因此從內存中取數據老是以偶數倍開始的,因此會看到M[8-9],而不是M[7-8]。
若是你看懂了上述高速緩存的整個過程,考慮下<font color=red size=3>如何編程來模擬高速緩存呢? </font>後面的文章我會詳細講解如何用C語言模擬高速緩存,歡迎關注個人公衆號【嵌入式與Linux那些事】,第一時間獲取更新。
觀察以上過程其實能夠發現,在第5步,讀地址0的數據的時候,咱們又得<font color=#0000FF size=3>從新從內存中取數據到緩存行中。 </font>在讀地址8的數據的時候,M[8-9]替換了緩存行中的M[0-1]。
<font color=#0000FF size=3>最主要的緣由是每個組中只容許存放一行緩存。 </font>假設,E = 2,每組中有2個緩存行,M[8-9]和M[0-1]就有很大可能同時存在於組0中。咱們在第5步訪問時,就不須要從新從內存中取數據了。所以,就有了E = 2的兩路相聯高速緩存。
直接映射高速緩存中衝突不命中形成的問題源於每一個組只有一行這個限制。組相聯高速存放鬆了這條限制,因此每一個組都保存有多於一個的高速緩存行。以下圖所示爲兩路相聯的高速緩存。
它的組選擇與直接映射高速緩存的組選擇同樣,組索引位標識組。具體以下圖所示,這裏再也不贅述。
組相聯高速緩存中的行匹配比直接映射高速緩存中的更復雜,由於它必須每次檢查<font color=#0000FF size=3>多個行 </font>的標記位和有效位,以肯定所請求的字是否在集合中。具體以下圖所示。
字選擇的過程和直接映射高速緩存中的方式同樣,這裏就再也不贅述。
下面,咱們模擬下兩路相聯高速緩存的過程,以便加深理解高速緩存是如何工做的。假設,內存地址爲4字節,S=2組,E=2行/組,B=2字節/塊。其結構圖以下所示。
咱們模擬CPU要從高速緩存中讀取地址爲0,1,7,8,0的數據。下面是具體的過程。
地址 | 二進制 | 是否命中 |
---|---|---|
0 | [${0000_2}$] (t=00,s=0,b=0) | |
1 | [${0001_2}$](t=00,s=0,b=1) | |
7 | [${0111_2}$](t=01,s=1,b=1) | |
8 | [${1000_2}$](t=10,s=0,b=0) | |
0 | [${0000_2}$](t=00,s=0,b=0) |
1. 讀地址0的數據。<font color=#FF4500 size=3>標記位爲00,索引位爲0,偏移位爲0,塊號爲0。</font>緩存行中沒有數據,組0的有效位爲0,地址的標記位和組0的第一行和第二行的標記位都不匹配,所以,未命中。而後,高速緩存從內存中取出塊0,塊1, 共2字節,並存儲在組0第一行中。具體以下圖所示。
2. 讀地址1的數據。<font color=#FF4500 size=3>標記位爲00,索引位爲0,偏移位爲1,塊號爲1。</font>緩存行中已有數據數據,組0的第一行有效位爲1,地址1的標記位和組0的第一行標記位匹配,所以,命中。具體以下圖所示。
3. 讀地址7的數據。<font color=#FF4500 size=3>標記位爲01,索引位爲1,偏移位爲1,塊號爲1。</font>緩存行中有數據,組1的有效位爲0,地址的標記位和組1中的第一行和第二行的標記位不匹配,所以,未命中。而後,高速緩存從內存中取出塊6,塊7, 共2字節,並存儲在組1中。具體以下圖所示。
4. 讀地址8的數據。<font color=#FF4500 size=3>標記位爲10,索引位爲0,偏移位爲0,塊號爲0。</font>緩存行中有數據,組0的第一行有效位爲1,第二行有效位爲0,地址的標記位和組0的第一行和第二行的標記位不匹配,所以,未命中。而後,高速緩存從內存中取出塊8,塊9, 共2字節,並存儲在組0的第二行中。具體以下圖所示。
5. 讀地址0的數據。<font color=#FF4500 size=3>標記位爲00,索引位爲0,偏移位爲0,塊號爲0。</font>緩存行中有數據,組0的第一行有效位爲1,地址的標記位和組0的第一行的標記位匹配,所以,命中。具體以下圖所示。
地址 | 二進制 | 是否命中 |
---|---|---|
0 | [${0000_2}$] (t=00,s=0,b=0) | 否 |
1 | [${0001_2}$](t=00,s=0,b=1) | 是 |
7 | [${0111_2}$](t=01,s=1,b=1) | 否 |
8 | [${1000_2}$](t=10,s=0,b=0) | 否 |
0 | [${0000_2}$](t=00,s=0,b=0) | 是 |
兩路相聯高速緩存與直接映射高速緩存相比,在每組中增長了一行,緩存命中率提高了15%。避免了緩存頻繁從內存中存取數據的狀況,提升了程序運行速度。
全相聯高速緩存中的行匹配和字選擇與組相聯高速緩存中的是同樣的,過程就再也不贅述,其結構圖以下所示。
相聯度越高越好嗎?答案是否認的。較高的相聯度會形成較高的成本。<font color=#FF4500 size=3>實現難度大,價格昂貴,並且很難使之速度變快。</font>較高的相聯度會增長命中時間,由於複雜性增長了,另外,還會增長不命中處罰,由於選擇犧牲行的複雜性也增長了。
相聯度的選擇最終變成了命中時間和不命中處罰之問的折中。通常來說,<font color=#FF4500 size=3>高性能系統會爲L1高速緩存選擇較低的相聯度</font>(這裏的不命中處罰只是幾個週期),而在不命中處罰比較高的較低層上使用比較小的相聯度。例如, Intel Core i7系統中,L和L2高速緩存是8路組相聯的,而L3高速緩存是16路組相聯的。
在此以前,咱們一直假設高速緩存只保存數據。不過,實際上,高速緩存既保存數據,也保存指令。只保存指令的高速緩存稱爲<font color=#0000FF size=3> i-cache </font>。只保存程序數據的高速緩存稱爲 <font color=#0000FF size=3> d-cache </font>。既保存指令又包括數據的高速緩存稱爲 <font color=#0000FF size=3> 統一的高速緩存 </font>。
以下圖所示爲 Intel Core i7處理器的高速緩存層次結構。每一個CPU芯片有四個核。每一個核有本身的L1 i-cache, L1 d-cache和L2統一的高速緩存。全部的核共享片上L3統一的高速緩存。其具體參數以下表所示。
緩存 | 大小 | 內部結構 | 訪問時間 |
---|---|---|---|
L1 | 32KB | 8路相聯 | 4時鐘 |
L2 | 256KB | 8路相聯 | 10時鐘 |
L3 | 8M | 16路相聯 | 40-75時鐘 |
最後介紹下衡量高速緩存性能的一些指標:
在一個程序執行或程序的一部分執行期間,內存引用不命中的比率,它等於:<font color=#0000FF size=3> 不命中數量/引用數量。 </font>
命中的內存引用比率。它等於:
從高速緩存傳送一個字到CPU所需的時間,包括組選擇、行確認和字選擇的時間。通常來說,L1緩存的命中時間爲:4個時鐘。L2緩存的命中時間爲:10個時鐘。
未命中須要的額外時間。對於主存來講,通常爲<font color=#0000FF size=3> 50 ~ 200個時鐘週期。 </font>
舉個例子:假設緩存命中時間爲1個時鐘週期,緩存未命中懲罰爲100個時鐘週期。
下面計算下97%緩存命中率和99%的緩存命中率的平均訪問時間爲多少?計算公式爲命中時間加上未命中處罰乘以百分系數。
97%的命中率:$1 + 0.03 \times 100 = 4$時鐘。
99%的命中率:$1 + 0.01 \times 100 = 2$時鐘。
<font color=#0000FF size=3> 結論:命中率增長2%,平均訪問時間減小了50%。 </font>
計算機中存在着各類各樣的緩存,好比,<font color=#0000FF size=3> 文件緩存 </font>把一些須要高速存取的變量緩存在內存中,每次訪問直接讀出便可。<font color=#0000FF size=3> 瀏覽器緩存 </font>根據一套與服務器約定的規則進行工做,若是在瀏覽過程當中前進或後退時訪問到同一個圖片,這些圖片能夠從瀏覽器緩存中調出而即時顯示。<font color=#0000FF size=3>數據庫緩存 </font>常常須要從數據庫查詢的數據、或常常更新的數據放入到緩存中,這樣下次查詢時,直接從緩存直接返回,減輕數據庫壓力。
咱們瞭解這麼多基本概念有什麼用呢?若是咱們理解了計算機系統是如何將數據在內存中組織和移動的,那麼在寫程序時就能夠把數據項存儲在合適的位置,CPU能更快地訪問到它們,提升程序的執行效率。
下一篇文章咱們將介紹<font color=#0000FF size=3>如何寫出高效的代碼,讓程序運行的更快! </font>歡迎關注個人公衆號,第一時間獲取更新!
養成習慣,先贊後看!若是以爲寫的不錯,歡迎關注,點贊,在看,轉發,謝謝!
如遇到排版錯亂的問題,能夠經過如下連接訪問個人CSDN。
**CSDN:[CSDN搜索「嵌入式與Linux那些事」]