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。异步操作的执行依赖于硬件。

<image>

### HMPP 存储器模型

HMPP考虑到两个地址空间: 一个主机处理器和硬件存储器。

<image>

### 指令概念

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)