Cache直接映射、組相連映射以及全相連映射(轉載)

說明:

  這篇文章挺好,轉載一下,防止迷路。

轉載:

  https://my.oschina.net/fileoptions/blog/1630855

 

  Cache的容量很小,它保存的內容只是主存內容的一個子集,且Cache與主存的數據交換是以塊爲單位的。爲了把信息放到Cache中,必須應用某種函數把主存地址定位到Cache中,這稱爲地址映射在信息按這種映射關係裝入Cache後,CPU執行程序時,會將程序中的主存地址變換成Cache地址,這個變換過程叫做地址變換。

     Cache的地址映射方式有直接映射、全相聯映射和組相聯映射。假設某臺計算機主存容量爲l MB,被分爲2048塊,每塊512B;Cache容量爲8KB,被分爲16塊,每塊也是512B。下面以此爲例介紹三種基本的地址映射方法。

直接映射

      直接映射的Cache組織如圖3-14所示。主存中的一個塊只能映射到Cache的某一特定塊中去。例如,主存的第0塊、第16塊、……、第2032塊,只能映射到Cache的第0塊;而主存的第1塊、第17塊、……、第2033塊,只能映射到Cache的第1塊……。

                                          327.gif

      直接映射是最簡單的地址映射方式,它的硬件簡單,成本低,地址變換速度快,而且不涉及替換算法問題。但是這種方式不夠靈活,Cache的存儲空間得不到充分利用,每個主存塊只有一個固定位置可存放,容易產生衝突,使Cache效率下降,因此只適合大容量Cache採用。例如,如果一個程序需要重複引用主存中第0塊與第16塊,最好將主存第0塊與第16塊同時複製到Cache中,但由於它們都只能複製到Cache的第0塊中去,即使Cache中別的存儲空間空着也不能佔用,因此這兩個塊會不斷地交替裝入Cache中,導致命中率降低。

全相聯映射

       圖3-15 是全相聯映射的Cache組織,主存中任何一塊都可以映射到Cache中的任何一塊位置上。

 

                                               328.gif

      全相聯映射方式比較靈活,主存的各塊可以映射到Cache的任一塊中,Cache的利用率高,塊衝突概率低,只要淘汰Cache中的某一塊,即可調入主存的任一塊。但是,由於Cache比較電路的設計和實現比較困難,這種方式只適合於小容量Cache採用。

組相聯映射

      組相聯映射實際上是直接映射和全相聯映射的折中方案,其組織結構如圖3-16所示。主存和Cache都分組,主存中一個組內的塊數與Cache中的分組數相同,組間採用直接映射,組內採用全相聯映射。也就是說,將Cache分成u組,每組v塊,主存塊存放到哪個組是固定的,至於存到該組哪一塊則是靈活的。例如,主存分爲256組,每組8塊,Cache分爲8組,每組2塊。

 

                                              329.gif

      主存中的各塊與Cache的組號之間有固定的映射關係,但可自由映射到對應Cache組中的任何一塊。例如,主存中的第0塊、第8塊……均映射於Cache的第0組,但可映射到Cache第0組中的第0塊或第1塊;主存的第1塊、第9塊……均映射於Cache的第1組,但可映射到Cache第1組中的第2塊或第3塊。

      常採用的組相聯結構Cache,每組內有2、4、8、16塊,稱爲2路、4路、8路、16路組相聯Cache。組相聯結構Cache是前兩種方法的折中方案,適度兼顧二者的優點,儘量避免二者的缺點,因而得到普遍採用。

 

一次內存訪問示意圖

              architecture

  注意事項

  • TLB採用組相聯
  • 頁表採用兩級頁表
  • cache採用組相聯
  • cache僅考慮L1 d-cache,不考慮L1 i-cache、L2 cache和L3 cache
  • 未考慮頁表缺頁
  • 簡化了cache未命中情況

實際例子

      下面展示了現代Intel處理器的CPU cache是如何組織的。有關cache的討論往往缺乏具體的實例,使得一些簡單的概念變得撲朔迷離。也許是我可愛的小腦瓜有點遲鈍吧,但不管怎樣,至少下面講述了故事的前一半,即Core 2的 L1 cache是如何被訪問的:

            

                                L1 cache – 32KB,8路組相聯,64字節緩存線

 

 

ç¸å³å¾ç

 

1. 由索引揀選緩存組(行)

      在cache中的數據是以緩存線(line)爲單位組織的,一條緩存線對應於內存中一個連續的字節塊。這個cache使用了64字節的緩存線。這些線被保存在cache bank中,也叫路(way)。每一路都有一個專門的目錄(directory)用來保存一些登記信息。你可以把每一路連同它的目錄想象成電子表格中的一列,而表的一行構成了cache的一組(set)。列中的每一個單元(cell)都含有一條緩存線,由與之對應的目錄單元跟蹤管理。圖中的cache有64 組、每組8路,因此有512個含有緩存線的單元,合計32KB的存儲空間。

      在cache眼中,物理內存被分割成了許多4KB大小的物理內存頁(page)(每一路是一個page?)。每一頁都含有4KB / 64 bytes == 64條緩存線。在一個4KB的頁中,第0到63字節是第一條緩存線,第64到127字節是第二條緩存線,以此類推。每一頁都重複着這種劃分,所以第0頁第3條緩存線與第1頁第3條緩存線是不同的。

      在全相聯緩存(fully associative cache)中,內存中的任意一條緩存線都可以被存儲到任意的緩存單元中。這種存儲方式十分靈活,但也使得要訪問它們時,檢索緩存單元的工作變得複雜、昂貴。由於L1和L2 cache工作在很強的約束之下,包括功耗,芯片物理空間,存取速度等,所以在多數情況下,使用全相聯緩存並不是一個很好的折中。

      取而代之的是圖中的組相聯緩存(set associative cache)。意思是,內存中一條給定的緩存線只能被保存在一個特定的組(或行)中。所以,任意物理內存頁的第0條緩存線(頁內第0到63字節)必須存儲到第0組,第1條緩存線存儲到第1組,以此類推。每一組有8個單元可用於存儲它所關聯的緩存線,從而形成一個8路關聯的組(8-way associative set)。當訪問一個內存地址時,地址的第6到11位(譯註:組索引,因爲有64個組,所以6bit索引)指出了在4KB內存頁中緩存線的編號,從而決定了即將使用的緩存組。舉例來說,物理地址0x800010a0的組索引是000010,所以此地址的內容一定是在第2組中緩存的。

      但是還有一個問題,就是要找出一組中哪個單元包含了想要的信息,如果有的話。這就到了緩存目錄登場的時刻。每一個緩存線都被其對應的目錄單元做了標記(tag);這個標記就是一個簡單的內存頁編號,指出緩存線來自於哪一頁。由於處理器可以尋址64GB的物理RAM,所以總共有64GB / 4KB == 224個內存頁,需要24位來保存標記。前例中的物理地址0x800010a0對應的頁號爲524,289。下面是故事的後一半:

           

 

2、在組中搜索匹配標記

      由於我們只需要去查看某一組中的8路,所以查找匹配標記是非常迅速的;事實上,從電學角度講,所有的標記是同時進行比對的,我用箭頭來表示這一點。如果此時正好有一條具有匹配標籤的有效緩存線,我們就獲得一次緩存命中(cache hit)。否則,這個請求就會被轉發的L2 cache,如果還沒匹配上就再轉發給主系統內存。通過應用各種調節尺寸和容量的技術,Intel給CPU配置了較大的L2 cache,但其基本的設計都是相同的。比如,你可以將原先的緩存增加8路而獲得一個64KB的緩存;再將組數增加到4096,每路可以存儲256KB。經過這兩次修改,就得到了一個4MB的L2 cache。在此情況下,需要18位來保存標記,12位保存組索引;緩存所使用的物理內存頁的大小與其一路的大小相等。(譯註:有4096組,就需要lg(4096)==12位的組索引,緩存線依然是64字節,所以一路有4096*64B==256KB字節;在L2 cache眼中,內存被分割爲許多256KB的塊,所以需要lg(64GB/256KB)==18位來保存標記。)

     如果有一組已經被放滿了,那麼在另一條緩存線被存儲進來之前,已有的某一條則必須被騰空(evict)。爲了避免這種情況,對運算速度要求較高的程序就要嘗試仔細組織它的數據,使得內存訪問均勻的分佈在已有的緩存線上。舉例來說,假設程序中有一個數組,元素的大小是512字節,其中一些對象在內存中相距4KB。這些對象的各個字段都落在同一緩存線上,並競爭同一緩存組。如果程序頻繁的訪問一個給定的字段(比如,通過虛函數表vtable調用虛函數),那麼這個組看起來就好像一直是被填滿的,緩存開始變得毫無意義,因爲緩存線一直在重複着騰空與重新載入的步驟。在我們的例子中,由於組數的限制,L1 cache僅能保存8個這類對象的虛函數表。這就是組相聯策略的折中所付出的代價:即使在整體緩存的使用率並不高的情況下,由於組衝突,我們還是會遇到緩存缺失的情況。然而,鑑於計算機中各個存儲層次的相對速度,不管怎麼說,大部分的應用程序並不必爲此而擔心。

      一個內存訪問經常由一個線性(或虛擬)地址發起,所以L1 cache需要依賴分頁單元(paging unit)來求出物理內存頁的地址,以便用於緩存標記。與此相反,組索引來自於線性地址的低位,所以不需要轉換就可以使用了(在我們的例子中爲第6到11位)。因此L1 cache是物理標記但虛擬索引的(physically tagged but virtually indexed),從而幫助CPU進行並行的查找操作。因爲L1 cache的一路絕不會比MMU的一頁還大,所以可以保證一個給定的物理地址位置總是關聯到同一組,即使組索引是虛擬的。在另一方面L2 cache必須是物理標記和物理索引的,因爲它的一路比MMU的一頁要大。但是,當一個請求到達L2 cache時,物理地址已經被L1 cache準備(resolved)完畢了,所以L2 cache會工作得很好。

      最後,目錄單元還存儲了對應緩存線的狀態(state)。在L1代碼緩存中的一條緩存線要麼是無效的(invalid)要麼是共享的(shared,意思是有效的,真的J)。在L1數據緩存和L2緩存中,一條緩存線可以爲4個MESI狀態之一:被修改的(modified),獨佔的(exclusive),共享的(shared),無效的(invalid)。Intel緩存是包容式的(inclusive):L1緩存的內容會被複制到L2緩存中。 

總結

1.內存層次結構的意義在於利用引用的空間局部性和時間局部性原理,將經常被訪問的數據放到快速的存儲器中,而將不經常訪問的數據留在較慢的存儲器中。

2.一般情況下,除了寄存器和L1緩存可以操作指定字長的數據,下層的內存子系統就不會再使用這麼小的單位了,而是直接移動數據塊,比如以緩存線爲單位訪問數據。

3.對於組衝突,可以這麼理解:與上文相似,假設一個緩存,由512條緩存線組成,每條線64字節,容量32KB。

    a) 假如它是直接映射緩存,由於它往往使用地址的低位直接映射緩存線編號,所以所有的32K倍數的地址(32K,64K,96K等)都會映射到同一條線上(即第0線)。假如程序的內存組織不當,交替的去訪問佈置在這些地址的數據,則會導致衝突。從外表看來就好像緩存只有1條線了,儘管其他緩存線一直是空閒着的。

     b)  如果是全相聯緩存,那麼每條緩存線都是獨立的,可以對應於內存中的任意緩存線。只有當所有的512條緩存線都被佔滿後纔會出現衝突。

     c)  組相聯是前兩者的折中,每一路中的緩存線採用直接映射方式,而在路與路之間,緩存控制器使用全相聯映射算法,決定選擇一組中的哪一條線。

     d) 如果是2路組相聯緩存,那麼這512條緩存線就被分爲了2路,每路256條線,一路16KB。此時所有爲16K整數倍的地址(16K,32K,48K等)都會映射到第0線,但由於2路是關聯的,所以可以同時有2個這種地址的內容被緩存,不會發生衝突。當然了,如果要訪問第三個這種地址,還是要先騰空已有的一條才行。所以極端情況下,從外表看來就好像緩存只有2條線了,儘管其他緩存線一直是空閒着的。

      e)  如果是8路組相聯緩存(與文中示例相同),那麼這512條緩存線就被分爲了8路,每路64條線,一路4KB。所以如果數組中元素地址是4K對齊的,並且程序交替的訪問這些元素,就會出現組衝突。從外表看來就好像緩存只有8條線了,儘管其他緩存線一直是空閒着的。

 

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