本文參考了:

  • courses.cs.washington.edu
  • Cache performance measurement and metric
  • Cache placement policies

簡述

相信你肯定對這一張圖很熟悉了(如果還是第一次聽說就請關掉此頁面吧:))。越靠近 CPU,速度越快,但是容量小且價格昂貴。如何能夠高效利用緩存(LEVEL 1),是操作系統中非常重要的一環。

上篇文章中,我們有講到同一個 CPU 中的 core 之間會對 last-level cache產生競爭,從而影響系統性能。UMA 架構下,假如一個 CPU 裡面有多個 threads 運行在不同 core 中,可以將其中一個 core 上的 thread 移動到其他核,從而緩解了對 last-level cahce 的競爭,達到提高速度的目的(我們也講了這種方法在 NUMA 架構中不適用,具體原因請看上篇文章)。

Last-level cache 競爭給性能帶來影響的原因是顯而易見的,因為 cahce 大小有限,其中一個 thread 佔據一部分 cache,很有可能是會把其他 cache 的擠佔掉的。這種現象是 cache miss中的一種。cache miss 的大小是衡量緩存效率的重要指標。

此篇文章將先講講 cache 一些基礎的知識,後面再逐步深入。

Direct Mapped Cache

下圖中,main memory 有16個 bytes,cache 有4個 bytes。

簡單來講,就是把main memory 中的 0,4,8,12映射到 cache 的0;1,5,9,13映射到 cache 的2… 對,就是取模運算。這回答了我們的第一個問題。

這種方法叫做Direct Mapped Cache,是最簡單的一種 map方法,效率也不是最高的,還有幾種可以通過以下鏈接查看 : https://en.wikipedia.org/wiki/Cacheplacementpolicies

如果要是複雜點,可以看下維基百科上的這張圖,原地址如下: https://en.wikipedia.org/wiki/Cacheplacementpolicies

從 cache 中讀數據

用上面的方法,可以很容易知道 main memory 某個 byte 的數據在 cache 的什麼位置,但是要知道有很多個不同的 block 指向cache 里同一個位置。

可以把 main memory 的最高位作為 tag,存在cache 裡面,如圖:

這樣,比如cache 中一個 entry 的 index 為 01,他的 tag 為 11,對應的 main memory 地址就是 tag+index,即 1101

再進一步,加上一個 valid bit,用來表示該 cache block是否是合法的。

  • 最開始的時候,cache 是空的,所以這個 bit 都是0.
  • 當有數據被 load 到這個 block 的時候,這個 bit 就變成了1.

cache hit 的時候會發生什麼?

我們已經知道該如何通過 main memory 的地址從 cache 中找數據了,如果正如我們所意,確實成功在 cache 中獲取到了,這一段發生了什麼呢?

當 CPU 需要從 memory 中read 數據的時候,地址會發送給 cache controller:

  • address 的最低幾位會對應到 cache 中的某個 block
  • 如何 valid bit 是1,且tag 位是匹配的,就說明cache 成功 hit 了,就可以把data 返回給 CPU 了

如下圖:

cache miss 的時候會發生什麼?

CPU 從cache 中獲取數據可以達到 ns 級,如果是 cache miss 了,就得先把main memory 的數據load 到 cache。 現在 data 已經從 main memory 中讀取到了,通過以下步驟load 到 cache:

  • 低位的 k 位指定了cache block的位置
  • 高位的(m-k)位存在 cache block 的tag 位
  • main memory 的 data 數據拷貝到 cache 的 data field
  • 將 valid bit 置為1

如圖:

再講講cache miss

首先 cache miss 相比 cache hit 的性能差異是數量級的,應該儘可能減少 cache miss 的幾率。cache miss 有很多種原因,這裡講最主要的兩種,詳細列表請看: https://en.wikipedia.org/wiki/Cacheperformancemeasurementandmetric

  • 無法避免的 miss:所有的 cache 最開始肯定都是不在 cache 中的,所以這必然需要先從 main memory 中 load 進來。(除非試用 prefetch: en.wikipedia.org/wiki/C
  • 衝突導致 miss:這種 miss 之前該數據已經在 cache 裡面,但是被另外的程序搶佔了。(回顧一下上篇文章講的 last-level cache 競爭~這種可以通過調度來解決)

Spatial locality

前面都是一個 byte 為一個 block 的。我們有一個這樣的假設,訪問一個 address,下一次訪問很有可能會訪問臨近的某一個 address。這個假設大多數情況下都是正確的。這個假設就叫做Spatial locality,那該如何實現這種假設?可以增加每個block的大小。

現在將每個 cache block 的大小設置為2個 bytes,於是我們就可以一次性 load 兩個 bytes 的數據。當我們要 load 位置12的數據到 cache 的時候,同時也會把位置13的也 load 過去。

這時候,可以把 main memory 也劃分成兩個 byte 一個 block。byte address 和 block address 的對應關係也很簡單,0和1對應一個 block address: 0

導入 cache 的過程也很之前一樣,沒什麼好贅述的:現在從 main memory 中 load 第12或者第13個byte,都會同時12,13這兩個 byte load 過去。

總結

Cache資源對於整個系統性能的影響巨大,關於如何設計 cache,調度 cache 資源的方法、論文層出不窮。此文講的Direct Mapped Cache是最最簡單的,但是對於我們理解 cache 有很大幫助。後面我們還會繼續深入學習一下 cache。

最後,gakki 式求贊~

廣告時間,歡迎大家關注我的微信公眾號。同時本文同步於 github: github.com/liaochangjia

推薦閱讀:

相关文章