程式設計師的 CPU 微架構

記憶體階層(memory hierarchy)

想像一位廚師:手邊有一塊很小的砧板、一個小流理台、房間另一頭的儲藏室,以及城市另一端的倉庫。砧板伸手就到,卻幾乎放不了東西;倉庫什麼都有,但每跑一趟都很慢。好廚師會把此刻正在用的東西放在砧板與流理台上,只在極少數時候才走去倉庫。你的中央處理器面對的正是這個取捨,而記憶體階層就是它的答案:好幾層儲存,每一層都比上一層更大也更慢。

由最快最小到最慢最大,這些層分別是:暫存器(幾百個位元組,讀取遠少於一奈秒,幾乎免費)、L1 快取(每核心通常 32 到 64 KiB,約 4 個週期、大致 1 ns)、L2 快取(256 KiB 到數 MiB,約十幾個週期)、L3 或最後一層快取(數到數十 MiB,多核共用,數十個週期),最後是主記憶體或 DRAM(數 GiB,但離你有 60 到 100 多奈秒——遠超過一百個週期)。每往下一階大致就慢一個數量級。中央處理器不讓你挑層級:它會自動把近期用過、或位置相鄰用過的資料留在快取裡,只有在快取失誤時才去 DRAM 取。

這對你的程式為何重要:L1 命中與 DRAM 存取之間的差距,是多數程式所會跌落的最大一道效能懸崖。一顆每奈秒能發出好幾道指令的處理器,可能為了等一次 DRAM 讀取而停滯相當於數百道指令的時間。這正是區域性(locality of reference)取勝的原因——觸碰已在快取中的記憶體、或觸碰剛碰過資料附近的程式,可能比外觀一模一樣卻存取分散的程式快上好幾倍。上面的數字是典型值而非精確值;它們因晶片而異,但那個形狀——陡峭的延遲梯度——是普遍的。

把一個 1 GiB 的陣列循序加總(arr[0]、arr[1]、arr[2]、…)會比以隨機順序加總同一個陣列快上好幾倍,即使兩者都恰好觸碰每個元素一次。循序走訪命中快取與預取器;隨機走訪在多數存取上都得付出 DRAM 延遲。

相同的工作量、相同的元素數——光是存取模式就決定了你得為階層中的哪一層付費。

這個階層是自動且隱形的:沒有任何指令能「把這個放進 L1」。你只能間接影響它,靠你的資料佈局與存取順序——這也正是「機械同理心」會有回報的原因。

又称
the memory pyramidaccess-latency gradient記憶體金字塔