HMPP開放標準

HMPP用於異構多核並行編程。

OpenHMPP標準基於一組指令，是用於處理硬體加速器的編程模型，但又沒有GPU編程的相關複雜性。這種基於指令的方法已經實現，因為他們使應用程式代碼和硬體加速器使用之間形成鬆散關係。

本文涉及組成OpenHMPP標準的HMPP指令, 但並不處理指令的執行連結到指令執行。

## 簡介

OpenHMPP基於指令的編程模型提供了一種語法，有效地減輕硬體加速器上的計算，優化數據向/從硬體存儲器移動。

模型基於 CAPS (編譯器和超標量體系結構以及嵌入式處理器) 的初始化工作, 以及來自INRIA, CNRS, 雷恩第一大學和雷恩INSA的共同項目。

## OpenHMPP標準概念

OpenHMPP標準基於codelets的概念, 可以在硬體上遠程執行。

### OpenHMPP Codelet 概念

codelet具有以下屬性:

- 它是一個純函數。
- 它不包含靜態或不穩定的變量聲明，也不涉及任何全局變量，除非這些已經由HMPP 「resident」指令所聲明
- 它不包含任何具有無形體(不能內聯)的函數調用。這包含庫和系統函數的使用, 例如 malloc, printf, ...
- 每個函數必須引用靜態純函數(沒有函數指針)。
- 它不返回任何值(C中的void函數或FORTRAN子程序)。
- 參數的數量應該是固定的(即沒有像C中的vararg那樣可變數量的參數)。
- 它不是遞歸的。
- 它的參數設定為non-aliased。
- 它不包含callsite指令(即RPC至另一個codelet)或其他HMPP指令。

這些屬性確保codelet RPC可以通過硬體遠程執行。此RPC及其相關的數據傳輸可以是異步的。

### Codelet RPCs

HMPP提供同步和異步的RPC。異步操作的執行依賴於硬體。

### HMPP 存儲器模型

HMPP考慮到兩個地址空間: 一個主機處理器和硬體存儲器。

### 指令概念

OpenHMPP指令可能被視為「元資訊」添加到應用程式原始碼。它們是安全的元資訊，即不會改變原始代碼的行為。它們處理函數的遠程執行(RPC)，以及數據向/從硬體存儲器傳輸。

下表介紹了OpenHMPP指令。OpenHMPP指令滿足不同需求: 其中一些專門用於聲明，其他用於執行的管理。

流程控制指令｜數據管理標籤

聲明｜codeletgroup｜residentmapmapbyname

操作標籤｜callsitesynchronizeregion｜allocatereleaseadvancedloaddelegatedstore

### 指令集的概念

HMPP方法的基本點之一是指令的概念及其關聯的標籤，使它能夠在分布於應用程式中的整個指令集上公開一個相干結構。

有兩種類型的標籤:

- 一類關聯到codelet。攜帶這種標籤的指令一般僅限於管理一個(在文檔 的其餘部分稱為stand-alone，以便從一組codelet中區分開)。
- 一類關聯到一組codelets。這些標籤說明如下: 「「, 其中「LabelOfGroup」是由使用者指定一個名稱。具有這種標籤的指令一般涉及到整個組。組的概念是保留給這樣一類問題，即要求對整個應用程式的數據做具體管理以獲取性能。

### OpenHMPP指令語法

為了簡化符號, 正則表達式 將用於描述HMPP指令的語法。

下面的顏色通常用於描述指令的語法:

- 保留的HMPP關鍵字是藍色;
- 在HMPP關鍵字中可以被減少的基本語法是紅色;
- 使用者變量仍然為黑色。

#### 一般語法

OpenHMPP指令的一般語法如下:

```
<font color="#0070C0">#pragma hmpp <</font>grp_label<font color="#0070C0">></font> [codelet_label]? <font color="990000">directive_type</font> [<font color="#0070C0">,</font><font color="990000">directive_parameters</font>]* [<font color="#0070C0">&</font>]
```

```
<font color="#0070C0">!$hmpp <</font>grp_label<font color="#0070C0">></font> [codelet_label]? <font color="990000">directive_type</font> [<font color="#0070C0">,</font><font color="990000">directive_parameters</font>]* [<font color="#0070C0">&</font>]
```

其中:

- `<grp_label>`: 是命名一組 codelets 的唯一標識符。在應用程式中沒有組被定義的情況下, 這個標籤可以簡單地略過。Legal標籤必須遵循此語法: a-z,A-Z,_a-z,A-Z,0-9,_*。請注意 字符屬於語法，且對這類標籤是強制性的。
- `codelet_label`: 是命名一個codelet的唯一標識符。Legal 標籤必須遵循此語法: a-z,A-Z,_a-z,A-Z,0-9,_*
- `directive`: 是指令的名稱;
- `directive_parameters`: 指定一些指令相關聯的參數。這些參數可能是不同類型並指定 給指令的某些參數或執行的一種模式(例如同步與異步);
- `[&]`: 是用於在下一行繼續該指令的字符 (C和FORTRAN都是相同的)。

#### 指令參數

關聯到一個指令的參數可能是不同類型。以下是 在HMPP中定義的指令參數:

- `version = major.minor[.micro]`: 由預處理器指定HMPP指令的版本。
- `args[arg_items].size={dimsize[,dimsize]*}`: 指定一個非標量參數 (陣列)的大小。
- `args[arg_items].io=[in|out|inout]`: 表示指定函數參數是輸入, 輸出或兩者兼而有之。默認情況下, 非限定參數為輸入。
- `cond = "expr"`: 指定組或codelets開始執行的一個條件C或Fortran布爾表達式的值為是 C或Fortran布爾表達式的值為true。
- `target=target_name[:target_name]*`: 指定是哪個target以嘗試使用給定的順序。
- `asynchronous`: 指定不阻止codelet的 執行 (默認是同步的)。
- `args[<arg_items>].advancedload=true`: 表明指定的參數是預加載的。只有in或inout參數可以被預加載。
- `args[arg_items].noupdate=true`: 此屬性指定 硬體上的數據已經可用，因此不需要轉換。當設置了此屬性時, 所考慮的參數沒有任何傳遞。
- `args[<arg_items>].addr="<expr>"`: `<expr>`是一個表達式，給出了數據上載的地址。
- `args[<arg_items>].const=true`: 表示參數只要上載一次。

## OpenHMPP 指令

### 聲明和執行一個codelet的指令

codelet指令聲明在硬體加速器上遠程執行計算。

codelet 指令:

- codelet標籤是強制性的並且在應用程式中是唯一的
- 如果沒有組被定義則不需要組標籤。
- Codelet指令在函數聲明之前插入。

該指令的語法是:

```
<font color="#0070C0">#pragma hmpp <</font>grp_label<font color="#0070C0">></font> codelet_label <font color="#0070C0">codelet</font>
                            [<font color="#0070C0">, version</font> = major.minor[.micro]?]?
                            [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].io=</font>[[<font color="#0070C0">in</font>|<font color="#0070C0">out</font>|<font color="#0070C0">inout</font>]]*
                            [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].size={</font>dimsize[,dimsize]*<font color="#0070C0">}</font>]*
                            [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].const=true</font>]*
                            [<font color="#0070C0">, cond =</font> "expr"]
                            [<font color="#0070C0">, target=</font><font color="#990000">target_name</font>[:<font color="#990000">target_name</font>]*]
```

可以在一個函數中加入多個codelet指令，以便指定不同用途或不同執行文本。但是, 一個給定調用站點標籤只能有一個codelet指令。

Callsite指令指定程序內的給定點如何使用一個codelet。

該指令的語法是:

```
<font color="#0070C0">#pragma hmpp <</font>grp_label<font color="#0070C0">></font> codelet_label <font color="#0070C0">callsite</font>
                     [<font color="#0070C0">, asynchronous</font>]?
                     [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].size={</font>dimsize[,dimsize]*<font color="#0070C0">}</font>]*
                     [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].advancedload=</font>[[<font color="#0070C0">true</font>|<font color="#0070C0">false</font>]]*
                     [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].addr="</font>expr<font color="#0070C0">"</font>]*
                     [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].noupdate=true</font>]*
```

這裡有一個例子:

/* declaration of the codelet */

```
#pragma hmpp simple1 codelet, args[outv].io=inout, target=CUDA
static void matvec(int sn, int sm, loat inv[sm], float inm[sn][sm], float *outv){
    int i, j;
    for (i = 0 ; i < sm ; i++) {
      float temp = outv[i];
      for (j = 0 ; j < sn ; j++) {
        temp += inv[j] * inm[i][ j];
    }
   outv[i] = temp;
 }

 int main(int argc, char **argv) {
   int n;
   ........

 /* codelet use */
 #pragma hmpp simple1 callsite, args[outv].size={n}
 matvec(n, m, myinc, inm, myoutv);
   ........
 }
```

某些情況下, 需要具體管理整個應用程式的數據(CPU/GPU 數據移動優化, 共享變量...)。

group指令允許聲明一組codelets。指令中定義的參數應用於所有屬於該組的 codelets。

該指令的語法是:

```
<font color="#0070C0">#pragma hmpp <</font>grp_label<font color="#0070C0">> group</font>
                          [<font color="#0070C0">, version =</font> <major>.<minor>[.<micro>]?]?
                          [<font color="#0070C0">, target =</font> <font color="#990000">target_name</font>[:<font color="#990000">target_name</font>]*]]?
                          [<font color="#0070C0">, cond  = “</font>expr<font color="#0070C0">”</font>]?
```

### 數據傳輸指令可以優化通信開銷

硬體使用時的主要瓶頸通常是硬體和住處理器之間的數據傳輸。

要限制通信開銷,可以通過使用硬體的異步屬性，連續執行相同的codelet以重疊數據傳輸。

- allocate指令

allocate指令鎖定硬體，並分配所需的記憶體量。

- pragma hmpp allocate ,argsarg_items.size={dimsize,dimsize*}*
- release指令

release指令指定何時為一組或一個獨立codelet釋放硬體。

```
<font color="#0070C0">#pragma hmpp <</font>grp_label<font color="#0070C0">> release</font>
```

- advancedload 指令

advancedload指令在codelet遠程執行之前預取數據。

- pragma hmpp codelet_label? advancedload

```
                  <font color="#0070C0">,args[</font>arg_items<font color="#0070C0">]</font>
                  [<font color="#0070C0">,args[</font>arg_items<font color="#0070C0">].size={</font>dimsize[,dimsize]*<font color="#0070C0">}</font>]*
                  [<font color="#0070C0">,args[</font>arg_items<font color="#0070C0">].addr="</font>expr<font color="#0070C0">"</font>]*
                  [<font color="#0070C0">,args[</font>arg_items<font color="#0070C0">].section={</font>[<font color="#990000">subscript_triplet</font><font color="#0070C0">,</font>]+<font color="#0070C0">}</font>]*
                  [<font color="#0070C0">,asynchronous</font>]
```

- delegatedstore 指令

delegatedstore指令是一個同步障，以等待一個異步codelet執行完成，然後下載結果。

- pragma hmpp codelet_label? delegatedstore

```
                <font color="#0070C0">,args[</font>arg_items<font color="#0070C0">]</font>
                [<font color="#0070C0">,args[</font>arg_items<font color="#0070C0">].addr="</font>expr<font color="#0070C0">"</font>]*
                [<font color="#0070C0">,args[</font>arg_items<font color="#0070C0">].section={</font>[<font color="#990000">subscript_triplet</font><font color="#0070C0">,</font>]+<font color="#0070C0">}</font>]*
```

- 異步計算

同步指令指定等待，直到一個異步callsite執行完成。對於同步指令, codelet 標籤始終是強制性的，並且若是codelet屬於一個組，需要有組標籤。

- pragma hmpp codelet_label synchronize
- 示例

在下面的例子中，完成設備初始化，記憶體分配和輸入數據的上載在循環外只有一次，而不是每次循環迭代。

同步指令允許在啟動另一個迭代之前等待codelet的異步執行的完成。最後在循環外delegatedstore指令將上載sgemm結果。

int main(int argc, char **argv) {

```
#pragma hmpp sgemm allocate, args[vin1;vin2;vout].siez={size,size}
#pragma hmpp sgemm advancedload, args[vin1;vin2;vout], args[m,n,k,alpha,beta]

for ( j = 0 ; j < 2 ; j ++) {
   #pragma hmpp sgemm callsite, asynchronous, args[vin1;vin2;vout].advancedload=true, args[m,n,k,alpha,beta].advancedload=true
   sgemm (size, size, size, alpha, vin1, vin2, beta, vout);
   #pragma hmpp sgemm  synchronize
}

#pragma hmpp sgemm delegatedstore, args[vout]
#pragma hmpp sgemm release
```

### Codelets之間共享數據

這些指令共同映射所有參數共享所有組的給定名稱。

所有映射參數的類型和尺寸必須是相同的。

map指令映射設備上的幾個參數。

- pragma hmpp map, argsarg_items

此指令除了參數按其名稱直接指定映射之外，與map指令很類似。mapbyname 指令相當於多映射指令。

```
<font color="#0070C0">#pragma hmpp <</font>grp_label<font color="#0070C0">> mapbyname</font> [<font color="#0070C0">,</font>variableName]+
```

### 全局變量

Resident指令聲明某些變量在一個組內為全局變量。然後可以從任何屬於組的codelet中直接訪問這些變量。此指令應用於原始碼中在其之後的聲明語句。

此指令的語法是:

```
<font color="#0070C0">#pragma hmpp <</font>grp_label<font color="#0070C0">> resident</font>
               [<font color="#0070C0">, args[::</font>var_name<font color="#0070C0">].io=</font>[[<font color="#0070C0">in</font>|<font color="#0070C0">out</font>|<font color="#0070C0">inout</font>]]*
               [<font color="#0070C0">, args[::</font>var_name<font color="#0070C0">].size={</font>dimsize[,dimsize]*<font color="#0070C0">}</font>]*
               [<font color="#0070C0">, args[::</font>var_name<font color="#0070C0">].addr="</font>expr<font color="#0070C0">"</font>]*
               [<font color="#0070C0">, args[::</font>var_name<font color="#0070C0">].const=true</font>]*
```

符號::var_name 以::為前綴, 表示一個應用程式的變量聲明為resident。

### 加速區

codelet/callsite指令合併為一個區域。目的是避免代碼重構中構建codelet。因此，所有codelet或callsite指令可用屬性都可以用於regions指令。

在C語言中:

```
<font color="#0070C0">#pragma hmpp [<</font>MyGroup<font color="#0070C0">>] [</font>label<font color="#0070C0">] region</font>
                           [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].io=</font>[[<font color="#0070C0">in</font>|<font color="#0070C0">out</font>|<font color="#0070C0">inout</font>]]*
                           [<font color="#0070C0">, cond = "</font>expr<font color="#0070C0">"]</font><
                           [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].const=true</font>]*
                           [<font color="#0070C0">, target=</font><font color="#990000">target_name</font>[<font color="#0070C0">:</font><font color="#990000">target_name</font>]*]
                           [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].size={</font>dimsize[<font color="#0070C0">,</font>dimsize]*<font color="#0070C0">}</font>]*
                           [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].advancedload=</font>[[<font color="#0070C0">true</font>|<font color="#0070C0">false</font>]]*
                           [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].addr="</font>expr<font color="#0070C0">"</font>]*
                           [<font color="#0070C0">, args[</font>arg_items<font color="#0070C0">].noupdate=true</font>]*
                           [<font color="#0070C0">, asynchronous</font>]?
                           [<font color="#0070C0">, private=[</font>arg_items<font color="#0070C0">]</font>]*
   {
C BLOCK STATEMENTS
   }
```

## 實現

HMPP開放標準基於HMPP 2.3版本(2009年5月, CAPS 公司).

HMPP基於指令的編程模型已經實現如下:

- HMPP 工作檯, 用於混合計算的CAPS企業編譯器
- PathScale ENZO 編譯器套件(支持NVIDIA GPUs)

此外，HMPP開放標準用於石油和天然氣，能源，製造業，金融，教育及研究領域的高性能計算，讓開發人員使用大部分多核處理器，同時保留其遺留資源。

## 參閱

- 圖形處理器通用計算
- 並行計算

## 參考

- OpenHMPP Website
- HMPP: A Hybrid Multi-core Parallel Programming Environment
- CAPS Entreprise SAS and PathScale, LLC to Jointly Collaborate on Making HMPP a New Open Standard
- How Hardware Will Shape Languages By David Chisnall
- Code acceleration with HMPP By ICHEC (Irish Center for High-End Computing)
- Expérience de programmation avec HMPP By IRMA (Institut de Recherche Mathématique Avancée) - FORTRAN examples
- Directive-based Heterogeneous Programming - A GPU-Accelerated RTM Use Case By TOTAL Technical and Scientific Center and CAPS Entreprise
- HMPP Port By CEA (Commissariat à l'Energie Atomique et aux Energies Alternatives) for PRACE (Partnership for Advanced Computing in Europe)