區域性原理(principle of locality)
看看你實際怎麼用廚房。一旦你伸手拿鹽,接下來幾分鐘你很可能又會拿它(你正在調味),而且你還會拿放在鹽旁邊的東西——胡椒、油。你不是從整個儲藏室裡均勻隨機挑食材;你的取用在時間上、在空間上都成群聚集。區域性原理就是觀察到:程式觸碰記憶體的方式也同樣成群聚集,而這一個事實,正是快取得以運作的根本原因。
它有兩種樣貌。時間區域性:若程式存取某個記憶體位置,它很可能很快又會存取同一個位置(想想迴圈計數器、被頻繁呼叫的函式、每次迭代更新的變數)。空間區域性:若程式存取某個位置,它很可能很快會存取附近的位置(想想逐元素走過一個陣列,或執行相連的指令)。兩者都是傾向,不是定律——但它們在真實程式碼中成立得夠強,硬體因此敢於押注,而且通常押對。
正因為有區域性,一塊只裝著最近用過與鄰近資料的小型快速快取,就能滿足絕大多數存取,即使快取比主記憶體小上千倍。空間區域性正是快取以整塊(快取列)而非單一位元組搬資料的原因——一次抓回一整片鄰域是划算的。時間區域性則是我們把最近用過的資料留著、而非丟掉的原因。誠實的提醒:程式碼也可能寫得區域性很差(隨機亂跳、迴圈順序錯誤),那時快取幫不上多少忙。區域性是程式設計者可以賺到、也可以揮霍掉的東西。
用「for i: total += a[i]」對陣列求和同時展現兩者:total 每次迭代都被重用(時間),而 a[i]、a[i+1]、a[i+2]… 在記憶體中相鄰(空間)。一次快取列提取就帶進好幾個未來的 a[i] 值,於是大多數迭代都命中快取。
真實程式很快重用同一筆資料,並把鄰近資料一起用掉——這正是快取所押的賭注。
區域性是典型程式的「傾向」,不是對每個程式的保證。存取模式真正隨機的程式碼(例如追逐四散的指標、雜湊進一張巨大表格)幾乎沒有區域性,再聰明的快取也救不了它。