1. 程式人生 > >體系結構複習3——資料級並行

體系結構複習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) == 2d-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];
}