高效能與平行計算

記憶體階層(memory hierarchy)

想像一位廚師在一塊很小的砧板上工作。砧板上的食材伸手就拿到;流理台上一臂之遙的東西稍微花點時間;房間另一頭冰箱裡的得真的走一趟;地下室冷凍庫裡的任何東西都意味著一段長路。電腦的記憶體也以同樣方式組織,是一座越離處理器遠就越大、越慢的儲存階層。這道階梯——從暫存器、到快取、到主記憶體、到磁碟——是「為何快的程式快」這件事最重要的單一事實。

具體而言各層為:核心內部數十個暫存器(不到一奈秒,那塊砧板);L1 快取(數十 KB,約 1 奈秒);L2 快取(數百 KB);L3 快取(數 MB 到數十 MB,核心共享,約 10-20 奈秒);主記憶體或 DRAM(數 GB,約 100 奈秒,那地下室);以及磁碟或 SSD(數 TB,微秒到毫秒)。每往下一階大約大一個數量級、慢一個數量級。硬體會自動把最近用過的資料留在小而快的層,只在不得已時才退回慢層,但它無法讀你的心——它賭你剛碰過的資料及其鄰居很快會再被用到。

這對數值計算之所以重要,原因是赤裸裸的算術。現代核心在從主記憶體取一個數的時間內,可做數十次浮點運算。所以如果你的演算法把巨大陣列串流過處理器、每個值只碰一次,處理器幾乎全部時間都在等、不在算——浮點運算數變得無關緊要,頻寬才是真正的瓶頸。因此設計快速數值程式,重點不在數乘法次數,而在安排工作,使資料一旦被搬上快取,就在被逐出前被重複使用許多次。局部性而非浮點數,才是現代的貨幣。

從暫存器讀一個值約耗 1 個週期;從 L1 約 4 個週期;從 L3 約 40 個週期;從主記憶體約 200-300 個週期。所以一次主記憶體存取的代價可抵 100 次浮點乘法——這正是為何一個不斷快取失誤的程式,可能比相同浮點運算數但從快取執行慢上 10 到 100 倍。

階層每往下一階約大 10 倍、慢 10 倍;一次 DRAM 存取可抵 100 次浮點運算。

硬體替你管理快取,所以階層大致是隱形的——這正是陷阱所在。兩個浮點運算數完全相同的演算法,僅因走訪記憶體的方式不同,實際牆鐘時間就可能差 50 倍;忽略快取行為的效能分析會誤導你。

又称
memory pyramidstorage hierarchy記憶體金字塔儲存階層