Hotball 的小屋

Carved Marker

CPU 的 cache 和 latency [Part 1]

這篇文章主要是探討現在的 CPU 的 cache 和記憶體系統之間的關係。

CPU 速度的進展,一直比記憶體的速度進展要來得快。在 IBM PC XT 的時代,CPU 和記憶體的速度是差不多的。不過,後來 CPU 的速度就愈來愈快。再加上 DRAM 需要 refresh 才能保存資料的特性,DRAM 很快就跟不上 CPU 的速度了。現在的 CPU 都利用了 pipeline 的方式,可以每個 cycle 都 issue 一個(甚至多個)指令,再加上現在的 CPU 時脈也比記憶體的時脈高,記憶體的速度可說是遠遠落在 CPU 之後了。

為了避免記憶體成為 CPU 速度的瓶頸,現在的 CPU 都有 cache 的設計,甚至還有多層的 cache。Cache 的原理,主要是利用到大部分的程式,在處理資料時,都有一定程度的區域性。所以,我們可以用一小塊快速的記憶體,來暫存目前需要的資料。

例如,幾乎所有的程式,大部分的執行時間是花在一些迴圈中。這些迴圈通常都不大,可能只佔整個程式空間的百分之一。如果一個程式經常要執行這段程式數千、甚至數萬次,那就可以把這一小段程式放在 cache 中,CPU 就不需要每次都到很慢的主記憶體中讀取這段程式了。很多一般用途的程式,在存取資料時,也有類似的特性。因此,cache 的幫助非常大。如果沒有 cache 的話,我們就不需要這麼快的 CPU 了,因為系統的速度會卡在記憶體的速度上面。

現在的 CPU 往往也有多層的 cache。例如,Intel 的 Pentium III 500Mhz CPU,有 32KB 的 L1 cache,和 512KB 的 L2 cache。其中,L1 cache 內建在 CPU 內部,速度非常快,而且它是 Harvard 式,即指令用的空間和資料用的空間是分開的。Pentium III 500Mhz CPU 的 L1 cache 是分成 16KB 的 I-cache 和 16KB 的 D-cache。而 L2 cache 則是在 CPU 外面,以 250Mhz 的速度運作。另外,它和 CPU 之間的 bus 也只有 64 bits 寬。L2 cache 通常就不會區分指令和資料的空間,也就是 unified cache。

Cache 對速度有什麼影響呢?這可以由 latency 來表示。CPU 在從記憶體中讀取資料(或程式)時,會需要等待一段時間,這段時間就是 latency,通常用 cycle 數表示。例如,一般來說,如果資料已經在 L1 cache 中,則 CPU 在讀取資料時(這種情形稱為 L1 cache hit),CPU 是不需要多等的。但是,如果資料不在 L1 cache 中(這種情形稱為 L1 cache miss),則 CPU 就得到 L2 cache 去讀取資料了。這種情形下,CPU 就需要等待一段時間。如果需要的資料也不在 L2 cache 中,也就是 L2 cache miss,那麼 CPU 就得到主記憶體中讀取資料了(假設沒有 L3 cache)。這時候,CPU 就得等待更長的時間。

另外,cache 存取資料時,通常是分成很多小單位,稱為 cache line。例如,Pentium III 的 cache line 長度是 32 bytes。也就是說,如果 CPU 要讀取記憶體位址 0x00123456 的一個 32 bits word(即 4 bytes),且 cache 中沒有這個資料,則 cache 會將 0x00123440 ~ 0x0012345F 之間的 32 bytes 資料(即一整個 cache line 長度)都讀入 cache 中。所以,當 CPU 讀取連續的記憶體位址時,資料都已經讀到 cache 中了。

我寫了一個小程式,用來測試 cache 的行為。這個程式會連續讀取一塊記憶體位址,並量測平均讀取時間。這個 程式的執行結果如下:

測試平台:

  • Pentium III 500Mhz, PC100 SDRAM, 440BX chipset
  • Celeron 466Mhz, PC100 SDRAM, VIA Apollo Pro 133 chipset
Latency result

程式的執行檔和原始碼可在這裡下載。

由上面的結果可以看出,當測試的區塊大小在 16KB 以下時,平均的 latency 都在 1 ~ 3 cycles 左右。這顯示出 16KB 的 L1 D-cache 的效果。在測試區塊為 1KB 和 2KB 時,因為額外的 overhead 較高,所以平均的 latency 變得較高,但是在 4KB ~ 16KB 的測試中,latency 則相當穩定。在這個範圍中,由於 Pentium III 和 Celeron 有相同的 L1 cache,所以測試結果是幾乎完全相同的。

在區塊超過 16KB 之後,就沒辦法放入 L1 D-cache 中了。但是它還是可以放在 L2 cache 中。所以,在 Pentium III 的情形下,從 32KB ~ 512KB,latency 都在 10 cycles 左右。這顯示出當 L1 cache miss 而 L2 cache hit 時,所需要的 latency。而 Celeron 的 L2 cache 只有 128KB,但是 Celeron 的 L2 cache 的 latency 則明顯的比 Pentium III 為低。這是因為 Celeron 的 L2 cache 是 on-die,以和 CPU 核心相同的速度運作。而 Pentium III 的 L2 cache 則是分開的,且以 CPU 核心速度的一半運作。

在區塊超過 512KB 之後,L2 cache 就不夠大了(Pentium III 500Mhz 只有 512KB 的 L2 cache)。這時,顯示出來的就是 L1 cache miss 且 L2 cache miss 時,所需要的 latency。在 1024KB 或更大的區塊中,Pentium III 的 latency 都大約是 28 cycles 左右,而 Celeron 的 latency 則超過 70 cycles。這是 CPU 讀取主記憶體時,平均的 latency。而 Celeron 的 latency 較高,應該是因為其外頻較低,而倍頻數較高的緣故(Pentium III 500Mhz 為 5 倍頻,而 Celeron 466 為 7 倍頻)。另外,晶片組的差異也可能是原因之一。

Cache 的效果十分明顯。不過,有時候 cache 是派不上用場的。例如,當資料完全沒有區域性,或是資料量太大的時候,都會讓 cache 的效果降低。例如,在進行 MPEG 壓縮時,存取的資料量很大,而且資料的重複利用率很低,所以 cache 的幫助就不大。另外,像是 3D 遊戲中,如果每個 frame 的三角面個數太多,也會超過 cache 能夠處理的範圍。

現在的電腦愈來愈朝向「多媒體應用」,需要處理的資料量也愈來愈大,因此,要如何善用 cache 就成了一個重要的問題。一個非常重要的方法,就是把讀取主記憶體的 latency 和執行運算的時間重疊,就可以把 latency「藏」起來。通常這會需要 prefetch 的功能,也就是 AMD 在 K6-2 及之後的 CPU,和 Intel 在 Pentium III 之後的 CPU 加入的新功能。在下一篇文章中,我們會討論 prefetch 的原理和用途。

[Part 1] [Part 2]

5/20/2000, Ping-Che Chen


Sorry, Traditional Chinese only. This page is encoded in UTF-8.

Copyright© 2000 Ping-Che Chen