1. 程式人生 > >30、Java並發性和多線程-阿姆達爾定律

30、Java並發性和多線程-阿姆達爾定律

開發者 dup 算術 currency 優化 blog 進行 變量 自己的

以下內容轉自http://ifeve.com/amdahls-law/:

阿姆達爾定律可以用來計算處理器平行運算之後效率提升的能力。阿姆達爾定律因Gene Amdal 在1967年提出這個定律而得名。絕大多數使用並行或並發系統的開發者有一種並發或並行可能會帶來提速的感覺,甚至不知道阿姆達爾定律。不管怎樣,了解阿姆達爾定律還是有用的。

我會首先以算術的方式介紹阿姆達爾定律定律,然後再用圖表演示一下。

阿姆達爾定律定義

一個程序(或者一個算法)可以按照是否可以被並行化分為下面兩個部分:

  • 可以被並行化的部分
  • 不可以被並行化的部分

假設一個程序處理磁盤上的文件。這個程序的一小部分用來掃描路徑和在內存中創建文件目錄。做完這些後,每個文件交個一個單獨的線程去處理。掃描路徑和創建文件目錄的部分不可以被並行化,不過處理文件的過程可以。

程序串行(非並行)執行的總時間我們記為T。時間T包括不可以被並行和可以被並行部分的時間。不可以被並行的部分我們記為B。那麽可以被並行的部分就是T-B。下面的列表總結了這些定義:

  • T = 串行執行的總時間
  • B = 不可以並行的總時間
  • T-B = 並行部分的總時間

從上面可以得出:

T = B + (T – B)

首先,這個看起來可能有一點奇怪,程序的可並行部分在上面這個公式中並沒有自己的標識。然而,由於這個公式中可並行可以用總時間T和B(不可並行部分)表示出來,這個公式實際上已經從概念上得到了簡化,也即是指以這種方式減少了變量的個數。

T-B是可並行化的部分,以並行的方式執行可以提高程序的運行速度。可以提速多少取決於有多少線程或者多少個CPU來執行。線程或者CPU的個數我們記為N。可並行化部分被執行的最快時間可以通過下面的公式計算出來:

(T – B ) / N

或者通過這種方式

(1 / N) * (T – B)

維基中使用的是第二種方式。

根據阿姆達爾定律,當一個程序的可並行部分使用N個線程或CPU執行時,執行的總時間為:

T(N) = B + ( T – B ) / N

T(N)指的是在並行因子為N時的總執行時間。因此,T(1)就執行在並行因子為1時程序的總執行時間。使用T(1)代替T,阿姆達爾定律定律看起來像這樣:

T(N) = B + (T(1) – B) / N

表達的意思都是是一樣的。

一個計算例子

為了更好的理解阿姆達爾定律,讓我們來看一個計算的例子。執行一個程序的總時間設為1.程序的不可並行化占40%,按總時間1計算,就是0.4.可並行部分就是1 – 0.4 = 0.6.

在並行因子為2的情況下,程序的執行時間將會是:

T(2) = 0.4 + ( 1 - 0.4 ) / 2
     = 0.4 + 0.6 / 2
     = 0.4 + 0.3
     = 0.7

在並行因子為5的情況下,程序的執行時間將會是:

T(5) = 0.4 + ( 1 - 0.4 ) / 5
     = 0.4 + 0.6 / 5
     = 0.4 + 0.12
     = 0.52

阿姆達爾定律圖示

為了更好地理解阿姆達爾定律,我會嘗試演示這個定定律是如何誕生的。

首先,一個程序可以被分割為兩部分,一部分為不可並行部分B,一部分為可並行部分1 – B。如下圖:

技術分享

在頂部被帶有分割線的那條直線代表總時間T(1)。

下面你可以看到在並行因子為2的情況下的執行時間:

技術分享

並行因子為3的情況:

技術分享

優化算法

從阿姆達爾定律可以看出,程序的可並行化部分可以通過使用更多的硬件(更多的線程或CPU)運行更快。對於不可並行化的部分,只能通過優化代碼來達到提速的目的。因此,你可以通過優化不可並行化部分來提高你的程序的運行速度和並行能力。你可以對不可並行化在算法上做一點改動,如果有可能,你也可以把一些移到可並行化放的部分。

優化串行分量

如果你優化一個程序的串行化部分,你也可以使用阿姆達爾定律來計算程序優化後的執行時間。如果不可並行部分通過一個因子O來優化,那麽阿姆達爾定律看起來就像這樣:

T(O, N) = B / O + (1 - B / O) / N

記住,現在程序的不可並行化部分占了B / O的時間,所以,可並行化部分就占了1 - B / O的時間.

如果B為0.1,O為2,N為5,計算看起來就像這樣:

T(2,5) = 0.4 / 2 + (1 - 0.4 / 2) / 5
       = 0.2 + (1 - 0.4 / 2) / 5
       = 0.2 + (1 - 0.2) / 5
       = 0.2 + 0.8 / 5
       = 0.2 + 0.16
       = 0.36

運行時間 vs. 加速

到目前為止,我們只用阿姆達爾定律計算了一個程序或算法在優化後或者並行化後的執行時間。我們也可以使用阿姆達爾定律計算加速比(speedup),也就是經過優化後或者串行化後的程序或算法比原來快了多少。

如果舊版本的程序或算法的執行時間為T,那麽增速比就是:

Speedup = T / T(O,N)

為了計算執行時間,我們常常把T設為1,加速比為原來時間的一個分數。公式大致像下面這樣:

Speedup = 1 / T(O,N)

如果我們使用阿姆達爾定律來代替T(O,N),我們可以得到下面的公式:

Speedup = 1 / ( B / O + (1 - B / O) / N )

如果B = 0.4, O = 2, N = 5, 計算變成下面這樣:

Speedup = 1 / ( 0.4 / 2 + (1 - 0.4 / 2) / 5)
        = 1 / ( 0.2 + (1 - 0.4 / 2) / 5)
        = 1 / ( 0.2 + (1 - 0.2) / 5 )
        = 1 / ( 0.2 + 0.8 / 5 )
        = 1 / ( 0.2 + 0.16 )
        = 1 / 0.36
        = 2.77777 ...

上面的計算結果可以看出,如果你通過一個因子2來優化不可並行化部分,一個因子5來並行化可並行化部分,這個程序或算法的最新優化版本最多可以比原來的版本快2.77777倍。

測量,不要僅是計算

雖然阿姆達爾定律允許你並行化一個算法的理論加速比,但是不要過度依賴這樣的計算。在實際場景中,當你優化或並行化一個算法時,可以有很多的因子可以被考慮進來。

內存的速度,CPU緩存,磁盤,網卡等可能都是一個限制因子。如果一個算法的最新版本是並行化的,但是導致了大量的CPU緩存浪費,你可能不會再使用x N個CPU來獲得x N的期望加速。如果你的內存總線(memory bus),磁盤,網卡或者網絡連接都處於高負載狀態,也是一樣的情況。

我們的建議是,使用阿姆達爾定律定律來指導我們優化程序,而不是用來測量優化帶來的實際加速比。記住,有時候一個高度串行化的算法勝過一個並行化的算法,因為串行化版本不需要進行協調管理(上下文切換),而且一個單個的CPU在底層硬件工作(CPU管道、CPU緩存等)上的一致性可能更好。

30、Java並發性和多線程-阿姆達爾定律