區域性/局部性(locality of reference)
觀察你實際上是怎麼用一本食譜的。你不會在全部 400 頁裡隨機亂翻;你會在一道食譜上停留一陣子,在它那幾頁之間來回瞄,然後移到下一道食譜並安頓在那裡。而你接下來要讀的頁,通常離你正在看的這一頁很近——同一道食譜橫跨相鄰的頁。這種習慣——你的注意力在時間上與空間上聚成一團,而非均勻散開——就是區域性。程式在觸碰記憶體時的行為完全一樣。
具體來說,區域性有兩種風味。時間區域性說的是:若程式現在參考了某個位置,它很可能很快又會再參考同一個位置——一個迴圈計數器、一個熱門變數、一個被反覆呼叫的函式。空間區域性說的是:若程式參考了某個位置,它很可能很快會參考鄰近的位置——逐一走過一個陣列的元素,或依序執行指令。兩種區域性都意味著:在任何一段短時間窗內,程式只觸碰它全部頁中一個小的、緩慢移動的子集,而不是把參考噴灑到整個位址空間。
為什麼重要:區域性是需求分頁之所以能運作、而非成為災難的唯一原因。如果一個程式隨機而均勻地參考它的頁,幾乎每一次存取都會碰到一個不在常駐的頁而發生錯誤,有效存取時間就會崩塌到磁碟速度。正因為真實的程式具有強烈的區域性,它們當前正在使用的那少數幾頁可以常駐於 RAM,暖機之後錯誤就變得罕見,而那些昂貴的磁碟之行就被攤平到許多次便宜的記憶體內命中之上。區域性也是工作集觀念的基礎,以及快取與 TLB 之所以划算的原因。誠實的提醒是:區域性差的程式——對龐大資料集進行隨機存取的工作負載——確實分頁得很糟,而那正是虛擬記憶體吃力的地方。
一個對陣列求和的迴圈,會一遍又一遍觸碰迴圈那幾頁指令頁(時間區域性),並依序走過陣列的資料頁(空間區域性)。在最初幾次錯誤把那些頁帶進來之後,成千上萬次迭代完全不再發生任何錯誤。
在時間與空間上聚集的存取,正是讓錯誤率維持低點的關鍵。
區域性是一種強烈的經驗傾向,而非定律。那些確實在龐大足跡上隨機存取記憶體的工作負載(某些資料庫、圖形處理)區域性弱、分頁得很糟——這正是為什麼虛擬記憶體對每個程式而言並非免費的午餐。