本文是論文Treadmarks: Distributed Shared Memory on Standard Workstations and Operating Systems 的讀書筆記,水平有限,若有任何錯誤的地方,請不吝指出。
本文是mit 6.824 Schedule: Spring 2016的第12課,前面課程內容可以在分布式找到,更多詳細資料可以到:distributed-system查看。
介紹
在并發編程中,我們需要處理兩個關鍵問題:
- 線程之間如何通信
- 線程之間如何同步
通信是指線程之間以何種機制來交換信息,在命令式編程中,線程之間的通信機制有兩種:
- 共享內存
- 消息傳遞
我們從通信和同步兩個維度來看共享內存和消息傳遞。
在共享內存的并發模型里,線程之間共享程序的公共狀態,線程之間通過寫-讀內存中的公共狀態來隱式進行通信。在消息傳遞的并發模型里,線程之間沒有公共狀態,線程之間必須通過明確的發送消息來顯式進行通信。
同步是指程序用于控制不同線程之間操作發生相對順序的機制。在共享內存并發模型里,同步是顯式進行的。程序員必須顯式指定某個方法或某段代碼需要在線程之間互斥執行。在消息傳遞的并發模型里,由于消息的發送必須在消息的接收之前,因此同步是隱式進行的。
通過上面的介紹我們知道了共享內存是一種隱式的通信手段,需要顯示的方法來實現同步。
而在分布式系統中,我們希望能夠的是能盡可能的利用普通的機器,來達到并行計算的目標,而distributed shared memory (DSM) 在分布式系統中實現了共享內存,讓所有process都共享一個全局地址空間,通過提供簡單的api,方便process的訪問。
先讓我們看下api
其中barrier,acquire和realease用于同步操作,一旦調用barrier等待所有process都到達這個點后才繼續執行,acquire和realease則用于鎖的獲取和釋放。
設計
在實現DSM時,主要考慮的兩個問題是:
- 一致性
- False Sharing
首先在分布式系統中,為了提高性能,往往會對同一份數據做本地緩存,加快訪問,但是數據雖然有多份,但是需要保證數據的一致性,同一份數據可能有多個副本,一旦數據做出了改變,需要通知所有持有副本的process,數據已經改變了。
我們先來看下如果要實現這種嚴格的數據改變,就必須可見,系統需要怎么做?
如上圖所示,每個寫操作一旦完成,必須要通知所有其他的進程該行為,帶來的問題是:
- 消息數的增加
- 延遲
這種嚴格的一致性被稱為是:sequence consistency,一般系統為了其他一些因素,都會做出一些trade-off,TreadMarks則是采取了release consistency,只有在同步點上才要求數據同步,看圖:
使用release consistency的目標是:
- 減少消息數
- 減少延遲
那此處具體的同步點是什么時候呢?
前面提到過TreadMarks提供了acquire和release兩個同步操作,當發生同步操作的時候,進行數據的同步。
此時在release的時候,將在acquire和release之間的數據改變廣播給所有的持有數據副本的process,但是由于需要等待所有副本答復說已經收到通知,release操作會比較耗時。
TreadMarks在實現release consistency采用了Lazy Release Consistency,
- 只有當下次acquire鎖的時候才會去獲取改變
- 獲取再取有效的減少了消息數
Eager和Lazy在實際運行中減少的消息數,可以通過下圖直觀的看到:
下一個需要解決的問題是:False Sharing,那什么是False Sharing?
我們看到P和Q都是操作同一個page,但是P是寫x,而Q是讀y,但是由于P寫完x后通知了Q改頁已經數據更改了,失效了保存在Q中的副本,因此Q再去訪問y的時候,必須要去P處獲取該頁的數據,加重了數據的傳輸成本。那解決辦法有什么呢?采用 multiply writer protocol,具體來講就是
- 采用copy-on-write機制
- 一旦某個進程被授權訪問write-shared的數據,將包含數據的頁標志為copy-on-write
- 第一次更改頁數據的時候,會創建一個備份:twin
當release操作的時候,TreadMarks會:
- 比較twin和修改過后的數據
- 將不同保存為diff
- 通知所有的副本(write notice)
- 當其他process訪問改頁的時候,會去請求diffs
TreadMarks在diffs的創建上,采取了Lazy的策略,只有當其他processor請求頁改變的時候,才會去創建diffs。
數據結構
主要數據結構如上圖所示,包括了
- Page Array
- Proc Array
- write notice records
- diff pool
此處PageArray中每個entry都是一個page,包含的數據有
- 頁的當前狀態:no access, read-only access, read-write access
- 當前持有當前頁的processor
- 一個以processor_id為index的array,每個array index都是來自processor的write notice,并且以interval排序,如果write notice對應的diffs已經創建,則會有一個指針指向
每個ProcArray則是記錄了processor知道的intervals records,每個interval records指向了當前intervals知道的write notices。
下面舉個例子:
M0: a1 x=1 r1 a2 y=9 r2
M1: a1 x=2 r1
M2: a1 a2 z = x + y r2 r1
有M0,M1,M2,3個進程,其中a和r代表acquire和release,那此時M2中獲取到x的值是M0設置的x=1還是M1設置的x=2呢?
這就要引入一個叫vector clock的東西,通過一組圖來看下是怎么回事。
起初P1,P2,P3開始counters都是0,
當有本地事件發生的時候,P1對應的counter加1
P3也發生了本地事件,counter加1
當P1收到P3發送來的消息的時候,本地counter加1,其余counter進行更新
P1發送事件自己的counter加1,P2接收事件,自己的counter加1,其余counter進行更新;
上面的圖基本上就說明了vector clock是怎么回事,維護了分布式系統中的一個因果關系。
現在我們再回到之前的例子:
M0: a1 x=1 r1 a2 y=9 r2
M1: a1 x=2 r1
M2: a1 a2 z = x + y r2 r1
此時M2怎么知道x等于幾?當M2在獲取鎖1的時候,會去請求前一個釋放鎖的進程,可能是M1也可能是M0,并將自己的vector clock傳遞過去,然后鎖的前一個releaser同自己的vector clock比較,將改變傳遞過來,具體通過一個圖來說明:
P1發送Vector timestamp給releaser P3
P3對于已經更新的counters附上invalidations
P3將其發送給P1
P1收到invalidation后,請求diffs,并將diffs應用到page。
性能
略
參考
Treadmarks: Distributed Shared Memory on Standard Workstations and Operating Systems - PowerPoint PPT
TreadMarks - PowerPoint PPT Presentation
這是6.824: Distributed Systems的第12課,你的鼓勵是我繼續寫下去的動力,期待我們共同進步。