對緩存的思考——提高命中率


開篇

編寫高效的程序並不只在於算法的精巧,還應該考慮到計算機內部的組織結構,cpu微指令的執行,緩存的組織和工作原理等。

好的算法在實際中不見得有高效率,如果完全沒有考慮緩存、微指令實現的話。

前兩篇博文

局部性原理淺析 介紹了程序的局部性原理,如何寫出局部性良好代碼。

提高程序性能、何爲緩存 討論了存儲器層次結構,計算機內部的存儲結構、緩存的概念,簡單的介紹了緩存的工作機制。

建議先閱讀前兩篇博文,雖然他們之間聯繫不大,在前面也有一些對本文的鋪墊。而且,這是一個系列的文章。旨在優化程序性能。

這篇博文主要介紹的是緩存的組織、工作原理。撥開迷霧,讓你更加清晰的認識緩存。

通用緩存結構

回顧

提高程序性能、何爲緩存中提到:早起的cpu存儲層次只有三層,即cup的寄存器,DRAM主存和磁盤存儲。因爲寄存器和主存之間的訪問時間開銷差距很大,於是設計者在寄存器(一個時鐘週期)和主存之間加入了L1緩存(2——4個時鐘週期),後來由於L1緩存和主存之間的差距,又在主存和L1之間加入了L2緩存,當然後面還有L3緩存,,,等等。

在這裏爲了簡單起見,假設CPU寄存器和主存之間只有一個L1緩存。

下圖是高速緩存存儲器的典型總線結構:

緩存結構

下圖清晰的說明了通用緩存的組織結構:

可以看到,緩存內部是以組的形式組織的。圖中的每一塊代表一組,每組由一到多行組成(當然圖中的是每組有多行)。

每一行包括

1 位標記位(valid bit)標明這行的信息是否有可用

t 位的標記,標明它是屬於這一組的哪一行

剩下的空間是存儲數據的數據的空間

可以看出在下面的圖中把數據地址分爲了三部分,左邊 t 位是標記行號的,中間的 s 位標明組號,最後的 b 位則是數據塊在行內的偏移量。

通常來說,緩存器可描述爲(S; E; B; m)其中S爲緩存中的組數,E爲每組的行數,B爲每行存儲的字節數,m爲緩存的地址位數。

所以緩存的容量爲C=S*E*B。

從緩存中取數據

(這個過程在上一篇博文中就有簡單的介紹)當cpu需要從主存中取出地址爲A的數據時,先把地址A發送給緩存,如果緩存中存有地址爲A的數據,就從緩存中取出該數據傳給cpu。

那麼,緩存是如何知道自己是否存有地址爲A的數據呢?這就和緩存的組織有關係了,上文中緩存把地址組分爲了三部分,t 、s 、b。

所以,只要簡單的檢查地址中的數據位,就能判斷該地址是否在緩存中,如果在的話,還能確定該數據的位置。

參數 s 、b 、m 把m個地址位分爲三個字段。如下圖:

下面的詳細的尋址過程

地址A中的中間S 位標記了該地址在緩存中屬於哪一組,先通過s 確定這個地址在緩存中的哪一組。

通過上面一步確定了屬於的組後,地址A中的左邊 t 位標記了該地址在該組的哪一行。

最後由右邊的 b 指出地址A中的元素在該行的偏移位。也就是確定在這行的哪一個位置。

CPU從主存中讀數據的詳細過程

和上文中說的一樣,這裏假設計算機的存儲結構只有:cpu寄存器,L1緩存,主存。

當cpu執行一條讀存儲器地址爲A的指令,它向高速緩存請求該地址,如果緩存命中,緩存很快返回數據。如果緩存不命中,L1緩存向主存請求該數據,在這期間cpu必須等待。當被請求塊從主存到達緩存L1時,L1緩存將數據放在他的一個高速緩存行裏,然後將數據從行中提取返回給cpu。也就是說,如果緩存不命中,先要把數據存入緩存,再返回給cpu。

概括的說,高速緩存確定一個請求是否命中有三個過程: 

1、組選擇

2、行匹配

3、字抽取

下面將會結合具體情況說明這一過程。

直接映射高速緩存

現在已經知道,我們用(S; E; B; m) 來描述緩存,這裏就根據其中的E,也就是一組的行數, 把緩存分爲不同的類別。

當E=1 時,也就是說每組只有一行的緩存組織形式,我們稱爲直接映射高速緩存。因爲容易理解,先對它進行介紹。

(圖片來源 《computer systems》)

正如上圖所示,直接映射高速緩存中每組只有一行。

直接映射高速緩存中取數據

下面將以直接映射高速緩存爲例,一步步說明cup從高速緩存中取數據的過程。

1、組選擇

如上圖所示,緩存從地址A中抽取出中間的s 位,這 s 爲的數值就標記了該地址所在的組。這裏可以把緩存當作是一維數組,其中每個元素是一個組,而地址中的 s 位則是這些組的索引。如圖中的組標記爲 0001 對應組 set1。這要把地址中間的 s 爲提取,就能得到該地址在緩存中對應的組。

2、 行選擇

選好組 i 之後,就是確定地址A在組 i 的哪一行。因爲直接映射緩存的每一組只有一個行。所以只要看A地址中的行標記是否和緩存中的行標記位匹配。匹配則地址A中的數據在緩存中。

3、字抽取

既然已知道了地址A中的數據在緩存中的位置,最後一步只要更具地址A中表示偏移量的位,從緩存中取出數據即可。

如下圖所示:

直接映射高速緩存不命中

當緩存不命中的時候,就要從下一層存儲中取出數據,放入緩存的某個位置中,放入的位置就由請求地址A中的組索引確定所在緩存的組,行所以確定應該放置的行。如果組中的行都是有效緩存行了,就必須要驅逐現有的一個行。對於直接映射高速緩存,每組包含一個行,替換策略就變的很簡單,用新來的行替換當前行。

直接映射緩存尋址示例

通過上面的介紹,已經基本瞭解了緩存的組織形式以及如何從緩存中取出數據,但是上面都只是理論上的闡述。

爲了能更好的瞭解,這裏會有一個具體的示例。誠然,學習一種只是最好的方式就是應用它。 如果你已經對上面的知識有所瞭解,那麼請繼續吧。下面的內容會讓你更清楚的瞭解到緩存工作的機制。

假設我們有一個直接映射的高速緩存,描述如下

(S; E; B; m)=(4;1;2;4)

也就是說:該緩存有4個組(s=4),每組有一行(E=1),每一塊有兩個字節(B=2)存儲器的地址是4位的(m=4)

該狀態有圖描述如下:

其中最左邊的一列是地址,中間的三列是地址的二進制表示形式。最右邊的一列是虛擬存儲器的塊的標號。

和上文中說的一樣,緩存尋址時,把地址分爲了三個部分。分別表示該地址在緩存中所在的組、行、以及偏移。和上圖所對應是四位的地址,

行:其中最高的一位標記所在的行,因爲是直接映射高速緩存,每組只有一行,所以一位就能表示。

:中間的兩位表示地址所在的組。從圖中可以看出,擁有相同組的地址有四個,比如組號爲00 的地址有四個,爲0、2、8、9

偏移:偏移位由最右邊的一位表示。每行中有兩個數據塊,所以偏移位用一位也就能表示。

看這個表的時候有一點提示:中間的三列其實是第一列地址的二進制表示形式。

下面是對這個特定緩存的一點分析:

(S; E; B; m)=(4;1;2;4)

該緩存有四個組,每組一行。有圖中可知,要放入緩存的地址爲16個。所以每組對應四個地址。在圖中的表現就是:四個相同的地址有相同的組索引。

每行有兩個數據塊,用地址最低位表示(0表示第一個,1爲第二個)。

看組索引爲00的地址,爲0 、1 和 8 、9。0和1有相同的行標記0,8和9有相同的行標記1.所以地址爲0、1的數據要麼都在組00中,要麼多不在。地址爲8、9的也一樣。說明了0、1是一個整體,8、9也一樣。如果在,都在;不在,都不在。這兩個整體通過最高位(標記爲)來標明。

下面是尋址實例

剛開始時,緩存是空的,也就是還沒有預熱,如下圖所示

1)讀地址0的字

地址0的爲 0 00 0 對應緩存中第0組,行標記位爲0的,偏移爲0的位置。顯然,現在緩存還是空的(標誌位 valid 都爲0)。緩存不命中,所以緩存先從下一級的存儲中取出改行對應的所有地址的元素,放入緩存中。(也就是地址爲0 和1 的元素)。然後再從緩存中取出數據m[0],傳遞給cpu。

進過對地址0的讀操作後,緩存的組織情況如下所示

這也驗證了上文的說法,地址0 和1 是一體的,他們要麼都在,要麼都不在。因爲他們有相同的組索引、行索引。

2)讀取地址1的字

地址1爲0 00 1 對應緩存中的第0組,行標記爲0,偏移爲1。這次緩存命中,從緩存返回m[1]

3)讀地址13的字

地址13爲1 10 1 對應緩存中的第2組 行標記爲1 偏移爲1。同1)一樣,緩存不命中,從低一級存儲中取出 組索引爲10 行爲1 的數據放入緩存,然後返回m[13]

對地址13進行操作後的緩存情況爲

4)讀地址爲8的字

地址8爲 1 00 0 組索引爲00 行標記爲1 偏移爲0 在看上圖的緩存組織情況,可判讀發生緩存不命中。於是從低一級存儲中取出組索引爲00 行標記爲1 的數據,也就是m[8]、m[9]放入第一行中,然後返回m[8]

操作後的緩存組織爲

通過上面的示例,應該對緩存的工作原理有一定了解了把。

其實就是吧地址分爲不同的部分,劃分爲表示組、行 和偏移。然後根據這些去判斷所需地址是否在緩存中。如果在,則返回數據,不在則從低一級的存儲中取出數據放入緩存中(放入的位置由地址確定)。然後返回地址。

組相聯高速緩存

 剛纔討論的直接映射高速緩存可以看作是緩存中的一個特例,因爲每組只有一行。這裏介紹一下更普遍的緩存結構:組相連高速緩存。

其實就是每一組有多行。如下圖是E =2 的緩存

同樣的,當要從緩存中取地址爲A的數據時,

1)先確定地址A所在的組,如下圖所示

2)確定行

3)抽取字(偏移)

全聯高速緩存

 全聯高速緩存中的S =1 ,也就是說,全聯高速緩存只有一個組。

全聯高速緩存中對數據的操作和之前討論過的兩種情況大同小異,主要就是三部。這裏就不說了。

小結

這篇博文先介紹了緩存內部的組織形式,並介紹了從緩存中讀取數據的方式,主要包括1)組選擇2)行匹配 3)字抽取

緩存可以用形如(S; E; B; m)的形式表述。其中S代表緩存中的組數,E爲每組的行數,B爲每個緩存塊的大小。

更具E的不同可將緩存分類。

這篇文章主要介紹的是緩存的工作機制。在以後的文章中會介紹如何寫出緩存友好的代碼

 

全文完。

 

 對於如何寫出緩存友好的代碼,對緩存的思考【續】——編寫高速緩存友好代碼中做出了詳細討論

 

參看資料:computer systems

如有轉載請註明出處:http://www.cnblogs.com/yanlingyin/

一條魚、尹雁鈴@ 博客園 2012-2-12

 E-mail:[email protected]

 

 

 

22
0
(請您對文章做出評價)
« 博主前一篇:提高程序性能、何爲緩存——從存儲器結構說起
» 博主後一篇:對緩存的思考【續】——編寫高速緩存友好代碼
posted @ 2012-02-14 08:28 一條魚~ 閱讀(4540) 評論(16) 編輯 收藏

發佈了18 篇原創文章 · 獲贊 7 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章