寫入失效(write-invalidate)
當一位編輯改了那頁共享的紙,有兩種辦法讓大家保持誠實。你可以打電話給其他每位編輯、口述那句新的,讓他們的副本保持最新——或者你可以乾脆告訴他們「你的副本現在作廢了,丟掉它」,等他們真正需要時才去取一份新的。第二種就是寫入失效(write-invalidate):寫入時,寫者摧毀所有其他快取副本,而不是更新它們。
機制如下。在一個核心寫一條共享列之前,它送出對那條列的失效;其他每個持有它的快取就把自己的副本標為 Invalid 並丟棄。寫者現在是唯一持有者,可以自由地寫。若另一個核心之後需要那筆資料,它的讀取會未命中,於是去提取新值——所以資料只在有人真正想要時才移動。另一種做法寫入更新(write-update,或寫入廣播)則是每次寫入都把新值推給每個共享者。寫入失效不論資料多大、不論寫者連寫幾次,都只送一次失效;寫入更新則每一次都送出完整的新值。
寫入失效在幾乎所有真實系統裡都勝出,原因在於常見的存取型態:一個核心常常一連寫同一條列許多次(一個更新變數的迴圈),別的核心才來讀。失效只付一次代價——一次失效——然後寫者就私下免費地猛敲那條列;寫入更新則會把那些寫入每一次都廣播出去,淹沒匯流排。只有對於那種被寫一次、立刻被許多等候中的核心讀取的資料(很罕見),這個取捨才反過來。失效誠實的代價是:下一個讀者要吃一次未命中去重新提取,但那通常比不斷的廣播便宜。
一個核心跑一個迴圈把一個計數器寫 1000 次,同時另外三個核心持有那條列。寫入失效:一次失效清掉三份副本,接著 1000 次私有寫入在匯流排上不花成本。寫入更新:那 1000 次寫入每一次都把新值廣播給三個共享者——3000 則更新訊息對上一次失效。
失效付一次代價,之後讓重複的寫入免費跑;更新則重播每一次寫入——這就是失效主宰真實硬體的原因。
寫入失效是現代一致性的預設(MESI 用的就是它),因為真實工作負載往往把一條列寫許多次才共享。寫入更新只在罕見的「寫一次、被許多人讀」型態裡才划算。