體系結構複習3——資料級並行
體系結構複習 CH6 資料級並行
6.1 資料級並行DLP和SIMD
資料級並行(Data Level Parallel,DLP)是指處理器能夠同時處理多條資料,屬於SIMD模型,即單指令流多資料流模型
繼續挖掘傳統ILP的缺陷:
- 提高流水線時鐘頻率可能導致CPI增加
- 每個時鐘週期很難預取和譯碼多條指令
- 大型科學計算、媒體流處理區域性性較差,Cache命中率低
並且SIMD模型有以下優點:
- SIMD可有效挖掘DLP,如矩陣運算、影象聲音等多媒體資料處理
- SIMD比MIMD更節能,對於一組資料相同操作只需要取一次指令
- (重要)SIMD允許程式設計師序列思維(序列演算法應用到並行體系結構)
因此近年來DLP發展迅速,特別是在超級計算機領域
6.2 向量體系結構
DLP思想:一條指令處理長度為N的一組向量元素的計算
6.2.1 VMIPS
書上引入VMIPS向量體系結構作為MIPS體系結構的向量擴充套件
(1)VMIPS主要元件
- 向量暫存器:8個向量暫存器,每個向量暫存器儲存64個元素,每個元素的寬度是64位
- 向量功能單元:包括浮點加/減、浮點乘、浮點除、整數運算、邏輯運算等向量功能單元(流水化)
- 向量載入/儲存單元:從儲存器中載入向量或將向量儲存到儲存器中(流水化)
- 標量暫存器:即MIPS基礎的32個通用暫存器和32個浮點暫存器
(2)VMIPS主要指令
向量指令字尾VV表示兩個向量暫存器操作、VS表示一個向量暫存器和一個標量暫存器操作(交換順序是SV),.D仍然是雙精度浮點的字尾,常用指令用:
ADDVV.D V1,V2,V3
ADDSV.D V1,F0,V2
MULVS.D V1,V2,F0
LV V1,R1
SV R1,V1
(3)VMIPS程式示例
DaXPY是Linpack的某項標準測試,表示雙精度a乘以向量X加上向量Y這一計算過程,用VMIPS表示為:
L.D F0,a
LV V1,Rx
MULVS.D V2,V1,F0
LV V3,Ry
ADDVV.D V4,V2,V3
SV Ry,V4
如果沒有迴圈間相關,編譯器可以為一段程式序列生成其VMIPS程式碼,稱這段程式是可向量化的(可以用類似之前提到的程式碼排程消除迴圈間相關再向量化)
6.2.2 一些術語
- 護航指令組Convoy:不包含任何結構冒險的可以在同一週期開始執行的向量指令集合
- 連結Chain:允許向量操作在其向量源運算元的各個元素可用時立即啟動,連結中第一個功能單元的結果被轉發給第二個功能單元
- 鐘鳴Chime:執行護航指令組所話費的時間單位;執行m個護航指令組需要m次鐘鳴,向量長度為n時需要花費m x n個時鐘週期
- 多車道Multi-Lane:多個功能單元來提高效能;單車道1條向量指令需要64週期完成,若變成4車道則只需16週期完成
6.2.3 條帶挖掘——處理長度不為64的迴圈
若處理下面DaXPY迴圈:
for (i = 0; i < n; i++) {
Y[i] = a * X[i] + Y[i];
}
其中n可以不等於向量暫存器最大長度MVL(VMIPS中MVL=64)也可以是根據前面程式動態確定的,此時需要引入一個向量長度暫存器VLR來實現條帶挖掘版本的迴圈來適應MVL
VLR控制所有向量操作(包括運算和載入儲存)的向量長度,當然VLR不能超過MVL
此時可以根據MVL把資料劃分為條狀,一條一條的處理,稱之為條帶挖掘:
low = 0;
VL = n % MVL;
for (j = 0; j < n / MVL; j++) {
for (i = low; i < low + VL; i++) {
Y[i] = a * X[i] + Y[i];
}
low += VL;
VL = MVL;
}
上述劃分策略為先處理長度不足MVL的單個條帶,再處理n / MVL - 1
條長度為MVL的整齊條帶
6.2.4 遮罩暫存器——處理向量迴圈中的if語句
如果可向量化的迴圈中存在if語句,那麼有些向量元素執行操作而有些不執行,難以直接向量化:
for (i = 0; i < n; i++) {
if (X[i] != 0) {
X[i] -= Y[i];
}
}
VMIPS中引入向量遮罩暫存器VM來處理if語句,VM用測試語句來測試每位向量元素,元素X[i]滿足條件時置VM[i] = 1
,否則置0(遮罩暫存器預設全1)
在遮罩暫存器的遮罩下,後續的向量指令只有VM[i] == 1
的對應元素才執行
基本塊的程式碼為:
LV V1,Rx
Lv V2,Ry
L.D F0,#0
SNEVS.D V1,F0 ; 若V1[i] != F0,則置VM[i]為1否則為0
SUBVV.D V1,V1,V2
SV V1,Rx
6.3 GPU和CUDA
見另外兩篇博文:
6.4 迴圈級並行
第五章中介紹過迴圈依賴(迴圈間相關)和消除迴圈依賴的辦法,迴圈級並行不僅要消除迴圈間相關還需要消除名稱相關(輸出相關、反相關)
6.4.1 GCD測試檢測迴圈間相關
GCD(最大公約數)能夠檢測一個迴圈中,對陣列的兩次訪問是否存在相關
假設一個迴圈中,以索引a * i + b
儲存一個數組元素,並以索引c * i + d
載入同一個陣列中的元素,GCD測試表示為:
若GCD(a,c)能夠整除d-b,則存在迴圈間相關
如迴圈為X[2 * i + 3] = X[2 * i] * 5.0;
,GCD(a,c) == 2
而d-b == -3
不能整除即不存在迴圈間相關
GCD測試最大公約數計算就是為了檢測某一次迴圈中load的元素會不會是其他某次迴圈中store的元素
注:由於GCD測試沒有考慮迴圈範圍,因此某些時候GCD測試表明存在相關但實際卻沒有(比如剛好重合的時候已經不在陣列界限範圍內了),但可以證明:GCD測試能夠確保不存在相關
6.4.2 消除名稱相關
消除名稱相關的主要方法還是之前介紹的重新命名方法,如下面的迴圈存在輸出相關和反相關:
for (i = 0; i < 100; i++) {
Y[i] = X[i] / c;
X[i] = X[i] + c;
Z[i] = Y[i] + c;
Y[i] = c - Y[i];
}
重新命名為:
for (i = 0; i < 100; i++) {
T[i] = X[i] / c; // Y -> T,消除輸出相關
X1[i] = X[i] + c; // X -> X1,消除反相關
Z[i] = T[i] + c; // Y -> T,消除反相關
Y[i] = c - T[i];
}