cache的基本原理


為什么需要cache

我們應該知道程序是運行在 RAM之中,RAM 就是我們常說的DDR(例如: DDR3、DDR4等)。我們稱之為main memory(主存)。當我們需要運行一個進程的時候,首先會從磁盤設備(例如,eMMC、UFS、SSD等)中將可執行程序load到主存中,然后開始執行。在CPU內部存在一堆的通用寄存器(register)。如果CPU需要將一個變量(假設地址是A)加1,一般分為以下3個步驟:

  1. CPU 從主存中讀取地址A的數據到內部通用寄存器 x0(ARM64架構的通用寄存器之一)。
  2. 通用寄存器 x0 加1。
  3. CPU 將通用寄存器 x0 的值寫入主存。

我們將這個過程可以表示如下:

其實現實中,CPU通用寄存器的速度和主存之間存在着太大的差異。兩者之間的速度大致如下關系:

CPU register的速度一般小於1ns,主存的速度一般是65ns左右。速度差異近百倍。因此,上面舉例的3個步驟中,步驟1和步驟3實際上速度很慢。當CPU試圖從主存中load/store 操作時,由於主存的速度限制,CPU不得不等待這漫長的65ns時間。如果我們可以提升主存的速度,那么系統將會獲得很大的性能提升。如今的DDR存儲設備,動不動就是幾個GB,容量很大。如果我們采用更快材料制作更快速度的主存,並且擁有幾乎差不多的容量。其成本將會大幅度上升。我們試圖提升主存的速度和容量,又期望其成本很低,這就有點難為人了。因此,我們有一種折中的方法,那就是制作一塊速度極快但是容量極小的存儲設備。那么其成本也不會太高。這塊存儲設備我們稱之為cache memory。在硬件上,我們將cache放置在CPU和主存之間,作為主存數據的緩存。 當CPU試圖從主存中load/store數據的時候, CPU會首先從cache中查找對應地址的數據是否緩存在cache 中。如果其數據緩存在cache中,直接從cache中拿到數據並返回給CPU。程序局部性:時間局部性:如果一個信息項正在被訪問,那么在近期它很可能還會被訪問;空間局部性:如果最近將要訪問的信息很可能與正在使用的信息在空間地址上是鄰近的。使用cache很好的利用這兩類程序局部性。當存在cache的時候,以上程序如何運行的例子的流程將會變成如下:

CPU和主存之間直接數據傳輸的方式轉變成CPU和cache之間直接數據傳輸。cache負責和主存之間數據傳輸。

 多級cache存儲結構

cahe的速度在一定程度上同樣影響着系統的性能。一般情況cache的速度可以達到1ns,幾乎可以和CPU寄存器速度媲美。但是,這就滿足人們對性能的追求了嗎?並沒有。當cache中沒有緩存我們想要的數據的時候,依然需要漫長的等待從主存中load數據。為了進一步提升性能,引入多級cache。前面提到的cache,稱之為L1 cache(第一級cache)。我們在L1 cache 后面連接L2 cache,在L2 cache 和主存之間連接L3 cache。等級越高,速度越慢,容量越大。但是速度相比較主存而言,依然很快。不同等級cache速度之間關系如下:

經過3級cache的緩沖,各級cache和主存之間的速度最萌差也逐級減小。在一個真實的系統上,各級cache之間硬件上是如何關聯的呢?我們看下Cortex-A53架構上各級cache之間的硬件抽象框圖如下:

在Cortex-A53架構上,L1 cache分為單獨的instruction cache(ICache)和data cache(DCache)。L1 cache是CPU私有的,每個CPU都有一個L1 cache。一個cluster 內的所有CPU共享一個L2 cache,L2 cache不區分指令和數據,都可以緩存。所有cluster之間共享L3 cache。L3 cache通過總線和主存相連。

多級cache之間的配合工作

首先引入兩個名詞概念,命中缺失。 CPU要訪問的數據在cache中有緩存,稱為“命中” (hit),反之則稱為“缺失” (miss)。多級cache之間是如何配合工作的呢?我們假設現在考慮的系統只有兩級cache。

 

當CPU試圖從某地址load數據時,首先從L1 cache中查詢是否命中,如果命中則把數據返回給CPU。如果L1 cache缺失,則繼續從L2 cache中查找。當L2 cache命中時,數據會返回給L1 cache以及CPU。如果L2 cache也缺失,很不幸,我們需要從主存中load數據,將數據返回給L2 cache、L1 cache及CPU。這種多級cache的工作方式稱之為inclusive cache。某一地址的數據可能存在多級緩存中。與inclusive cache對應的是exclusive cache,這種cache保證某一地址的數據緩存只會存在於多級cache其中一級。也就是說,任意地址的數據不可能同時在L1和L2 cache中緩存。

cache的基本原理

cache是比主存小得多的高速SRAM,成本較高。cache的所有功能都是硬件來實現的,對於應用程序員和系統程序員來說是透明的,他們感覺不到cache的存在,也無法操作cahce,但是在系統初始化時,需要嵌入式程序員對cache進行配置和使能。
cahce line是cache與主存之間進行數據交換的基本單位。CPU在讀取指令或者數據時會同時將其相鄰的指令和數據保存到一個cache line中。當CPU再次訪問這個數據或者其相鄰數據時會大大的提高速度,對系統性能由較大的改進,這利用了程序的局部性規律。
一般,主存有多大2n個字組成,這些字可以被尋址並且每個字有唯一的n位地址。為實現主存到cache的映射,主存被看成由許多定長的行組成,每行K個字,共有M=2n/K個行。對應的cache由C個行組成,每行也有K個字。cache中行的數量遠小於主存中行的數量,cache的行僅是主存中若干行的副本,每個cache line都有一個標簽tag寄存器,用於存放該行與主存中行的映射關系和一些控制信息。

cahce按結構可分為兩種,普林斯頓(Princeton):單一式,指令和數據統一存放到一個cache中;哈佛(Harvard)結構:分離式,指令和數據分開存放到兩個cache中。Harvard結構的優點是CPU能同時訪問指令cache和數據cache,使得取指令和存取數據並行操作,在流水線處理器中確實存在某條指令處於取值階段,而另一條指令處於存儲器訪問階段;其次哈佛結構可以獨立配置指令cache和數據cache的容量。不足之處也在於,當不同的程序對指令和數據有不同需求時,不能靈活調整。在現在超標量CPU的設計中,第一級都采用分離式cache架構,而第二級乃至第三級都采用單一的cache架構。分離式cache在基於指令流水線的設計中效率較高,因為它消除了一條指令取值和另一條指令訪存的結構冒險。

cache的映射方式

直接映射(Direct mapped cache)

直接映射是最簡單的映射方式。每個固定的cache line只能映射有共同特征的主存行,例如采用取模方式的映射方式。如下圖所示,大小為64 byte的cache,cache line大小為8 byte。對於32位內存地址來說,最低3 bit是offset,代表一個byte在cache line的行內偏移,然后3 bit是索引地址,代表cache line的行號,剩余bit代表Tag地址。當CPU發出訪存地址時,cache控制器首先利用3 bit索引找到對應的cache line的tag,並與其比較,如果相同則cache命中。雖然直接映射技術實現比較簡單,但是當程序經常訪問映射到同一cache line的主存來說,會導致cahce的顛簸,效率大大降低。

全關聯映射(Full associative cache)

全關聯映射允許cahce line存儲主存任意行。cahce控制器把內存地址簡單分為tag和offset兩部分。由於當CPU發出訪存地址時,cache需要比較cahce中的每個tag,效率較低,功耗大,

組關聯映射(v-way set associative cache)

組關聯映射是直接映射和全關聯映射的一種折中辦法。在組關聯映射中,cahce分為v個路,每路有k個行,稱為v路組關聯映射。對於v路組關聯映射,主存行可映射到某一特定組中的v個cache行的任意一行(共k組,內存行所映射的組號與其地址中索引段決定)。組關聯映射具有較強的靈活性,而且每次命中判斷時CPU發出的地址只需與所映射組的k個tag相比較,因此功耗較低。如下圖,當CPU發出訪存時,首先通過索引地址找到對應的組,每個組有兩個cache行,對組內的tag進行依次比較,如果相同則命中cahce。

一般,在cache行大小和容量相同的前提下,組關聯數越高命中率越高,但每次訪問都需要比較所有的tag,造成單次訪問延時大,功耗高;而關聯數較低,容易引起cache的沖突,性能下降。

cache替換策略

由於cache的行數遠小於主存的函數,因此必定會發生行的替換,為了提高訪問速度,替換算法由硬件完成。

  • 隨機替換算法

用偽隨機來產生要替換的行。在實際測試中有不錯的表現,對於硬件實現也比較簡單。

  • 最近最少用替換策略LRU

該替換策略選擇那些最近最少使用的行,意味着需要為每行設計相應的計數器,並且在替換時選擇最少使用的行,硬件設計較為負載。

  • 偽LRU替換策略

每個cahce行設置1 bit控制位。初始狀態所有行都為0,如果訪問過該行,則置為1,替換時總是選擇狀態為0的行,如果有多個0則隨機選擇一行,如果都為1,那么把其他行置為0,隨機選擇一行。

cache分配策略

  • 讀操作分配策略(read-allocate)

對於讀操作分配策略,在指令或者數據缺失時為主存分配cache行,將主存讀入cache行之后,再把目標數據返回給CPU;在寫數據缺失時並不分配cache行,只把數據寫入主存。

  • 寫操作分配策略(write-allocate)

寫缺失時,主存讀入cache后才進行寫操作

  • read-write-allocate

無論寫缺失還是讀缺失都會分配相應的cache行給主存。

cache寫策略

  • 寫穿write through策略

如果寫命中,即cache已經緩存地址段,那么寫數據寫入cache的同時寫入主存;寫缺失,則只將寫數據寫入主存,而不在cache進行備份。CPU需要等待緩慢的主存操作。

  • 帶write buffer的寫穿策略

如果寫命中,則將寫數據寫入cache的同時寫入write buffer;寫缺失時只寫入write buffer。寫緩沖是一塊片上存儲器,CPU發起的寫操作可以立即保存到寫緩沖,不用等待片外存儲器。由於寫緩沖可能有有效數據,因此CPU每次讀操作時需要先判斷數據是否在寫緩沖區中

  • 寫回write back策略

寫回策略是指CPU在寫操作時,如果命中,只更新cache內容,而不立刻寫入主存,這時cache和主存數據不同。僅當cache line被替換是才更新臟塊到主存中。寫回策略需要1 bit控制位,表示dirty狀態。
可以從以下方面對寫回和寫穿進行比較:可靠性、與主存的通信量、控制復雜性。

  1. 寫穿比寫回可靠性強,因為能保證cache和主存數據一致
  2. 寫穿與主存的通信量要高於寫回
  3. 寫回比寫穿控制邏輯復雜,因為寫回需要dirty位

參考:https://zhuanlan.zhihu.com/p/102293437

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM