1. 程式人生 > >併發資料結構- 1.1.1 效能

併發資料結構- 1.1.1 效能

原文連結譯文連結,譯者:俞升兵,校對:周可人

1.1.1 效能

一個執行在P個處理上的應用程式的加速度是它在單個處理器上的執行時間和在P個處理器的執行時間的比值。這是一種評價應用程式對於機器資源利用程度的衡量。理想情況下,我們想要的結果是線性加速度:當我們使用P個處理器的時候,我們希望可以獲得P的加速度(譯者注:例如一個應用程式在單處理器的執行時間是10秒,那麼在雙處理的執行時間理想情況下是5秒)。加速度隨著P一起增加的資料結構我們稱之為可擴充套件的資料結構。在設計可擴充套件的資料結構的時候,我們必須注意:為了同步使用簡單的方法會嚴重破壞擴充套件性。

回到基於鎖的計數器,我們會發現鎖會帶來順序性的瓶頸:在任何時間點,最多隻有一個fetch-and-inc操作是有用的,例如:遞增變數X。這種順序性的瓶頸會對本來能夠到達的加速度造成令人驚訝的影響。順序執行部分程式碼對效能帶來的影響已經在基於Amdahl’s law的一個簡單公式中闡明瞭。假設b是一個程式中順序性瓶頸所佔的百分比。假設在單處理器中執行這個程式需要1個時間單位,那麼在P個處理器的環境中,執行順序執行部分的程式碼需要b個時間單位,同時在最好的情況下,併發部分的程式碼消耗(1-b)/p個時間單位,那麼加速度最多等於 1/(b+(1-b)/p) 。這意味中如果程式中只有10%是屬於順序性瓶頸的部分,那麼在一個10個處理器的環境中,程式能達到的加速度最多隻有5.3:我們的應用只利用了機器的一半效能。減少順序性執行程式碼塊的數量和長度對效能是至關重要的。在基於鎖的上下文中,這意味著減少申請鎖的次數,降低鎖的粒度

:一種用來表示在持有鎖時,執行指令數目的衡量。

我們的簡單計數器實現碰到的第二個問題是記憶體競爭:這是底層硬體為了多執行緒併發嘗試訪問相同的記憶體地址所造成的流量的開銷。只有當你理解普通的共享記憶體多處理器架構一些方面,你才能意識到記憶體競爭。如果保護著我們計數器的鎖就像很多簡單鎖一樣,是使用單一地址實現的話,那麼為了請求鎖,執行緒必須重複嘗試修改這個地址。以快取一致性多處理器為例,包含著鎖的cache line的獨佔所有權必須重複的從一個處理器傳輸到別的處理器上,這會導致每次嘗試修改記憶體地址的操作都需要長時間的等待,失敗的嘗試獲取鎖的操作會進一步加重相應的記憶體流量。在1.1.7中我們會討論針對不同型別的共享記憶體架構實現相應的鎖,避免類似這樣的問題.

基於鎖的實現的計數器的第三個問題是:當持有鎖的執行緒被延期了,那麼所有嘗試獲取鎖的執行緒都會被延期。這種現象稱為阻塞,阻塞是多道程式設計(multiprogrammed)中特有的問題,每個處理器都有多個執行緒,作業系統會給擁有鎖的執行緒優先權。對於很多的資料結構來說,這個問題可以通過設計非阻塞的演算法來解決,在非阻塞演算法中一個執行緒的延期不會導致其他執行緒的延期。根據定義而言,這些演算法不能使用鎖。

下面我們繼續討論我們的共享計數器的例子,分別討論阻塞和非阻塞技術;我們會引入更多和效能相關的話題.


on the way to be a real programmer

Latest posts by sambean