1. 程式人生 > 程式設計 >深入學習快取一致性問題和快取一致性協議MESI(二)

深入學習快取一致性問題和快取一致性協議MESI(二)

寫緩衝器與無效化

背景:

MESI 協議解決了快取一致性問題, 但是其自身也存在一個效能弱點——處理器執行寫記憶體操作時,必須等待其他所有處理器將其快取記憶體中的相應副本資料刪除並接收到這些處理器所回覆的 Invalidate Acknowledge/Read Response訊息之後才能將資料寫入高速緩 存。為了規避和減少這種等待造成的寫操作的延遲 (Latency),硬體設計者引入了寫緩衝器和無效化佇列,

寫快取器

寫緩衝器 (Store Buffer,也被稱為 Write Buffer) 是處理器內部的一個容扯比高速緩 存還小的私有高速儲存部件6. 每個處理器都有其寫緩衝器, 寫緩衝器內部可包含若干條 目 (Entry)。 一個處理器無法讀取另外一個處理器上的寫緩衝器中的內容。

image.png

引入寫緩衝器之後,處理器在執行寫操作時會做這樣的處理:

如果相應的快取條目狀態為E或者M. 那麼處理器可能會直接將資料寫入相應的快取行而無須傳送任何訊息飛 如果相應的快取條目狀態為 s. 那麼處理器會先將寫操作的相關資料(包括資料和待操作 的記憶體地址)存人寫緩衝器的條目之中,併傳送Invalidate訊息;如果相應的快取條目狀態為I,我們就稱相應的寫操作遇到了寫未命中(WriteMiss),那麼此時處理韶會先將寫 操作相關資料存人寫緩衝器的條目之中,併傳送ReadInvalidate訊息。

由此可見, 寫緩衝器的引入使得處理器在執行寫操作的時候可以不等待 Invalidate Acknowledge訊息, 從而減少了寫操作的延時. 這使得寫操作的執行處理器在其他處理器回覆Invalidate Acknowledge/Read Response訊息這段時間內能夠執行其他指令,從而提高了處理器的指令執行效率。

引入無效化佇列(Invalidate Queue) 之後, 處理器在接收到Invalidate訊息之後並不刪除訊息中指定地址對應的副本資料, 而是將訊息存入無效化佇列之後就回復Invalidate Acknowledge訊息,從而減少了寫操作執行處理器所需的等待時間。有些處理器(比如x86)可能沒有使用無效化佇列。

寫緩衝器和無效化佇列的引入又會帶來一些新的問題——記憶體重排序和可見性問題。

儲存轉發

這種處理器直接從寫緩衝器中讀取資料來實現記憶體讀操作的技術被稱為儲存轉發(Store Forwarding)。 儲存轉發使得寫操作的執行處理器能夠在不影響該處理器執行讀操作的情 況下將 寫操作的結果存入寫緩衝器。

再探記憶體重排序

​ 寫緩衝器和無效化佇列都可能導致記憶體重排序。

-硬體層的記憶體屏障分為兩種:Load BarrierStore Barrier即讀屏障和寫屏障。

-處理器在一些特定條件下(比如寫緩衝器滿、I/0指令被執行)會將寫緩衝器排空(Drain)或者沖刷(Flush),即將寫緩衝器中的內容寫入快取記憶體,但是從程式對一個或者一組變扯更新的角度來看,處理器本身並無法保證這種沖刷對程式來說是及時的。

為了保證一個處理器對共享變措所做的更新可以被其他處理器同步,編譯器等底層系統需要藉助一類被稱為記憶體屏障的特殊指令。記憶體屏障中的儲存屏障(Store Barrier)可以使執行該指令的處理器沖刷其寫緩衝器。
複製程式碼

​ 然而,沖刷寫緩衝器只是解決了可見性問題的一半。因為可見性問題的另一半是無效化佇列導致的。無效化佇列的引入本身也會導致新的問題一處理器在執行記憶體讀取操作前如果沒有根據無效化佇列中的內容將該處理器上的快取記憶體中的相關副本資料刪除,那麼就可能導致該處理器讀到的資料是過時的舊資料,從而使得其他處理器所做的更新丟失。

​ 為了使一個處理器上執行的執行緒能夠讀取到另外一個處理器上執行的執行緒對共享變數所做的更新, 該處理器必須先根據無效化佇列中儲存的Invalidate訊息刪除其快取記憶體中的相應副本資料,從而使其他處理器上執行的執行緒對共享變拯所做的更新在快取一 致性協議的作用下能夠被同步到該處理器的快取記憶體之中。

​ 記憶體屏障中的載入屏障(Load Barrier)正是用來解決這個問題的。載入屏障會根據無效化佇列內容所指定的記憶體地址, 將相應處理器上的快取記憶體中相應的快取條目的狀態都標記為I' 從而使該處理器後續執行鍼對相應地址(無效化佇列內容中指定的地址)的讀記憶體操作時必須傳送Read訊息.以將其他處理器對相關共享變數所做的更新同步到該處理器的快取記憶體中。

​ 不同的處理器架構所支援(允許)的記憶體重排序各有不同。比如,現代處理器都會採 用寫緩衝器, 而有的處理器(比如x86)會保障寫操作的順序, 即這些處理器不允許StoreStore 重排序的出現。

再探可見性

​ 我們說寫緩衝器是可見性問題的硬體根源。

​ 處理器在一些特定條件下(比如寫緩衝器滿、I/0指令被執行)會將寫緩衝器排空(Drain)或者沖刷(Flush),即將寫緩衝器中的內容寫入快取記憶體 ,但是從程式對一個或者一組變扯更新的角度來看,處理器本身並無法保證這種沖刷對程式來說是及時的。因此,為了保證一個處理器對共享變措所做的更新可以被其他處理器同步,編譯器等底層系統需要藉助一類被稱為記憶體屏障的特殊指令。記憶體屏障中的儲存屏障(Store Barrier)可以使執行該指令的處理器沖刷其寫緩衝器。

​ 然而,沖刷寫緩衝器只是解決了可見性問題的一半。因為可見性問題的另一半是無效化佇列導致的。無效化佇列的引入本身也會導致新的問題一處理器在執行記憶體讀取操作前如果沒有根據無效化佇列中的內容將該處理器上的快取記憶體中的相關副本資料刪除,那麼就可能導致該處理器讀到的資料是過時的舊資料,從而使得其他處理器所做的更新丟失。因此,為了使一個處理器上執行的執行緒能夠讀取到另外一個處理器上執行的執行緒對共享變數所做的更新, 該處理器必須先根據無效化佇列中儲存的Invalidate訊息刪除其快取記憶體中的相應副本資料,從而使其他處理器上執行的執行緒對共享變拯所做的更新在快取一 致性協議的作用下能夠被同步到該處理器的快取記憶體之中。

​ 記憶體屏障中的加栽屏障(Load Barrier)正是用來解決這個問題的。載入屏障會根據無效化佇列內容所指定的記憶體地址, 將相應處理器上的快取記憶體中相應的快取條目的狀態都標記為I' 從而使該處理器後續執行鍼對相應地址(無效化佇列內容中指定的地址)的讀記憶體操作時必須傳送Read訊息.以將其他處理器對相關共享變數所做的更新同步到該處理器的快取記憶體中。

​ 因此, 解決可見性問題首先要使寫執行緒對共享變數所做的更新能夠到達(被儲存到) 快取記憶體,從而使該更新對其他處理器是可同步的。其次 , 讀執行緒所在的處理器要將其無效化佇列中的內容 ”應用” 到其快取記憶體上, 這樣才能夠將其他處理器對共享變怔所做的 更新同步到該處理器的快取記憶體中。

​ 而這兩點是通過儲存屏障與載入屏障的成對使用實現的:寫執行緒的執行處理器所執行的儲存屏障保障了該執行緒對共享變數所做的更新對讀執行緒 來說是可同步的;讀執行緒的執行處理器所執行的載入屏障將寫執行緒對共享變扯所做的更新同步到該處理器的快取記憶體之中。

​ 儲存轉發技術也可能導致可見性問題。

整理不易,有收穫請點贊