跳轉到

1969 - 快取記憶體:記憶體階層的誕生#

案件摘要#

1969 年,IBM System/360 Model 85 量產——第一台配備 cache(高速緩衝記憶體)的商用電腦。 CPU 與 DRAM 的速度鴻溝擋在中間,cache 以「小而快、擋在最前」化解: 靠局部性原理(剛用過的資料還會再用、旁邊的資料也會用到),把平均存取時間壓到接近 CPU 的速度。 此後 L1/L2/L3 階層成為每一顆處理器的標準配備。

前因 -- 為什麼會有這個案子#

  • 速度鴻溝:CPU 週期以數十 ns 計,core memory 與 DRAM 更慢——CPU 大半時間在等記憶體。
  • 大而快的記憶體太貴:SRAM 快 10 倍但貴 10 倍,全機用 SRAM 等於燒錢。
  • 局部性原理(Denning, 1968):程式的存取集中在少數位址區——這是 cache 可行的科學根據。
  • 關鍵推理:把少數資料放進快而貴的 SRAM,其餘留在慢而便宜的 DRAM:
\[ \text{AMAT} = t_{\text{hit}} + \text{miss rate} \times t_{\text{miss}} \]

只要 hit rate 夠高(局部性保證),AMAT 就貼近 \(t_{\text{hit}}\) ——記憶體階層自此誕生。

線索與推理 -- 數學式、程式、理論#

1. 案件現場:cache 的組織#

主記憶體切成固定大小的 block(line,如 64B),cache 決定每個 block 放哪:

組織 映射 特點
Direct-mapped \(idx = (\text{addr} / B) \bmod C\) 一對一、最簡單、衝突多
Set-associative 每組 \(E\) 路( \(C = S \times E\) ) 折衷、現代標準(E = 4–16)
Fully-associative 任意放 零衝突、最貴、只用於 TLB
  • hit:位址對應的組中有 tag 相符且 valid 的行——命中。
  • miss:沒有——要從下層搬整條 line 進來(可能先逐出別的行)。

2. AMAT 與記憶體牆#

\[ \text{AMAT} = t_{L1} + m_{L1}\big(t_{L2} + m_{L2}\,t_{\text{mem}}\big) \]
  • 局部性讓 \(m_{L1}\) (L1 miss rate)壓到 2–5%,AMAT 貼近 L1 速度。
  • 但 CPU 變快 50%/年、DRAM 只變快 7%/年——processor-memory gap 持續擴大:
\[ \text{memory wall} = \frac{\text{CPU 速度增長}}{\text{DRAM 速度增長}} \to \infty \ \text{(若不加深階層)} \]

多層 cache(L2/L3)、prefetch、頻寬擴充——記憶體牆戰役從 1969 打到今天。

3. 寫入策略與三 C 模型#

  • write-through:寫 cache 同時寫下層——簡單但頻寬消耗大。
  • write-back:只寫 cache、逐出時才寫回(dirty bit 標記)——省頻寬、現代標準。
  • 三 C miss 模型:compulsory(第一次必然 miss)、capacity(cache 裝不下)、conflict(映射撞車)——診斷 miss 來源的工具。

4. 局部性原理:階層的科學根據#

  • 時間局部性:剛存取的資料短期內會再存取(迴圈變數)。
  • 空間局部性:存取某位址後,鄰近位址也會被存取(陣列、連續指令)。
  • working set(Denning, 1968):程式在時間窗口內活躍的頁集合——cache 容量只要蓋住 working set 就能高命中。

5. Python 實作:cache 模擬#

def simulate(addr_seq, cache_lines, ways, block=4):
    sets = cache_lines // ways
    cache, hits, misses = {}, 0, 0
    for a in addr_seq:
        idx, tag = (a // block) % sets, (a // block) // sets
        lines = cache.setdefault(idx, [])
        if tag in lines:                        # hit
            hits += 1; lines.remove(tag); lines.insert(0, tag)   # LRU 更新
        else:                                   # miss
            misses += 1
            if len(lines) >= ways: lines.pop()  # 逐出 LRU 行
            lines.insert(0, tag)
    rate = hits / (hits + misses)
    return hits, misses, rate

addr = [0,4,8,0,8,16,4,20,0,24,8,0,12,4,28,0]   # 存取序列(block=4 words)
for name, lines, ways in [('direct-mapped', 4, 1), ('2-way', 4, 2), ('4-way', 4, 4)]:
    h, m, r = simulate(addr, lines, ways)
    print(f"{name:14s}: hit={h:2d} miss={m:2d} hit rate={r:.0%}")
# direct-mapped : hit= 7 miss= 9 hit rate=44%
# 2-way         : hit= 9 miss= 7 hit rate=56%
# 4-way         : hit=10 miss= 6 hit rate=62%
# (相聯度越高衝突越少——但硬體成本也越高)

def amat(t1, m1, t2, m2, tm):
    return t1 + m1 * (t2 + m2 * tm)
print(f"AMAT(L1 1ns/95%, L2 12ns/80%) = {amat(1, .05, 12, .20, 100):.1f} ns")
# AMAT = 1 + 0.05*(12 + 0.2*100) = 2.6 ns  ← 遠低於 DRAM 的 100ns

結案 -- 後果與影響#

  • 記憶體階層成為標準:L1/L2/L3、TLB、分頁系統——所有電腦的記憶體都是多層金字塔。
  • 局部性成為設計準則:演算法的 cache-friendly 設計(blocking、循環交換)、prefetch 指令。
  • 記憶體牆至今未解:gap 持續擴大,HBM、3D 堆疊、CXL 都在續命。
  • 理論延續:cache coherence(多核心一致性的戰場)、Spectre(cache side-channel 的教訓)。

關鍵人物與文獻#

  • Peter Denning:working set 模型與局部性原理, ACM Computing Surveys, 1968/1970。
  • IBM System/360 Model 85(1969):第一台商用 cache 機器。
  • Maurice Wilkes(1965):最早提出 slave memory(cache 的概念先驅)。
  • J. Hennessy, D. Patterson, Computer Architecture: A Quantitative Approach, 1990——三 C 模型與 AMAT 的標準教材。
  • 相關案件:1962-虛擬記憶體.md、1964-CDC6600超級電腦.md、2007-多核心CPU.md。