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。異步操作的執行依賴於硬件。

 
Synchronous versus asynchronous RPC

HMPP 存儲器模型

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

 
HMPPP memory Model

指令概念

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

流程控制指令 數據管理標籤
聲明 codelet
group
resident
map
mapbyname
操作標籤 callsite
synchronize
region
allocate
release
advancedload
delegatedstore

指令集的概念

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

參閱

參考