Accueil🇫🇷Chercher

OpenHMPP

Le Standard Ouvert OpenHMPP (HMPP pour Hybrid Multicore Parallel Programming) est un modèle de programmation basé sur un jeu de directives, conçu pour manipuler les accélérateurs matériels sans se soucier de la complexité associée à la programmation GPU. Le choix d’implémentation concernant ce modèle s’est porté sur les directives car elles permettent une relation déliée entre le code de l’application et l’utilisation des accélérateurs matériels.

NB : Cet article porte sur les directives OpenHMPP qui constituent l'Open Standard, mais ne traite pas de l'exécution des directives, liée à l'implémentation des directives.

Introduction

La syntaxe offerte par le modèle OpenHMPP permet de répartir efficacement les calculs sur les accélérateurs matériels et d’optimiser les mouvements de données de/vers la mémoire du matériel.

Le modèle est basé sur le projet CAPS (Compiler and Architecture for Embedded and Superscalar Processors) mené en commun par l'INRIA, le CNRS, l'Université de Rennes 1 et l'INSA de Rennes.

Le concept du standard OpenHMPP

Le standard OpenHMPP est basé sur le concept de codelet, qui est une fonction qui peut être exécutée à distance sur le matériel.

Le concept de codelet

Un codelet a les propriétés suivantes :

  1. C'est une fonction pure.
    • Il ne contient pas de dĂ©clarations de variables statiques ou volatiles et ne fait aucune rĂ©fĂ©rence Ă  des variables globales, exception faite des variables dĂ©clarĂ©es comme "resident" par une directive OpenHMPP.
    • il ne contient pas d'appels Ă  des fonctions avec un contenu non visible (c'est-Ă -dire qu'il ne peut pas ĂŞtre inlinĂ©). Cela inclut l'utilisation de librairies et de fonctions systèmes telles que malloc, printf…
    • Chaque appel Ă  la fonction doit faire rĂ©fĂ©rence Ă  une fonction statique pure (pas de pointeurs de fonction).
  2. Il ne retourne aucune valeur (l'Ă©quivalent en C est une fonction void; l'Ă©quivalent en FORTRAN est une subroutine).
  3. Le nombre d'arguments doit être fixé (pas de vararg comme en C).
  4. Il n'est pas récursif.
  5. Ces paramètres sont supposés ne pas être aliasés (au sens pointeur du terme).
  6. Il ne contient pas de directives callsite (c'est-Ă -dire pas d'appel RPC Ă  un autre codelet) ou d'autres directives HMPP.

Ces propriétés permettent de s'assurer qu'un codelet RPC peut être exécuté à distance par un matériel. Cet appel RPC et ses transferts de données associés peuvent être asynchrones.

L'exécution RPC d'un codelet

OpenHMPP fournit un protocole d'appel de procédures à distance synchrone et asynchrone.

L'implémentation d'opération asynchrone est dépendante du matériel.

Synchronous versus asynchronous RPC

Le modèle de mémoire mis en œuvre

OpenHMPP prend en compte deux espaces d'adressage :

  • celui du processeur hĂ´te
  • celui de la mĂ©moire du matĂ©riel.
HMPP memory Model

Les directives HMPP

Les directives OpenHMPP peuvent être vues comme des “méta-informations” que l’on ajoute dans le code source de l’application. Ce sont donc des méta-informations inoffensives, c’est-à-dire qu’elle ne changent pas le comportement original du code.

Elles adressent l’exécution à distance (RPC) de la fonction ainsi que les transferts de données de/vers la mémoire du matériel. Le tableau qui suit introduit les directives OpenHMPP, classées selon le besoin adressé : certaines sont destinées à des déclarations, d’autres sont destinées à la gestion de l’exécution.

Directives HMPP
Instructions de contrôle de flux Directives pour la gestion des données
DĂ©clarations codelet group resident map mapbyname
Directives opérationnelles callsite synchronize region allocate release advancedload delegatedstore

Concept de jeu de directives

L’un des points fondamentaux de l’approche proposée par le modèle OpenHMPP est le concept de directives, associées à des labels, qui rendent possible la mise en place d’une structure cohérente pour un jeu de directives entier, disséminé dans l’application.

Il existe deux types de labels :

  • Le premier est associĂ© Ă  un codelet. En gĂ©nĂ©ral, les directives portant ce genre de label sont limitĂ©es Ă  la gestion d’un seul codelet, appelĂ© codelet autonome dans la suite de l’article (pour le distinguer d’un groupe de codelets).
  • Le second est associĂ© Ă  un groupe de codelets. Ces labels sont notĂ©s comme suit : “<LabelOfGroup>“, oĂą “LabelOfGroup” est un nom spĂ©cifiĂ© par l’utilisateur. En gĂ©nĂ©ral, les directives avec un label de cet type se rapportent au groupe entier. Le concept de groupe est rĂ©servĂ© Ă  une classe de problèmes qui nĂ©cessitent une gestion spĂ©cifique des donnĂ©es Ă  travers l'ensemble de l’application, dans le but d'amĂ©liorer les performances.

Syntaxe des directives OpenHMPP

Dans le but de simplifier les notations, les expressions régulières seront utilisées pour décrire la syntaxe des directives OpenHMPP.

Les conventions de couleurs suivantes seront également utilisées :

  • Les mots-clĂ©s rĂ©servĂ©s Ă  HMPP sont en bleu;
  • Les Ă©lĂ©ments de la grammaire qui peuvent ĂŞtre dĂ©clinĂ©s en mots-clĂ©s HMPP sont en rouge;
  • les variables de l’utilisateur reste en noir.

Syntaxe générale

La syntaxe générale des directives OpenHMPP est :

  • Pour le langage C :
#pragma hmpp <grp_label> [codelet_label]? directive_type [,directive_parameters]* [&]
  • Pour le langage FORTRAN :
!$hmpp <grp_label> [codelet_label]? directive_type [,directive_parameters]* [&]

oĂą :

  • <grp_label> est un identifiant unique reprĂ©sentant un groupe de codelets. Dans le cas oĂą aucun groupe n’est dĂ©fini dans l’application, ce label peut ĂŞtre absent. Pour ĂŞtre valide, le nom du label doit obligatoirement suivre la grammaire suivante : [a-z,A-Z,_][a-z,A-Z,0-9,_]*. Notez que les deux caractères < et > appartiennent Ă  la syntaxe et sont obligatoires dans ce contexte de label ;
  • codelet_label est un identifiant unique nommant un codelet. Pour ĂŞtre valide, le nom du label doit obligatoirement suivre la grammaire suivante : [a-z,A-Z,_][a-z,A-Z,0-9,_]*
  • directive est le nom de la directive ;
  • directive_parameters dĂ©signe les paramètres associĂ©s Ă  la directive. Ces paramètres peuvent ĂŞtre de diffĂ©rents types et spĂ©cifient soit des arguments passĂ©s Ă  la directive soit un mode d’exĂ©cution (asynchrone ou synchrone par exemple) ;
  • [&] est un caractère utilisĂ© pour indiquer que la directive continue sur la ligne suivante (identique pour le C et le FORTRAN).

Paramètres des directives

Les paramètres associés à une directive peuvent avoir plusieurs types.

À suivre, les paramètres définis avec OpenHMPP :

  • version = major.minor[.micro]: spĂ©cifie la version des directives HMPP prise en compte par le prĂ©processeur;
  • args[arg_items].size={dimsize[,dimsize]*}: spĂ©cifie la taille des paramètres non scalaires (un tableau);
  • args[arg_items].io=[in|out|inout]: indique le statut des arguments de la fonction spĂ©cifiĂ©e (input, output ou les deux). Par dĂ©faut, les arguments non qualifiĂ©s sont des input;
  • cond = "expr": spĂ©cifie une condition pour l’exĂ©cution, sous forme d’une expression boolĂ©enne C ou FORTRAN qui doit ĂŞtre vraie pour que l’exĂ©cution du groupe ou du codelet commence;
  • target=target_name[:target_name]*: spĂ©cifie la liste des cibles Ă  essayer d’utiliser, l’ordre d’apparition Ă©tant l’ordre de vĂ©rification de la disponibilitĂ©;
  • asynchronous: spĂ©cifie que l’exĂ©cution du codelet n’est pas bloquante (par dĂ©faut, synchrone);
  • args[<arg_items>].advancedload=true: indique que les paramètres spĂ©cifiĂ©s sont prĂ©-chargĂ©s. Seuls les paramètres ayant pour statut in ou inout peuvent ĂŞtre prĂ©-chargĂ©s;
  • args[arg_items].noupdate=true: spĂ©cifie que la donnĂ©e est dĂ©jĂ  disponible sur le matĂ©riel et que son transfert n’est donc pas nĂ©cessaire. Lorsque cette propriĂ©tĂ© est fixĂ©e, aucun transfert n’est fait sur l’argument considĂ©rĂ©;
  • args[<arg_items>].addr="<expr>": est une expression fournissant explicitement l’adresse de la donnĂ©e;
  • args[<arg_items>].const=true: indique que l’argument doit ĂŞtre tĂ©lĂ©chargĂ© une et une seule fois.

Les directives OpenHMPP

Déclaration et exécution d'un codelet

Une directive codelet spécifie qu'une version de la fonction qui suit doit être optimisée pour un matériel spécifié.

Pour la directive codelet :

  • Le label du codelet est obligatoire et doit ĂŞtre unique dans l'application.
  • Le label du groupe n'est pas obligatoire si aucun groupe n'est dĂ©fini.
  • La directive doit ĂŞtre insĂ©rĂ©e immĂ©diatement avant la dĂ©claration de la fonction concernĂ©e.

La syntaxe de la directive est:

#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]*]

Il est possible d'avoir plusieurs directives codelet pour une fonction donnée, chacune spécifiant différents usages ou différents contextes d'exécution. Toutefois, il ne peut y avoir qu'une directive callsite pour un label codelet donné.

La directive callsite spécifie l'usage d'un codelet à un point donné dans le programme.

La syntaxe de la directive est:

#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]*

Un exemple Ă  suivre :

/* déclaration du 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;
   ........
 /* Utilisation du codelet */
 #pragma hmpp simple1 callsite, args[outv].size={n}
 matvec(n, m, myinc, inm, myoutv);
   ........
 }

Dans certains cas, une gestion spécifique des données est nécessaire dans l'application (optimisation des mouvements de données entre CPU et GPU, variables partagées…).

La directive group permet la déclaration d'un groupe de codelets. Les paramètres définis pour cette directive sont appliqués à tous les codelets associés à ce groupe.

La syntaxe de la directive est:

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

Directives appliquées aux transferts de données (optimisation des surcoûts liés aux communications)

Le principal goulet d'étranglement lors de l'utilisation du HWA réside souvent dans les transferts de données entre le matériel et le processeur principal.

Pour limiter les surcoûts liés aux communications, les transferts de données peuvent être communes à des exécutions successives d'un même codelet en utilisant la propriété asynchrone du matériel.

  • la directive « allocate »

Elle verrouille le matériel et alloue la quantité de mémoire nécessaire.

#pragma hmpp <grp_label> allocate [,args[arg_items].size={dimsize[,dimsize]*}]*
  • la directive « release »

Elle spécifie à quel moment le matériel doit être libéré pour un groupe ou pour un codelet autonome.

#pragma hmpp <grp_label> release
  • la directive « advancedload »

Elle charge les données avant l'exécution à distance du 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]
  • la directive « delegatedstore »

Elle constitue une barrière synchrone qui permet d'attendre la complétion de l'exécution d'un codelet asynchrone puis avant de télécharger les résultats.

#pragma hmpp <grp_label> [codelet_label]? delegatedstore 
                ,args[arg_items]
                [,args[arg_items].addr="expr"]*
                [,args[arg_items].section={[subscript_triplet,]+}]*
  • Calculs asynchrones

La directive synchronize spécifie qu'il faut attendre la fin de l'exécution asynchrone du callsite.
Pour cette directive, le label du codelet est toujours obligatoire et le label group est nécessaire si le codelet est associé à un groupe. La syntaxe de la directive est:

#pragma hmpp <grp_label> codelet_label synchronize
  • Exemple

Dans l'exemple qui suit, l'initialisation du matériel, l'allocation de la mémoire et le téléchargement des données d'entrée sont effectués une seule fois, en dehors de la boucle au lieu d'être effectués à chaque itération de la boucle.
La directive synchronize permet d'attendre la fin de l'exécution asynchrone du codelet avant de démarrer une autre itération. Finalement, la directive delegatedstore, placée en dehors de la boucle, télécharge le résultat de "sgemm".

int main(int argc, char **argv) {
#pragma hmpp sgemm allocate, args[vin1;vin2;vout].size={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

Partage de données entre codelets

Ces directives permettent de partager tous les arguments ayant le mĂŞme nom pour tout un groupe.

Les types et dimensions de tous les arguments partagés doivent être identiques.

La directive map permet d'associer plusieurs arguments sur le matériel.

#pragma hmpp <grp_label>  map, args[arg_items]

La directive mapbyname est semblable à la directive map sauf que les arguments à mapper sont directement spécifiés par leur nom. La directive mapbyname est équivalente a de multiples directives map.

La notation est la suivante :

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

Variable globale

La directive resident déclare des variables comme globales au sein d'un groupe.

Ces variables peuvent être directement accédées à partir de n'importe quel codelet du groupe sur le matériel (elles sont en quelque sorte considérées comme "résidentes" sur le matériel).

Cette directive s'applique à la déclaration qui la suit dans le code source.

La syntaxe de cette directive est :

#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]*

La notation ::var_name, avec le préfixe ::, indique une variable d'application déclarée comme resident.

Accélération de région

Une région est un mélange des directives codelet/callsite. Son but est d'éviter la restructuration du code imposée par la création explicite des codelets. Par conséquent, tous les attributs disponibles pour les directives codelet ou callsite peuvent être utilisés pour la directive region.

La syntaxe est la suivante :

#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
   }

Implémentations

OpenHMPP est basé sur la version 2.3 de HMPP (, CAPS entreprise).

Le modèle proposé par OpenHMPP est implémenté par :

  • CAPS Compilers, les compilateurs fournis par CAPS Entreprise pour le calcul hybride
  • PathScale ENZO Compiler Suite (support des GPU NVIDIA)

De plus, OpenHMPP est utilisé dans le cadre de projet HPC mené dans des domaines tels que le pétrole, l'énergie, l'industrie, l'éducation et la recherche et permet de développer des versions hautes performances de leurs applications, tout en préservant le code déjà produit.

Voir aussi

Références

Cet article est issu de wikipedia. Text licence: CC BY-SA 4.0, Des conditions supplémentaires peuvent s’appliquer aux fichiers multimédias.