首頁>技術>

早期的CPU是通過提高主頻來提升CPU的效能,但是隨著頻率“紅利”越來越困難的情況下,廠商開始用多核來提高CPU的計算能力。多核是指一個CPU裡有多個核心,在同一時間一個CPU能夠同時執行多個執行緒,通過這樣提高CPU的併發能力。

記憶體一致性模型(memory consistency model)就是用來描述多執行緒對共享儲存器的訪問行為,在不同的記憶體一致性模型裡,多執行緒對共享儲存器的訪問行為有非常大的差別。這些差別會嚴重影響程式的執行邏輯,甚至會造成軟體邏輯問題。在後面的介紹中,我們將分析不同的一致性模型裡,多執行緒的記憶體訪問亂序問題。

目前有多種記憶體一致性模型:

順序儲存模型(sequential consistency model)完全儲存定序(total store order)部分儲存定序(part store order)寬鬆儲存模型(relax memory order)一致性模型的特性

在後面我們會分析這幾個一致性模型的特性

在分析之前,我們先定義一個基本的記憶體模型,以這個記憶體模型為基礎進行分析

上圖是現代CPU的基本記憶體模型,CPU內部有多級快取來提高CPU的load/store訪問速度(因為對於CPU而言,主存的訪問速度太慢了,上百個時鐘週期的記憶體訪問延遲會極大的降低CPU的使用效率,所以CPU內部往往使用多級快取來提升記憶體訪問效率。)

C1與C2是CPU的2個核心,這兩個核心有私有快取L1,以及共享快取L2。最後一級儲存器才是主存。後面的順序一致性模型(SC)中,我們會以這個為基礎進行描述(在完全儲存定序、部分儲存定序和寬鬆記憶體模型裡會有所區別,後面會描述相關的部分)

為了簡化描述的複雜性,在下面的記憶體一致性模型描述裡,會先將快取一致性(cache coherence)簡單化,認為快取一致性是完美的(假設多核cache間的資料同步與單核cache一樣,沒有cache引起的資料一致性問題),以減少描述的複雜性。

順序儲存模型

順序儲存模型是最簡單的儲存模型,也稱為強定序模型。CPU會按照程式碼來執行所有的load與store動作,即按照它們在程式的順序流中出現的次序來執行。從主儲存器和CPU的角度來看,load和store是順序地對主儲存器進行訪問。

在順序儲存器模型裡,MP(多核)會嚴格嚴格按照程式碼指令流來執行程式碼

所以上面程式碼在主存裡的訪問順序是:

S1 S2 L1 L2

通過上面的訪問順序我們可以看出來,雖然C1與C2的指令雖然在不同的CORE上執行,但是C1發出來的訪問指令是順序的,同時C2的指令也是順序的。雖然這兩個執行緒跑在不同的CPU上,但是在順序儲存模型上,其訪問行為與UP(單核)上是一致的。

我們最終看到r2的資料會是NEW,與期望的執行情況是一致的,所以在順序儲存模型上是不會出現記憶體訪問亂序的情況

完全儲存定序

為了提高CPU的效能,晶片設計人員在CPU中包含了一個儲存快取區(store buffer),它的作用是為store指令提供緩衝,使得CPU不用等待儲存器的響應。所以對於寫而言,只要store buffer裡還有空間,寫就只需要1個時鐘週期(哪怕是ARM-A76的L1 cache,訪問一次也需要3個cycles,所以store buffer的存在可以很好的減少寫開銷),但這也引入了一個訪問亂序的問題。

首先我們需要對上面的基礎記憶體模型做一些修改,表示這種新的記憶體模型

相比於以前的記憶體模型而言,store的時候資料會先被放到store buffer裡面,然後再被寫到L1 cache裡。

首先我們思考單核上的兩條指令:

\tS1:store flag= set\tS2:load r1=data\tS3:store b=set

如果在順序儲存模型中,S1肯定會比S2先執行。但是如果在加入了store buffer之後,S1將指令放到了store buffer後會立刻返回,這個時候會立刻執行S2。S2是read指令,CPU必須等到資料讀取到r1後才會繼續執行。這樣很可能S1的store flag=set指令還在store buffer上,而S2的load指令可能已經執行完(特別是data在cache上存在,而flag沒在cache中的時候。這個時候CPU往往會先執行S2,這樣可以減少等待時間)

這裡就可以看出再加入了store buffer之後,記憶體一致性模型就發生了改變。

如果我們定義store buffer必須嚴格按照FIFO的次序將資料傳送到主存(所謂的FIFO表示先進入store buffer的指令資料必須先於後面的指令資料寫到儲存器中),這樣S3必須要在S1之後執行,CPU能夠保證store指令的儲存順序,這種記憶體模型就叫做完全儲存定序(TSO)。

我們繼續看下面的一段程式碼

在SC模型裡,C1與C2是嚴格按照順序執行的

程式碼可能的執行順序如下:

\tS1 S2 L1 L2\tS1 L1 S2 L2\tS1 L1 L2 S2\tL1 L2 S1 S2\tL1 S1 S2 L2\tL1 S1 L2 S2

由於SC會嚴格按照順序進行,最終我們看到的結果是至少有一個CORE的r1值為NEW,或者都為NEW。

在TSO模型裡,由於store buffer的存在,L1和S1的store指令會被先放到store buffer裡面,然後CPU會繼續執行後面的load指令。Store buffer中的資料可能還沒有來得及往儲存器中寫,這個時候我們可能看到C1和C2的r1都為0的情況。

所以,我們可以看到,在store buffer被引入之後,記憶體一致性模型已經發生了變化(從SC模型變為了TSO模型),會出現store-load亂序的情況,這就造成了程式碼執行邏輯與我們預先設想不相同的情況。而且隨著記憶體一致性模型越寬鬆(通過允許更多形式的亂序讀寫訪問),這種情況會越劇烈,會給多執行緒程式設計帶來很大的挑戰。

部分儲存定序

晶片設計人員並不滿足TSO帶來的效能提升,於是他們在TSO模型的基礎上繼續放寬記憶體訪問限制,允許CPU以非FIFO來處理store buffer緩衝區中的指令。CPU只保證地址相關指令在store buffer中才會以FIFO的形式進行處理,而其他的則可以亂序處理,所以這被稱為部分儲存定序(PSO)。

那我們繼續分析下面的程式碼

S1與S2是地址無關的store指令,cpu執行的時候都會將其推到store buffer中。如果這個時候flag在C1的cahe中存在,那麼CPU會優先將S2的store執行完,然後等data快取到C1的cache之後,再執行store data=NEW指令。

這個時候可能的執行順序:

S2 L1 L2 S1

這樣在C1將data設定為NEW之前,C2已經執行完,r2最終的結果會為0,而不是我們期望的NEW,這樣PSO帶來的store-store亂序將會對我們的程式碼邏輯造成致命影響。

從這裡可以看到,store-store亂序的時候就會將我們的多執行緒程式碼完全擊潰。所以在PSO記憶體模型的架構上程式設計的時候,要特別注意這些問題。

寬鬆記憶體模型

喪心病狂的晶片研發人員為了榨取更多的效能,在PSO的模型的基礎上,更進一步的放寬了記憶體一致性模型,不僅允許store-load,store-store亂序。還進一步允許load-load,load-store亂序, 只要是地址無關的指令,在讀寫訪問的時候都可以打亂所有load/store的順序,這就是寬鬆記憶體模型(RMO)。

我們再看看上面分析過的程式碼

在PSO模型裡,由於S2可能會比S1先執行,從而會導致C2的r2暫存器獲取到的data值為0。在RMO模型裡,不僅會出現PSO的store-store亂序,C2本身執行指令的時候,由於L1與L2是地址無關的,所以L2可能先比L1執行,這樣即使C1沒有出現store-store亂序,C2本身的load-load亂序也會導致我們看到的r2為0。從上面的分析可以看出,RMO記憶體模型裡亂序出現的可能性會非常大,這是一種亂序隨可見的記憶體一致性模型。

記憶體屏障

晶片設計人員為了儘可能的榨取CPU的效能,引入了亂序的記憶體一致性模型,這些記憶體模型在多執行緒的情況下很可能引起軟體邏輯問題。為了解決在有些一致性模型上可能出現的記憶體訪問亂序問題,晶片設計人員提供給了記憶體屏障指令,用來解決這些問題。

記憶體屏障的最根本的作用就是提供一個機制,要求CPU在這個時候必須以順序儲存一致性模型的方式來處理load與store指令,這樣才不會出現記憶體訪問不一致的情況。

對於TSO和PSO模型,記憶體屏障只需要在store-load/store-store時需要(寫記憶體屏障),最簡單的一種方式就是記憶體屏障指令必須保證store buffer資料全部被清空的時候才繼續往後面執行,這樣就能保證其與SC模型的執行順序一致。

而對於RMO,在PSO的基礎上又引入了load-load與load-store亂序。RMO的讀記憶體屏障就要保證前面的load指令必須先於後面的load/store指令先執行,不允許將其訪問提前執行。

我們繼續看下面的例子:

例如C1執行S1與S2的時候,我們在S1與S2之間加上寫屏障指令,要求C1按照順序儲存模型來進行store的執行,而在C2端的L1與L2之間加入讀記憶體屏障,要求C2也按照順序儲存模型來進行load操作,這樣就能夠實現記憶體資料的一致性,從而解決亂序的問題。

ARM的很多微架構就是使用RMO模型,所以我們可以看到ARM提供的dmb記憶體指令有多個選項:

\tLD load-load/load-store\tST store-store/store-load\tSY any-any

這些選項就是用來應對不同情況下的亂序,讓其迴歸到順序一致性模型的執行順序上去。

本文修改自:http://www.wowotech.net/memory_management/456.html

最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 記一次神奇的sql查詢經歷,group by慢查詢優化