並行計算複習
第二篇 並行計算理論基礎:並行算法設計
Ch5 並行算法與並行計算模型
5.1 並行算法的基礎知識
1.並行算法的表達
(1)par-do
n個節點並行完成for循環(每個節點不同,和i相關):
for i = 1 to n par-do
...
endfor
(2)for all
所有節點都執行相同語句:
for all Pi, where 0 <= i <= k do
...
endfor
2.並行算法的複雜度
- 運行時間t(n):求解問題的時間,包括計算時間和通信時間
- 處理器數目p(n):求解給定問題所用的處理器數目
- 成本c(n):c(n) = t(n)*p(n)
- 成本最優——並行算法的成本在數量級上等於最壞情況下串行求解次問題所需要的執行步數
- 工作量W(n):並行算法完成的總的操作數量
- 工作量最優:功耗低、環保
並行算法的WT表示——Brent定理:
令W(n)是並行算法A在運行時間T(n)內所執行的運算量,則A使用p臺處理器可在t(n) = O(W(n)/p + T(n))時間內執行完畢
5.2 並行計算模型
1.PRAM模型(SIMD-SM)
PRAM(Parallel Random Access Machine)並行隨機存取機器,是一種抽象並行計算模型,它假設:
- 存在容量無限大的SM
- 有限或無限個功能相同的處理器,且均有簡單算術運算和邏輯判斷功能
- 任何時刻各處理器可通過SM交換數據
根據併發訪問機制,又分爲:
- 不允許同時讀和同時寫的PRAM-EREW
- 允許同時讀但不允許同時寫的PRAM-CREW
- 允許同時讀和同時寫的PRAM-CRCW
PRAM-CRCW又分爲:
- 只允許同時寫相同的數CPRAM-CRCW
- 只允許優先處理器先寫PPRAM-CRCW
- 允許任意處理器自由寫APRAM-CRCW
PRAM優點:
- 適合並行算法表達、分析和比較
- 使用簡單,屏蔽了通信、存儲管理、進程同步等並行細節
- 易於修改算法設計以適應不同並行機
PRAM缺點:
- PRAM是同步模型,同步鎖費時
- 不適用於MIMD和DM
- 假設任何處理器可在單位時間內訪問任何處理單元而不考慮競爭和帶寬,不現實
2.異步APRAM模型(MIMD-SM)
異步APRAM模型假設:
- 每個處理器有LM、局部時鐘、局部程序
- 處理器通信經過SM
- 無全局時鐘,各處理器異步執行各自指令
- 處理器之間的指令相互依賴關係必須顯式加入同步障
- 一條指令可以在非確定但有界時間內完成
指令類型有:
- 全局讀:從SM讀到LM
- 局部操作:LM操作存入LM
- 全局寫:LM寫入SM
- 同步:各處理器需等到其他處理器到達後才能繼續執行
APRAM比PRAM更加接近實際並行機
3.BSP模型(MIMD-DM)
BSP(Bulk Synchronous Parallel)大同步並行機(APRAM算作輕量)是一個分佈式存儲的MIMD模型,它的計算由若干全局同步分開的、週期爲L的超級步組成,各超級步中處理器做LM操作並通過選路器接收和發送消息;然後做一次全局檢查,以確定該超級步是否已經完成(塊內異步並行,塊間顯式同步)
參數:處理器數p、選路器吞吐率g、全局同步間隔L、一個超級步中一個處理器至多發送或接收h條消息
4.LogP模型:MIMD-DM,點到點通訊
LogP模型是分佈式存儲、點到點通信的MIMD模型
LogP採取隱式同步,而不顯式同步障
Ch6 並行算法基本設計策略
6.1 串改並
發掘和利用現有串行算法中的並行性,直接將串行算法改造爲並行算法
最常用的設計思路但並不普適,好的串行算法一般無法並行化(數值串行算法可以)
快速排序的串改並
SISD上串行執行,最壞情況下O(n^2),理想情況下O(nlogn)
思路:將O(n)的劃分(Partition)並行化是關鍵,算法:
- 構造一棵二叉排序樹,主元是根
- 小於等於主元素處於左子樹,大於主元素處於右子樹
- 左右子樹均是二叉排序樹
在CRCW模型上,用僞代碼描述如下:
//A[1...n]排序用n個處理器,處理器i中存有A[i]
//f[i]中存有其元素是主元素的處理器號
//LC[1...n]和RC[1...n]分別記錄給定主元素的左兒子和右兒子
for each processor i par-do
root = i //所有處理器競爭,只有一個寫入root
f[i] = root //所有處理器都把root寫入自己的f[i]
LC[i] = RC[i] = n + 1 //初始值
endfor
repeat for each processor i != root do
if (A[i] < A[f[i]]) || (A[i] == A[f[i]] && i < f[i])
LC[f[i]] = i //併發寫,所有滿足條件的i只有一個寫入LC[f[i]]作爲左子樹的根,也就是下一次循環的主元素
if i == LC[f[i]] then
exit //若當前處理器寫入則什麼也不做
else
f[i] = LC[f[i]] //若當前處理器沒有寫入,那麼它只能當LC[f[i]]的字節點了
endif
else
RC[f[i]] = i //併發寫,所有滿足條件的i只有一個寫入RC[f[i]]作爲右子樹的根,也就是下一次循環的主元素
if i == RC[f[i]] then
exit //若當前處理器寫入則什麼也不做
else
f[i] = RC[f[i]] //若當前處理器沒有寫入,那麼它只能當RC[f[i]]的字節點了
endif
endif
endrepeat
每次迭代構造一層排序二叉樹花費O(1)時間,在基本平衡的情況下樹高O(logn),則算法複雜度爲O(logn)
6.2 全新設計
從問題本身描述出發,不考慮相應的串行算法,設計 一個全新的並行算法
有向環K着色算法的並行化設計
問題:有向環頂點着色,一共K種顏色,相鄰頂點不允許同色
串行算法(3着色):交替2種顏色,若頂點是奇數則需要用到第3種顏色(難以並行化)
SIMD-EREW上的並行K着色算法:
//初始隨機着色爲c[i],每個頂點着色不同
//輸出着色方案爲nc[i]
for i = 1 to n par-do
k = c[i]和c[i的後繼]的最低的不同二進制位
nc[i] = 2 * k + c[i]的二進制第k位
endfor
O(1)時間完成,需要算法正確性證明
6.2 借用法
找出求解問題和某個已解決問題之間的聯繫,改造或利用已知算法應用到求解問題上
利用矩陣乘法求所有點對間最短路徑
d[k][i][j]表示從vi到vj**至多**經過k-1箇中間頂點時的最短路徑,d[k][i][j] = min{d[k/2][i][l]+d[k/2][l][j]}
那麼可以用矩陣乘法的改進(乘變加,求和變min)做logn次矩陣乘法即可
思路是這樣,具體僞代碼略,需要O(n^3)個節點,時間複雜度爲O(log^2(n))
Ch7 並行算法常用設計技術
6.1 劃分設計技術
使用劃分法把問題求解分成兩步:
- 把給定問題劃分成p個幾乎等尺寸的子問題
- 用p臺處理器並行求解子問題
(1)均勻劃分(PSRS排序)
長度爲n的待處理序列均勻劃分給p個處理器,每個處理器處理n/p個元素
MIMD機器上的PSRS排序算法:
(1)均勻劃分:將n個元素A[1..n]均勻劃分成p段,分配給p個處理器
(2)局部排序:pi調用串行排序算法對A[(i-1)n/p+1..in/p]排序
(3)選取樣本:pi從其有序子序列A[(i-1)n/p+1..in/p]中選取p個樣本元素
(4)樣本排序:用一臺處理器對p2個樣本元素進行串行排序
(5)選擇主元:用一臺處理器從排好序的樣本序列中選取p-1個主元,並播送給其他pi
(6)主元劃分:pi按主元將有序段A[(i-1)n/p+1..in/p]劃分成p段
(7)全局交換:各處理器將其有序段按段號交換到對應的處理器中(一定保證均勻劃分??)
(8)歸併排序:各處理器對接收到的元素進行歸併排序
(2)方根劃分(Valiant歸併排序)
長度爲n的待處理序列,取i*sqrt(n)爲劃分元,將元素劃分成若干段交給處理器處理
SIMD-CREW機器上的Valiant歸併排序:
(1)方根劃分:把A和B(長n有序段)的第i*sqrt(n)元素作爲劃分元,把A和B分成若干段
(2)段間比較:A中的所有劃分元和B中的所有劃分元比較,確定A中劃分元應插入B中的哪一段
(3)段內比較:A中的劃分元和B中相應段比較並確定插入位置,這些插入位置又將B重新劃分成了若干段
(4)段組合並:插入A劃分元后,又得到若干有序段組需要歸併,遞歸直到有一組(A)的長度爲0
使用n個處理器可以在O(loglogn)內完成
(3)對數劃分(並行歸併排序)
取i*logn作爲劃分元劃分
定義位序rank(x,X)爲X中小於等於x的元素個數,則有PRAM-CREW上的對數劃分:
//非降有序的A={a[1],...,a[n]}和B={b[1],...,b[m]}歸併
//假設log(m)和k=m/log(m)均爲整數
j[0]=0
j[k]=n
for i = 1 to k - 1 par-do
j[i] = rank(b[ilogm], A)
endfor
for i = 1 to k - 1 par-do
Bi = {b[ilogm+1], ..., b[(i+1)logm]}
Ai = {a[j[i]+1], ..., a[j[i+1]]}
endfor
//將原問題轉化爲子序列組Bi和Ai的歸併,那麼同樣可以遞歸調用完成整個序列的歸併
//對數劃分保證Bi和Ai中的元素均大於Bi-1和Ai-1中的元素
(4)功能劃分( (m,n)-選擇)
功能劃分是根據特定問題而把序列分成p個等長組,每組符合問題特性的一種劃分方法
(m,n)-選擇問題是將長爲n的序列中選取前m個較小的元素,利用功能劃分來實現並行化的(m,n)-選擇問題求解:
(1)功能劃分:將A劃分成g=n/m組,每組含m個元素
(2)局部排序:使用Batcher排序網絡將各組並行排序
(3)兩兩比較:將所排序的各組兩兩比較,從而形成MIN序列
(4)排序-比較:對各個MIN序列,重複執行第(2)和第(3)步,直至選出m個最小者
6.2 分治設計技術
分治將複雜問題劃分成較小規模特性相同的子問題,且子問題類型和原問題類型相同,通常用遞歸完成分治算法
(1)雙調歸併網絡
雙調序列:若序列x[0],…,x[n-1]是雙調序列,則存在一個0<=k<=n-1使得x[0]>=…>=x[k]<=x[k+1]<=…<=x[n-1]或序列循環移動可以得到此關係
Batcher定理:雙調序列對0<=i<=n/2-1,比較所有x[i]和x[i+n/2]得到的小序列MIN和大序列MAX仍然是雙調序列
Batcher雙調歸併排序算法:
//輸入雙調序列X得到非降有序序列Y
procedure Bitonic-Merge(x) {
for i = 0 to n/2 - 1 par-do
s[i] = min(x[i], x[i+n/2])
l[i] = max(x[i], x[i+n/2])
endfor
Bitonic-Merge(s)
Bitonic-Merge(l)
output s followed by l
}
6.3 平衡樹設計技術
以樹的葉結點爲輸入,中間結點爲處理結點,由葉向根或由根向葉逐層進行並行處理
(1)求最大值
SIMD-TC(SM)上O(logn)的樹上求最大值算法:
//帶求最大值的n個元素放在A[n, ..., 2n-1]中,最大值放在A[1]中
//n=2^m
for k = m-1 to 0 do
for j = 2^k to 2^(k+1)-1 par-do
A[j]=max(A[2j],A[2j+1])
endfor
endfor
SIMD-CRCW上常數時間求最大值算法:
//輸入A[1...p]共p個元素
//B[1..p][1..p],M[1..p]爲中間處理用的布爾數組, 如果M[i]=1, 則A[i]爲最大值
for i = 1 to p, j = 1 to p par-do //W(n)=O(p^2)換取時間O(1)
if A[i] >= A[j] then
B[i,j] = 1
else
B[i,j] = 0
endif
endfor
for i = 1 to p par-do
M[i] = B[i,1]∧ B[i,2]∧... ∧B[i,p]
//向量操作保證O(1)完成,則該並行段也是O(1)
//若M[i]=1,則是A[i]最大值
endfor
(2)計算前綴和
定義二元結合運算*,則n個元素x[1,…,n]的前綴和是如下定義的n個部分和:
s[i]=x[1]*x[2]*x[i],i=1,…,n
串行計算n個元素前綴和需要O(n)的時間(利用s[i] = s[i-1] * x[i])
下面給出SIMD-TC上的求前綴和O(logn)的算法:
for j = 1 to n par-do
B[0, j] = A[j] //平衡樹底層是n個元素
endfor //O(1)
for h = 1 to logn do //正向遍歷,父節點存所有字節點之和
for j = 1 to n/2^h par-do
B[h,j]=B[h-1,2j-1]*B[h-1,2j] //父節點存子節點之和
endfor //O(1)
endfor //共logn層,則總時間O(logn)
for h = logn to 0 do
for j = 1 to n/2^h par-do
if j % 2 == 0 then //該節點是右兒子
C[h,j]=C[h+1,j/2] //右兒子等於父節點的值
else if j == 1 then
C[h,j]=B[h,1] //每一層第一個節點等於B樹上對應值
else //該節點是左兒子
C[h,j]=C[h+1,(j-1)/2]*B[h,j] //叔節點和\*自身和
endif
endfor
endfor
6.4 倍增設計技術
遞歸調用時,所要處理數據之間的距離逐步加倍, 經過k步後即可完成距離爲2^k的所有數據的計算
(1)表序問題
n個元素的序列L,每個元素分配一個處理器,給定k求出L[k]的rank(k)值,rank(k)等於其到表尾的距離
每個元素有一個指向下一個元素的指針next(k)
下面給出SIMD-EREW上求元素表序的算法:
for k in L par-do
p(k) = next(k)
if p(k) != k then
distance(k)=1
else
distance(k)=0
endif
endfor //O(1)完成distance初始化
repeat t = ceil(logn) times
for k in L par-do
if p(k)!=p(p(k)) then //不是到數第2個
distance(k) += distance(p(k)) //把next的dis加到自己上來
p(k)=p(p(k)) //next倍增
endif
endfor
for k in L par-do
rank(k)=distance(k) //爲什麼每次都去賦值rank不最後賦值一次?
endfor
endrepeat //O(logn)
(2)求森林的根
森林是一組有根有向樹,找森林的根就是求出所有節點所在的樹的根節點
可以用倍增技術去修改每個節點的後繼(後繼即是父節點),下面是SIMD-CREW上的求森林根算法:
for i = 1 to n par-do
s[i] = p[i]
while s[i] != s[s[i]] do //該節點不是根
s[i] = s[s[i]] //指向父節點
endwhile //這個while不需要同步嗎?
endfor //O(logn)
6.5 流水線技術
將算法路程分成p個前後銜接的任務片段,一個任務片段完成之後其後繼任務片段可以立即開始,那麼久可以引入流水線的思想來處理多條數據
(1)五點的DFT計算
計算DFT時引入Horner法則把任務劃分成流水段:
O(n)的時間即可完成DFT計算
(2)4流水線編程實例
老師舉出一個可流水化的矩陣賦值的例子,意義不是很大,略