記憶體階層與快取

快取/快取記憶體(cache)

/ cash /

想像你身邊擺著一張很小的桌子,上面只放你正在使用的那幾本書。完整的圖書館在城的另一頭、跑去很慢,但只要你要的書已經在小桌上,你就立刻拿到。當你需要一本不在桌上的書,你就跑一趟圖書館,把它帶回來放上桌——並擠掉你最久沒碰的那一本。快取正是這樣:一份較大、較慢記憶體中最近用過那幾塊的小型快速副本。

硬體快取坐落在 CPU 與主記憶體之間,存放最近存取過的記憶體塊(快取列)的副本。每次記憶體存取,硬體都先查快取。若資料在裡面就是命中,存取很快;若不在就是未命中,必須從較慢的層級把資料抓來(付出未命中代價),再存進快取,好讓下次存取它時很快。由於程式有區域性,絕大多數存取都是命中,於是平均存取時間降到接近快取那個快速的命中時間,即使快取只裝著記憶體的極小一部分。

關於快取最重要的一條誠實話:它是自動的,而且對「正確性」完全隱形。無論快取幫了大忙還是毫無幫助,你的程式都產生一模一樣的結果——快取只改變速度,從不改變答案。但這速度差異極大:區域性良好、對快取友善的程式碼,可能比對快取不友善、卻算出相同結果的程式碼快上好幾倍。所以快取最好被視為一項你必須靠區域性「賺來」的效能特性,而非免費的禮物。現代 CPU 正因如此而有好幾層快取(L1、L2、L3)。

讀位址 A:未命中,於是包含 A 的快取列從 DRAM 抓來(約 80 奈秒)並存進快取。再讀 A,以及和它一起進來的鄰居:都命中(各約 1 奈秒)。第一次存取付了代價;其餘的靠區域性免費搭便車。

最近用過記憶體的小型快速副本;命中很快,未命中要付代價回填,區域性讓命中變得常見。

快取從不改變你的程式算出什麼——只改變多快。兩次快取行為差很多的執行給出相同結果。這就是為什麼快取問題表現為神祕的變慢,而非錯誤答案(在單一核心內;跨核心共享是另一主題——一致性)。

又稱
CPU cache快取