計算機組成與設計-Cache基本原理


Cache的基本原理

在學習Cache的基本原理之前,我們首先先介紹什么是時間局部性以及空間局部性,我們會用一個例子說明存儲順序是如何對我們編寫的程序性能產生影響的。

時間局部性和空間局部性

我們仍然回到我們一開始講的那個圖書館的例子,如果你已經忘了,我們重新來回顧一下那個例子:

想象你正坐在圖書館中完成一份關於計算機硬件重要歷史性發展的論文,你可以從圖書館的書架上精心挑選一些經典的計算機書籍,並將它們放在書桌上。你從這些書中找到了需要寫的幾種重要的計算機,但是沒有找到關於EDSAC的,因此,你返回書架去尋找其他書,並在早期的英國計算機書籍中找到了一本有關EDSAC的書。一旦在你的書桌上有了選好的一些書,你就有可能從這些書中找到你需要的內容。這樣一來,你的大部分時間只需花在閱讀這些書上,而無需返回書架。

試比較這兩種情況:一種是在你的書桌上有好幾本書;另一種是書桌上只有一本書,你不得不頻繁的返回書架,進行還書后取另一本書。很明顯,在書桌前放一些常用的書籍會更加節省時間。

同樣,在計算機底層實現中,我們可以構建一個大容量的虛擬存儲器,它能像小容量的存儲器那樣被快速訪問。就像你不會同時以相同的概率查閱圖書館中的每一本書那樣,一個程序自然也不會同時以同樣的概率訪問全部的代碼和數據。否則,不可能讓存儲器在保持大容量的同時又能快速訪問,就像你不能既要求你把圖書館中所有的圖書都放在書桌上,還要求你能保持快速查找一樣。

這就是局部性原理的理解,局部性原理表明了在任何時間內,程序訪問的只是地址空間內較小的一段內容

【CSAPP-深入理解計算機系統】6-3. 固態硬盤 局部性 - 1.喵影-我的影片(Av505208776,P1).mp4_20211109_090141.882

  • 時間局部性:如果某個數據被訪問,那么在不久的將來它可能再次被訪問。
  • 空間局部性:如果某個數據項被訪問,那么在不久的將來,與它地址相鄰的數據項也可能被訪問。

程序的局部性起源於簡單自然的程序結構。例如,大多數程序都包含了循環結構,因此這部分指令和數據被重復的訪問,呈現出來很高的時間局部性。由於指令通常是順序執行的,因此也體現出很高的空間局部性。

在我們用C語言編寫訪問二維數組程序的時候,你有沒有考慮過如果我們先訪問數組的列再訪問數組的行,會對程序的性能產生何種影響呢?

【CSAPP-深入理解計算機系統】6-3. 固態硬盤 局部性 - 1.喵影-我的影片(Av505208776,P1).mp4_20211110_152351.456

在這個程序中,我們首先訪問數組的行接着訪問數組的列。

我們將這個程序改為先訪問列,再訪問行。

【CSAPP-深入理解計算機系統】6-3. 固態硬盤 局部性 - 1.喵影-我的影片(Av505208776,P1).mp4_20211110_152346.480

雖然得到的結果是完全一致的,但是程序的空間局部性變差,程序的性能受到了較大影響。

【CSAPP-深入理解計算機系統】6-3. 固態硬盤 局部性 - 1.喵影-我的影片(Av505208776,P1).mp4_20211110_152254.993

一般而言,有良好局部性的程序比局部性差的程序運行的更快

先訪問列導致空間局部性變差的原因是在內存中的存儲也是按照行優先的順序進行存儲的。(具體可看上節內容)

Cache的基本原理

在前面介紹的圖書館例子中,書桌就好比Cache(高速緩存),緩存在如今的計算機中幾乎無處不在,用途十分廣泛。

【CSAPP-深入理解計算機系統】6-4. 存儲器層次結構 - 1.6-4(Av805346423,P1).mp4_20211109_090219.532

例如:圖中第 \(K+1\)層的存儲器被划分成了16個大小固定的塊,每個塊都有唯一的地址 這里我們用編號0-15來表示。第\(K\)層的存儲器有4個塊的空間,每個塊的大小與\(K+1\)層的塊一樣。

數據總是以塊為單元 在第\(K\)層和第\(K\)加一層之間來回復制。例如 當前第\(K\)層的存儲器包含了四個塊的副本,對於相鄰層之間的塊大小是固定的,然而 不相鄰的層次之間塊大小是不一樣的。一般來說 層次結構中離CPU越遠的設備,訪問時間就越長。

接下來,我們將重新介紹在之前已經提到的幾個緩存相關概念。

【CSAPP-深入理解計算機系統】6-4. 存儲器層次結構 - 1.6-4(Av805346423,P1).mp4_20211109_090234.035

當程序需要讀取第\(K+1\)層的某個數據對象\(d\)時,它首先從第\(K\)層的數據塊中檢索是否包含目標數據d的副本,如果目標數據d剛好緩存在第\(K\)層中 我們將這種情況成為緩存命中。另一方面,如果第\(k\)層沒有緩存目標數據d 我們將這種情況成為緩存不命中

當發生不命中時,第\(k\)層的緩存要從第\(k+1\)層取出包含目標數據的塊。如果第\(K\)層的緩存已經滿了,這時包含目標數據的塊就會覆蓋現存的一個塊。我們把這個過程稱為替換。被替換的塊也稱為犧牲塊

決定替換哪個塊是由緩存的替換策略來具體決定的

接下來 我們重點來看一下基於SRAM的高速緩存。

早期計算機系統的存儲層次結構只有三層 分別是計算器 文件 內存以及磁盤。

【CSAPP-深入理解計算機系統】6-4. 存儲器層次結構 - 1.6-4(Av805346423,P1).mp4_20211109_090237.798

由於CPU與內存之間的性能差距逐漸增大,於是系統設計者在寄存器文件和內存之間插入了SRAM的高速緩存(L1 cache L2cache)。

【CSAPP-深入理解計算機系統】6-4. 存儲器層次結構 - 1.6-4(Av805346423,P1).mp4_20211109_090240.028

Cache的內部結構

整個cache被划分成一個或者多個set,這里我們用變量S來表示set的個數,每個set包含一個或者多個cache line(高速緩沖行)。這里我們用變量e來表示一個set中cache line的行數。

每個cache line由三部分組成 分別是有效位、標記、數據塊。

【CSAPP-深入理解計算機系統】6-4. 存儲器層次結構 - 1.6-4(Av805346423,P1).mp4_20211109_090254.806

  • 其中有效位的長度是一個bit ,表示當前開始按存儲的信息是否有效。當valid為1時表示數據有效 當valid為零時表示數據無效。
  • 標記是用來確定目標數據是否存在於當前的cache line中。
  • 數據塊就是一小部分內存數據的副本,大小用B來表示

通常來說,Cache的結構可以用元組\({S,E,B,m}\)來描述

Cache的大小是指所有數據塊的和,其中有效位和標記位不包括在內。

因此 開始的容量可以通過:

\[S×E×B \]

得到。

【CSAPP-深入理解計算機系統】6-4. 存儲器層次結構 - 1.6-4(Av805346423,P1).mp4_20211109_090306.954

Cache的直接映射

我們如何確定數據在cache中的位置呢?我們需要引入一種叫做直接映射的方法。

在Cache中為主存的每個字分配一個位置的最簡單方法就是根據這個字的主存地址進行分配,這種Cache結構稱為直接映射。每個存儲器地址對應到Cache中一個確定的地址。我們可以使用以下的映射方法確定:

\[(塊地址)mod(cache中的塊數) \]

如果cache中的塊數是2的冪,那么我們只主要取地址的低\(log_2\)位。因此,一個8塊的cache可以使用塊地址中的低三位

【CSAPP-深入理解計算機系統】6-4. 存儲器層次結構 - 1.6-4(Av805346423,P1).mp4_20211109_090330.216

【CSAPP-深入理解計算機系統】6-4. 存儲器層次結構 - 1.6-4(Av805346423,P1).mp4_20211109_090333.667

對標記的深入理解

標記中包含了地址信息,這些地址信息可以用來判斷cache中的字是否就是所請求的字。標記只需包含地址的高位,也就是沒有用來檢索cache的那些位。

Cache的尋找過程

搞清楚了cache的內部結構以及cache的索引原則,我們就可以開始了解cache的工作流程了,也就是cache是如何尋找對應的數據的。

首先 我們可以通過長度為s的組索引位來確定目標數據存儲在哪個set中,一旦我們知道了目標數據屬於哪個set。接下來 我們需要確定目標數據放在哪一行,確定具體的行是通過長度為t的標記來實現的。不過還需要注意一點 此時有效位必須為1。也就是說需要有效位和標記共同來確定目標數據屬於哪一行。最后 我們需要根據長度為b的塊偏移量來確定目標數據在數據塊中的確切地址。通過以上三步開始就能確定是否命中。

【CSAPP-深入理解計算機系統】6-4. 存儲器層次結構 - 1.6-4(Av805346423,P1).mp4_20211109_090324.823

直接映射cache的工作原理

當每個sit只有一個cache line,也就是e等於1時,我們將這種結構的開始成為直接映射。

【CSAPP-深入理解計算機系統】6-5. 直接映射高速緩存 - 1.6-5(Av462907427,P1).mp4_20211110_160620.388

首先 我們先來看一下直接映射的開始是如何工作的。

判斷是否命中,獲取目標數據的過程一共分為三步:

  1. 組選擇
  2. 行匹配
  3. 字抽取

組選擇

這一步是根據組索引值來確定目標數據屬於哪個set

【CSAPP-深入理解計算機系統】6-5. 直接映射高速緩存 - 1.6-5(Av462907427,P1).mp4_20211110_160629.296

行匹配

而且當前cache line的有效位等於1,此時cache line中的數據是有效的。

【CSAPP-深入理解計算機系統】6-5. 直接映射高速緩存 - 1.6-5(Av462907427,P1).mp4_20211110_160645.934

然后我們需要對比cache line中的標記與地址中的標記位是否一致。如果一致,表示目標數據一定在當前的cache line中。另一方面,如果不一致或者有效位等於零,表示目標數據不在當前的cache line中。因此 行匹配最終的結果無非就是命中或者不命中

【CSAPP-深入理解計算機系統】6-5. 直接映射高速緩存 - 1.6-5(Av462907427,P1).mp4_20211110_160648.346

字抽取

一旦命中,就可以繼續執行第三步--字抽取。這一步需要根據偏移量來確定目標數據的確切位置。通俗來講就是從數據塊的什么位置開始抽取數據。

當塊偏移等於100時,他表明目標數據的起始地址位於字節4處。

【CSAPP-深入理解計算機系統】6-5. 直接映射高速緩存 - 1.6-5(Av462907427,P1).mp4_20211110_160701.780

經過以上三步開始就可以將目標數據返回給CPU,上述過程就是開始命中的情況。

Cache缺失

如果發生了不命中,那么cache需要從存儲器層次結構的下一層取出被請求的塊。由於直接映射的每個sit只包含一行,因此替換策略十分簡單。直接用新取出的行來代替當前的行就可以了。

cache缺失:由於數據不在cache中而導致被請求的數據不能滿足

cache缺失處理由兩部分共同完成:處理器控制單元以及一個進行初始化主存訪問和重新填充cache的獨立控制器。當cache缺失,我們等待主存操作完成時(可以理解為替換),整個處理器阻塞,臨時寄存器和可見的寄存器內容被凍結。

讀操作中的沖突不命中

沖突不命中指的是A與B交替占用了同一個緩存空間,隨着時間的進行,A與B反復對緩存空間中的同一個區域進行替換操作。

我們看一個例子:

【CSAPP-深入理解計算機系統】6-5. 直接映射高速緩存 - 1.6-5(Av462907427,P1).mp4_20211110_160755.562

【CSAPP-深入理解計算機系統】6-5. 直接映射高速緩存 - 1.6-5(Av462907427,P1).mp4_20211110_160813.366

每個元素的長度為四個字節,因此可以得到數組x各個元素的起始地址。數組y緊跟其后,\(y[0]\)的地址從32開始。

當程序開始運行時 循環在第一次迭代時引用了元素\(x[0]\)此時發生不命中,cache把包含\(x[0] - x[3]\)的塊加載到\(set_0\),接下來又立刻引用了數組元素 \(y[0]\)又一次不命中,這時開始把包含\(y[0]-y[3]\)的塊加載到\(set 0\)

這里需要注意的是,之前\(set0\)中存儲的內容是數據塊\(x[0] - x[3]\)的數據。那么 這些數據會被\(y[0]-y[3]\)覆蓋。

實際上 后面每次對x和y的引用都會導致cache line的替換。

我們把這種現象稱為”抖動“

沖突不命中的原因是這些塊被映射到了同一個set中,我們可以將數組的長度由8變為12即可解決問題。

【CSAPP-深入理解計算機系統】6-5. 直接映射高速緩存 - 1.6-5(Av462907427,P1).mp4_20211110_160813.366

此時 數組y的起始地址發生了改變。這樣一來通過這種數據填充的方式就可以消除抖動,從而解決沖突不命中的問題。

組相聯 全相聯高速緩存

之前我們講述的內容才用的是最簡單的定位機制:一個塊只能放到cache中一個明確的位置。實際上,有一整套放置塊的方法。直接映射是一種極端的情況,此時一個塊被精確地放到一個位置。

另一種極端方式是,一個塊可以被放置在cache中的任何位置,這種機制被稱為全相聯,因為存儲器的快可以與cache中任何一項相關。全相聯只適合塊數較少的cache。

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_163846.467

介於直接映射和全相聯之間的設計是組相聯。在組相聯cache中,每個塊可被放置的位置數是固定的。每個塊有n個位置可放的cache被稱為n路組相聯cache。

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_163827.092

組相聯包含存儲塊的組是這樣給出的:

\[(塊號)mod(cache中的組數) \]

由於塊可能被放在組中的任何位置,因此組中所有塊的標記都要被檢索。而在全相聯cache中,塊可能被放在任何位置,所以也都要被檢索。

提高相聯度的好處在於它通常能夠降低缺失率,缺點則是增加了命中時間。

組相聯全相聯查找

組相聯

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_164244.458

組相聯的查找同樣需要執行三步

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_164247.545

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_164255.250

如果找不到符合條件的,cache line表示不命中。此時開始必須從內存中取出包含目標數據的塊,不過一旦開始取出 這個塊應該替換哪一行呢。

如果存在空行 也就是valid等於零的cache line,那么這個空行就是不錯的選擇。

但是 如果這個set中沒有空行,這時我們需要從中選擇一個非空行作為被替換的對象

下面介紹了幾種常用的替換策略:

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_163844.438

最近最少使用法也就是LRU算法,是最常用的方法。

全相聯

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_163846.467

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_163848.286

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_163852.925

這樣一來 地址只需要划分成標記和塊偏移即可。

關於全相聯cache的行匹配和字選擇與組相聯cache是一樣的

寫操作處理

【CSAPP-深入理解計算機系統】6-6. 組相聯 全相聯高速緩存 - 1.6-6(Av250553164,P1).mp4_20211110_165008.752

當CPU需要往內存中寫入數據時,需要考慮寫命中和不命中兩種情況。當發生寫命中時,有兩種策略,分別是寫穿透和寫回。

寫命中

寫直達:也譯為寫通過或寫穿。寫操作總是同時更新cache和下一存儲器層次,以保持二者一致性。

寫穿透是指CPU在寫cache的同時寫內存(更低一級cache),這種策略的好處是內存的數據永遠都是新的,cache替換時,直接扔掉舊的數據就可以。

寫回策略是指CPU只寫開始不寫內存,寫回的好處是寫開始時比較省事,不用關注是否與內存一致,只有當替換算法要驅逐這個更新的塊時,再寫回到內存里。不過,這種策略會增加cache的復雜性

為了表明每個數據塊是否被修改過,每一個cache line需要增加一個額外的修改位。

當發寫不命中時 也有兩種策略 分別是寫分配和寫不分配。

寫不命中

寫分配就是把目標數據所在的塊從內存加載到cache中,然后再往cache中寫。

寫不分配就是繞開cache,直接把要寫的內容寫到內存里

通常情況下,寫分配與寫回搭配使用。寫不分配與寫穿透搭配使用。

第五章的基本內容到此就暫時結束,還有一個重要概念虛擬內存,我會在之后與深入理解計算機系統這一本書一起做總結。


免責聲明!

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



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