HMPP开放标准

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

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

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

## 簡介

OpenHMPP 基於指令的編程模型提供了一種語法，有效地減輕硬體加速器上的計算，優化數據向 / 從硬體存儲器移動。 
模型基於 CAPS (編譯器和超純量體系結構以及嵌入式處理器)（頁面存檔備份，存於網際網路檔案館） 的初始化工作，以及來自 INRIA, CNRS, 雷恩第一大學和雷恩 INSA 的共同項目。

## OpenHMPP 標準概念

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

### OpenHMPP Codelet 概念

codelet 具有以下屬性:

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

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

### Codelet RPCs

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

### HMPP 存儲器模型

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

### 指令概念

OpenHMPP 指令可能被視為「元信息」 添加到應用程式原始碼。它們是安全的元信息，即不會改變原始代碼的行為。它們處理函數的遠程執行 (RPC)，以及數據向 / 從硬體存儲器傳輸。 
下表介紹了 OpenHMPP 指令。OpenHMPP 指令滿足不同需求：其中一些專門用於聲明，其他用於執行的管理。

### 指令集的概念

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

有兩種類型的標籤:

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

### OpenHMPP 指令語法

為了簡化符號，正則表達式 將用於描述 HMPP 指令的語法。
下面的顏色通常用於描述指令的語法:

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

#### 一般語法

OpenHMPP 指令的一般語法如下:

* C 語言:

```
#pragma hmpp <grp_label> [codelet_label]? directive\_type [,directive\_parameters]* [&]
```

* FORTRAN 語言:

```
!$hmpp <grp_label> [codelet_label]? directive\_type [,directive\_parameters]* [&]
```

其中:

* `<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 指令在函數聲明之前插入。

該指令的語法是:

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

可以在一個函數中加入多個 codelet 指令，以便指定不同用途或不同執行文本。但是，一個給定調用站點標籤只能有一個 codelet 指令。
Callsite 指令指定程序內的給定點如何使用一個 codelet。 
該指令的語法是:

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

這裡有一個例子:

/\* 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。

該指令的語法是:

```
#pragma hmpp <grp_label> group 
                          [, version = <major>.<minor>[.<micro>]?]? 
                          [, target = target\_name[:target\_name]*]]? 
                          [, cond = “expr”]?
```

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

硬體使用時的主要瓶頸通常是硬體和住處理器之間的數據傳輸。 
要限制通信開銷，可以通過使用硬體的異步屬性，連續執行相同的 codelet 以重疊數據傳輸。

* allocate 指令

allocate 指令鎖定硬體，並分配所需的內存量。#pragma hmpp <grp\_label> allocate [,args[arg\_items].size={dimsize[,dimsize]\*}]\*

* release 指令

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

```
#pragma hmpp <grp_label> release
```

* advancedload 指令

advancedload 指令在 codelet 遠程執行之前預取數據。#pragma hmpp <grp\_label> [codelet\_label]? advancedload

```
                  ,args[arg_items]
                  [,args[arg_items].size={dimsize[,dimsize]*}]*
                  [,args[arg_items].addr="expr"]*
                  [,args[arg_items].section={[subscript\_triplet,]+}]*
                  [,asynchronous]
```

* delegatedstore 指令

delegatedstore 指令是一個同步障，以等待一個異步 codelet 執行完成，然後下載結果。#pragma hmpp <grp\_label> [codelet\_label]? delegatedstore

```
                ,args[arg_items]
                [,args[arg_items].addr="expr"]*
                [,args[arg_items].section={[subscript\_triplet,]+}]*
```

* 異步計算

同步指令指定等待，直到一個異步 callsite 執行完成。對於同步指令，codelet 標籤始終是強制性的，並且若是 codelet 屬於一個組，需要有組標籤。#pragma hmpp <grp\_label> 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 <grp\_label> map, args[arg\_items]

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

```
#pragma hmpp <grp_label> mapbyname [,variableName]+
```

### 全局變量

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

此指令的語法是:

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

符號::var\_name 以：為前綴，表示一個應用程式的變量聲明為 resident。

### 加速區

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

```
#pragma hmpp [<MyGroup>] [label] region         
                           [, args[arg_items].io=[[in|out|inout]]*
                           [, cond = "expr"]<
                           [, args[arg_items].const=true]*
                           [, target=target\_name[:target\_name]*]
                           [, args[arg_items].size={dimsize[,dimsize]*}]*
                           [, args[arg_items].advancedload=[[true|false]]*
                           [, args[arg_items].addr="expr"]*
                           [, args[arg_items].noupdate=true]*
                           [, asynchronous]?
                           [, private=[arg_items]]*
   {
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)