區域性與記憶體階層 (Locality & the Memory Hierarchy)

Overview Table

主題 核心概念 書頁
區域性原理 (principle of locality) 程式傾向重複引用最近用過或鄰近的資料 p.640
Temporal locality 同一位置短期內被重複引用 p.640
Spatial locality 引用某位置後,很快引用其鄰近位置 p.640
Stride-k 引用模式 stride 越小,spatial locality 越好;stride-1 最佳 p.642
指令擷取的區域性 迴圈本體:循序執行(spatial)+ 重複執行(temporal) p.643
記憶體階層 (memory hierarchy) L0 registers → L1–L3 SRAM cache → L4 DRAM → L5 disk → L6 remote p.645-646
階層式快取 第 k 層是第 k+1 層的 cache;以 block 為傳輸單位 p.646-647
Cache hit / miss hit 直接讀第 k 層;miss 從 k+1 層搬 block、可能 evict victim block p.648
Miss 三分類 cold(compulsory)/ conflict / capacity miss p.648-649
Cache 管理 各層由 compiler / 硬體 / OS+硬體 / 應用程式等不同邏輯管理 p.649-650

6.2 區域性 (Locality) (p.640-641)

寫得好的程式傾向展現良好區域性 (locality):引用的資料項靠近最近引用過的項目,或就是剛引用過的項目本身。此區域性原理 (principle of locality) 深刻影響硬體與軟體系統的設計與效能,具有兩種形式:

各層級如何利用區域性:

層級 利用區域性的方式
硬體 cache memory(小而快的 SRAM)保存最近引用的指令與資料 block,加速 main memory 存取
作業系統 用 main memory 作為 virtual address space 最近引用區塊的 cache;也用 main memory 快取磁碟檔案系統的 disk block
應用程式 Web browser 把最近瀏覽的文件快取在本地磁碟;高流量 Web server 用 front-end disk cache 直接回應熱門文件,不需 server 介入

背景(p.640, Figure 6.16):CPU 與 DRAM/disk 的速度差距(processor–memory gap)逐年擴大,現代電腦大量使用 SRAM-based cache 來彌補——這個做法之所以有效,正是因為程式的區域性。

Aside:cycle time 停滯與 multi-core(p.641)

21 世紀初廠商撞上 power wall:功耗 P=fCV2(f 時脈頻率、C 電容(約與面積成正比)、V 電壓),無法再快速拉高時脈,改以多核心換取效能——只要總面積不變,多核的功耗可維持恆定。CPU cycle time 在 2003 年觸底後甚至回升,但因 multi-core(2004 dual-core、2007 quad-core),effective cycle time 仍以接近原速率下降。從此電腦變快靠的是核心數與架構創新,而非時脈。

6.2.1 程式資料引用的區域性 (p.642-643)

sumvec(循序加總向量)為例,判斷函式區域性的方法是逐一檢視每個變數的引用模式:

Stride 引用模式:

二維陣列的迴圈順序決定 stride(C 陣列以 row-major order 儲存):

函式 迴圈順序 引用模式 Spatial locality
sumarrayrows i 外、j 內(逐列掃描) stride-1,與儲存順序一致
sumarraycols j 外、i 內(逐行掃描) stride-N(N 為列寬),在記憶體中跳躍
記憶體佈局 (int a[2][3], row-major, 每元素 4 bytes):

Address:    0     4     8     12    16    20
Content:  a00 | a01 | a02 | a10 | a11 | a12

sumarrayrows 訪問順序:  1  2  3  4  5  6   (stride-1, 循序)
sumarraycols 訪問順序:  1  3  5  2  4  6   (stride-N=3, 跳躍)
應排成 j 外、k 中、i 內)。

6.2.2 指令擷取的區域性 (p.643-644)

程式指令也儲存在記憶體、須由 CPU 擷取 (fetch),因此同樣可以評估程式對指令擷取的區域性:

6.2.3 區域性判斷準則總結 (p.644)

定性評估程式區域性的簡單規則:

  1. 重複引用相同變數的程式有好的 temporal locality。
  2. stride-k 引用模式中,stride 越小 spatial locality 越好;stride-1 最佳;在記憶體中大步跳躍的程式 spatial locality 差。
  3. 就指令擷取而言,迴圈天生有好的 temporal 與 spatial locality;迴圈本體越小、迭代次數越多,區域性越好
6.4 節之後會用 cache hit/miss 把區域性量化,並解釋為何區域性好的程式跑得快;但光看原始碼就能對區域性有高層次的感覺,是程式設計師必備技能。

6.3 記憶體階層 (The Memory Hierarchy) (p.645-646)

兩個基本且持久的事實恰好互補,催生了所有現代系統採用的記憶體階層 (memory hierarchy):

                 ^  L0:  Registers            CPU 單一 clock cycle 存取
  Smaller,       |  L1:  L1 cache (SRAM)      數個 cycle;存放來自 L2 的 cache line
  faster,        |  L2:  L2 cache (SRAM)      存放來自 L3 的 cache line
  costlier/byte  |  L3:  L3 cache (SRAM)      存放來自 main memory 的 cache line
                 |  L4:  Main memory (DRAM)   數十~數百 cycle;快取本地磁碟的 disk block
  Larger,        |  L5:  Local disks          存放來自遠端伺服器的檔案
  slower,        |  L6:  Remote storage       分散式檔案系統 (AFS/NFS)、Web servers
  cheaper/byte   v
Figure 6.21 只是「一種」典型階層,其他組合也很常見(p.647 Aside):Google 資料中心等以磁帶 (tape) 備份本地磁碟,構成 local disk 之下的另一層——磁帶每 byte 更便宜(可存多份快照),代價是存取更慢;SSD 則日益扮演橋接 DRAM 與旋轉磁碟鴻溝的角色。同樣的通則適用於所有層。

6.3.1 記憶體階層中的快取 (Caching) (p.646-648)

Cache(讀作 "cash")是小而快的儲存裝置,作為較大、較慢裝置中資料物件的暫存區 (staging area);使用 cache 的過程稱為 caching

中心思想:對每個 k,第 k 層較快較小的裝置,作為第 k+1 層較大較慢裝置的 cache——每一層都快取下一層的資料物件。例:local disk 快取遠端磁碟的檔案(如 Web pages)、main memory 快取 local disk 的資料……直到最小的 cache——CPU registers。

Level k:      +----+----+----+----+   較小、較快、較貴的裝置,
   (cache)    |  4 |  9 | 14 |  3 |   快取 level k+1 的「部分」block
              +----+----+----+----+
                     ^    |
        以 block 為單位雙向傳輸 (block-size transfer units)
                     |    v
              +----+----+----+----+
Level k+1:    |  0 |  1 |  2 |  3 |   較大、較慢、較便宜的裝置,
              +----+----+----+----+   被劃分成 block(此例 16 個
              |  4 |  5 |  6 |  7 |   固定大小 block,編號 0..15)
              +----+----+----+----+
              |  8 |  9 | 10 | 11 |
              +----+----+----+----+
              | 12 | 13 | 14 | 15 |
              +----+----+----+----+

Cache hit(p.648):程式需要第 k+1 層的資料物件 d 時,先在第 k 層目前存放的 block 中找;若 d 已被快取,即為 cache hit,直接從較快的第 k 層讀取。

Cache miss(p.648):若 d 不在第 k 層,即為 cache miss。第 k 層從第 k+1 層取回含 d 的 block,若 cache 已滿則需覆寫既有 block:

程式請求資料 d
      |
      v
 d 在 level k? ----是----> Cache HIT:直接從 level k 讀 d(快)
      |
      否
      v
 Cache MISS:向 level k+1 取回含 d 的 block
      |
      v
 level k 已滿? --是--> 依 replacement policy 選 victim block 並 evict
      |                          |
      否                         v
      +------------------> 依 placement policy 放入 block
                                 |
                                 v
                    程式從 level k 讀 d;block 留下等待後續 hit

Miss 的種類(p.648-649):

Miss 類型 成因 特徵
Cold miss(compulsory miss) cache 是空的(cold cache),任何存取必 miss 暫態現象,cache 被重複存取warmed up 後通常不再發生
Conflict miss 限制性 placement policy 使多個 block 映射到同一 cache block cache 容量其實裝得下,但目標 block 互相踢來踢去、持續 miss
Capacity miss working set 大小超過 cache 容量 cache 就是太小,裝不下這個 phase 的工作集

Cache 管理(p.649-650):每一層都要有某種邏輯管理 cache——切割 block、在層間搬移、判斷 hit/miss 並處理;管理者可以是硬體、軟體或兩者組合:

Cache 層 管理者
Register file(cache 階層最高層) Compiler(決定 miss 時何時發出 load、資料放哪個暫存器)
L1 / L2 / L3 cache 完全由內建於 cache 的硬體邏輯管理
Main memory(virtual memory 下作為磁碟資料的 cache) OS 軟體 + CPU 上的 address translation 硬體
Local disk(AFS 分散式檔案系統下) 本機上執行的 AFS client process

多數情況下 cache 自動運作,程式不需做任何明確或特定的動作。

6.3.2 記憶體階層概念總結 (p.650)

基於快取的記憶體階層之所以有效,因為慢的儲存比快的便宜,且程式傾向展現區域性:

Figure 6.23:快取在現代系統中無所不在(由快至慢):

Type 快取什麼 存放在哪 Latency (cycles) 管理者
CPU registers 4/8-byte words on-chip registers 0 Compiler
TLB address translations on-chip TLB 0 Hardware MMU
L1 cache 64-byte blocks on-chip L1 4 Hardware
L2 cache 64-byte blocks on-chip L2 10 Hardware
L3 cache 64-byte blocks on-chip L3 50 Hardware
Virtual memory 4-KB pages main memory 200 Hardware + OS
Buffer cache 檔案片段 main memory 200 OS
Disk cache disk sectors disk controller 100,000 Controller firmware
Network cache 檔案片段 local disk 10,000,000 NFS client
Browser cache Web pages local disk 10,000,000 Web browser
Web cache Web pages remote server disks 1,000,000,000 Web proxy server
TLB、virtual memory 等詞在此僅為展示快取的普遍性而列出,詳見 09-Virtual-Memory/03-Address-Translation-and-TLB09-Virtual-Memory/01-Address-Spaces-and-VM-Caching

Exam/Test Patterns

情境 / 關鍵字 答案
「同一變數在迴圈中每次迭代都被引用」屬於哪種區域性? Temporal locality(scalar 無 spatial locality)
「循序走訪陣列元素」屬於哪種區域性? Spatial locality(stride-1;每元素只讀一次,temporal 差)
判斷函式整體區域性的方法 逐一分析每個變數的引用模式,再綜合判斷
交換二維陣列迴圈 i、j 的影響 row-major 下逐行掃描變成 stride-N,spatial locality 變差
多維陣列如何得到 stride-1?(P6.7 型) 重排迴圈使最右邊的索引由最內層迴圈變動最快(a[j][k][i] → j 外、k 中、i 內)
比較多個函式的 spatial locality(P6.8 型) 畫出記憶體佈局、比 stride:clear1(stride-1)>clear2(struct 內以 offset 0,12,4,16,8,20 跳動)>clear3(struct 內外都跳)
迴圈的指令擷取區域性 循序執行 → spatial 好;重複執行 → temporal 好;本體越小、迭代越多越好
「cache 容量夠大卻反覆 miss」 Conflict miss(restrictive placement 使 block 映到同一位置,如 0、8 交替)
「空 cache 的第一次存取」 Cold / compulsory miss(暫態;warmed up 後消失)
「working set 超過 cache 大小」 Capacity miss
LRU replacement policy 逐出誰? 最久未被存取的 block;random 策略則隨機挑 victim
誰管理 register file / L1-L3 / main memory(VM)/ AFS local disk? Compiler / 硬體 / OS + address translation 硬體 / AFS client process
為何階層低層用較大的 block? 存取時間長,用大 block 攤銷 (amortize) 存取成本
為何時脈頻率不再上升?(power wall) P=fCV2;改用 multi-core 使 effective cycle time 持續下降
範例 placement policy:block i 放哪? 第 k+1 層 block i 映到第 k 層 block (i mod 4)
記憶體階層為何行得通? 慢儲存較便宜 + 程式有區域性(temporal → 重複 hit;spatial → block 內攤銷)