目錄協定(directory protocol)
六個人時,隔著桌子喊行得通;在體育場裡就垮了。解法是登記簿:為每一頁保留一份「究竟誰持有副本」的清單。當你想改一頁時,你不向整座體育場喊——你查它的登記條目,只私訊那少數真正握有那頁的人。目錄協定(directory protocol)就是這樣做的一致性:一份目錄為每一塊記憶體記錄哪些快取目前持有它,於是一致性訊息點對點地只送給共享者,而不是廣播給所有人。
具體地說,目錄為每一塊記憶體儲存它的狀態,和一份清單(常是位元圖,每個核心一位元)記哪些快取共享它。當核心 A 想寫區塊 B 時,它送一個請求到 B 的目錄條目;目錄查出共享者,只對那些特定的快取送出失效訊息,再授予 A 寫入權限。讀取也一樣:目錄知道最新副本在哪,就把請求轉送過去。從不需要全域廣播——只有針對性的訊息,而且只發給實際有多少共享者就發多少。
這就是讓一致性能擴展到數十、數百、乃至數千個核心的關鍵:流量正比於共享程度,而非總核心數,而且沒有單一的共享匯流排會飽和。代價是目錄本身——要額外的記憶體儲存共享者資訊(它隨記憶體大小與核心數一起成長),以及在動手前先問目錄這個間接帶來的額外延遲。大型 NUMA 伺服器和眾核心晶片使用目錄一致性;小晶片常維持較簡單的監聽。目錄對監聽,是一致性裡那個經典的可擴展性取捨。
區塊 B 由核心 2 和核心 5 共享;目錄對 B 的條目記錄 {狀態:共享,共享者:核心 2、5}。核心 7 想寫 B。它詢問目錄,目錄只對核心 2 和核心 5 送出失效(不送給核心 0、1、3、4、6),等候確認,再授予核心 7 獨佔寫入權。共享者集合之外的核心從不被打擾。
目錄只針對實際的共享者,所以一致性流量隨共享程度而定——這是眾核心機器的關鍵。
目錄的擴展性遠勝監聽,但並非免費:為每塊每核心存一位元,所耗的記憶體會隨機器一起成長,而那層額外的間接也為每一次一致性動作增添延遲。