Transformation de programmes
Transcription
Transformation de programmes
TELECOM SUD PARIS Transformation de programmes Génération d'un programme multi-GPU MPI+HMPP à partir d'un programme GPU avec des directives HMPP Thean Rémy – Marechal Victor Encadrant : Frédérique Silber-Chaussumier 21/01/2012 Sommaire Introduction ................................................................. 3 1 – Contexte du projet ................................................................... 4 a) Objectifs du projet ............................................................................................ 4 b) STEP et son parser OpenMP ............................................................................. 4 c) CAPS HMPP ....................................................................................................... 8 d) HMPP et STEP ................................................................................................... 9 2 – Travaux réalisés .................................................... 10 a) Ecriture d’un parser OpenMP ......................................................................... 10 1) Partie Flex ....................................................................................................................... 10 2) Partie Bison .................................................................................................................... 11 3) Exemple d’exécution du parser OpenMP ...................................................................... 12 b) Etude et prise en main de HMPP ..................................................................... 13 c) Parser HMPP .................................................................................................. 14 1) Ecriture du parser ........................................................................................................... 14 2) Exemple d’exécution du parser HMPP ........................................................................... 15 c) Préparation à l’intégration des deux parsers ................................................... 16 3 - Bilan du projet ........................................................ 19 a) Rendu du projet ............................................................................................. 19 a) Difficultés rencontrés durant le projet ............................................................. 19 a) Temps de travail ............................................................................................. 19 Conclusion .................................................................. 20 2 Introduction Au sein de Telecom Sudparis, un outil de transformation de programme appelé STEP a été développé. L’objectif principal de cet outil est de réaliser une parallélisation semiautomatique d’un code source afin de faciliter la parallélisation et ainsi réduire le temps de calcul d’un programme C ou Fortran. Pour le moment, cet outil peut à partir d’un code C ou Fortran contenant des instructions OpenMP transformer ce code source en un nouveau code source parallélisé de manière hybride, c'est-à-dire, contenant à la fois des instructions OpenMP mais aussi des instructions MPI. Maintenant, l’équipe en charge de STEP souhaite étendre leur outil pour permettre le partage des calculs sur la GPU de plusieurs machines. Pour cela, ils font fait le choix d’utiliser CAPS HMPP, un programme permettant de réaliser un code s’exécutant sur la GPU à partir d’un code dont on a ajouté des instructions HMPP. C’est là qu’intervient notre projet de fin d’étude, notre rôle est d’intégrer dans STEP la possibilité de transformer un programme HMPP. Dans ce document, afin d’expliciter le déroulement de notre projet, nous allons tout d’abord aborder le contexte et les objectifs du projet, pour nous concentrer sur le travail réalisé et enfin terminer par un bilan concernant le projet. 3 1 – Contexte du projet a) Objectifs du projet L’objectif principal du projet est de pouvoir intégrer dans STEP, une solution similaire à celle pour OpenMP. Pour cela, notre projet a été divisé en plusieurs phases : Etude de STEP, sa structure et son fonctionnement. Etude et prise en main de HMPP. Ecriture d’un parser OpenMP afin de remplacer celui de STEP. Ecriture d’un parser HMPP. Intégration de ces deux parser dans STEP. b) STEP et son parser OpenMP STEP ou est un outil de transformation source à source inscrit dans un projet des écoles des Mines appelé PIPS. STEP utilisent les modules de PIPS pour ses analyses de structures et les transformations qu’il réalise sur un code source. Le principal objectif de STEP de l’outil STEP est de faciliter et accélérer la parallelisation, pour cela, son rôle est de transformer un code source C ou Fortran écrit avec des instructions OpenMP en un code source dans le même langage avec des instructions OpenMP + MPI. Les transformations de codes que réalise STEP a pour but de modifier la manière dont est exécuté le programme issue d’un code source. Ainsi, un code source produit par STEP, divisera son calcul qui doit être réalisé en parallèle sur une machine en un calcul en parallèle sur plusieurs machines. La partie du calcul divisé sur plusieurs machines comprend uniquement les parties du code entourées pas les instructions parallèles. Les autres opérations non parallèles sont réalisées par toutes les machines. MPI intervient dans l’objectif d’obtenir un résultat semblable à une exécution normale du code source, pour cela, après la région parallèle, MPI est utilisé afin de mettre les différentes variables à jour chaque région parallèle du code. Au final, chaque machine se retrouvera avec le même résultat qu’une exécution normale du code aurait donné, cependant, le temps d’exécution du code est réduit. Le schéma (figure 1) suivant explicite bien la transformation que subit le code source au niveau de son exécution après avoir été transformé par STEP. 4 Figure 1 – Modèle d’exécution de STEP Parlons maintenant plus précisément du fonctionnement de STEP. STEP, avant son travail de transformation, réalise tout d’abord une représentation du code en une représentation dite intermédiaire (RI). STEP travaillera ensuite sur cette représentation intermédiaire pour modifier réaliser sa transformation. La transformation que réalise STEP peut se diviser en 3 grandes étapes Outlining : détecte les sections parallèles et change la structure du code source. Cela a pour but de hiérarchiser le code. Analyse des régions : détermine les données à être échangées via MPI ; Génération du code MPI Dans le cadre de notre projet, nous nous intéressons plutôt à l’étape consistant de faire une représentation du code en une représentation intermédiaire (RI). En effet, le parser OpenMP est appelé afin de pouvoir réaliser cette représentation intermédiaire. Concentrons nous donc plus sur le détail du parser OpenMP de STEP. Le parser STEP est un parser écrit totalement en langage Flex. Il est contenu dans un fichier step_omp.l contenu dans la bibliothèque de STEP. Ce parser est le même pour le langage C et pour le langage Fortran. Le rôle du parser OpenMP est de réaliser une analyse d’un code source en C ou Fortran contenant des instructions (statement) OpenMP/STEP, et selon l’instruction (statement) détectée fait appel à des fonctions définis dans le même fichier. A l’entrée du parser, on lui entre une instruction détectés préalablement par STEP grâce à la fonction pragma_omp_handle(). Le parser détecte ensuite la structure de l’instruction grâce à l’utilisation de machines à états dés la détection d’un certain mot clé. 5 Les mots clés détectés par l’analyseur peuvent se diviser en 3 grandes catégories + 2 catégories permettant la gestion des clauses OpenMP. Ces 3 grandes catégories sont : Les directives STEP (indique le type de transformation désirée) Les directives OpenMP Les clauses OpenMP STEP ne fait pas la détection de toutes les instructions possible d’OpenMP, seule certaines sont utiles à STEP pour son travail de transformation. Ainsi, après étude du parser, nous avons pu déterminer quels étaient les mots que détectait STEP. Nous les avons regroupés dans le tableau suivant : Transformation STEP step transform_mpi transform_nompi transform_hybrid transform_ignore Directive openMP Clauses OpenMP omp nowait parallel private do/for shared parallel for reduction end barrier master single Figure 2 – Tableau des mots détectés par le parser de STEP Les deux dernières catégories de mots clés permettent comme dit au dessus d’aider à la representation des clauses openMP. Ces catégories consistent en : La gestion d’une liste de variable pour les clauses private , shared et reduction. La gestion des opérateurs pour la clause reduction. Ainsi, lorsqu’une clause de type reduction, private ou shared est détecté par l’analyseur, celuici construit une liste avec l’opérateur (si on se trouve dans une réduction), et les variables passées en paramètre de la clause. Par l’utilisation des machines à état, STEP fait aussi la detection de la structure des instructions qui lui sont passé en paramètre. Après l’étude de ces machines à état, on peut se représenter la structure possible des instructions de la manière suivante : Pour la partie concernant les directives STEP transform_mpi transform_nompi step transform_hybrid transform_ignore 6 Pour la partie concernant les directives et les clauses OpenMP private parallel shared reduction private do/for reduction nowait private omp parallel for reduction single master barrier private parallel do/for nowait parallel for end single nowait master barrier Légende : Mots ouvrant une machine à état Mots n’ouvrant pas de machine à état Figure 3 - Representation de la structure des machines à Etats 7 Comprendre cette représentation de la structure des instructions analysées par le parser est important dans notre projet pour l’écriture de notre parser OpenMP en flex/bison. c) CAPS HMPP HMPP (pour Hybrid Multicore Parallel Programming) est un SDK créé par la société CAPS. Son but est de permettre le développement d’applications GPU. HMPP permet de définir des régions de code qui seront exécutées sur un GPU. Ces régions sont appelées codelet et sont écrites en introduisant dans le code des directives HMPP. On ajoute avant le prototype de la fonction une directive HMPP permettant de définir les arguments en entré ou sortie et différentes options (CUDA, OpenCL…). Cette fonction sera ensuite appelée dans le programme en rajoutant la directive HMPP callsite avant de faire l’appel de la fonction en elle-même. Figure 4 - Exemple d’utilisation HMPP avec definition du codelet et appel dans le main Les directives HMPP permettent aussi de guider en détail la façon dont le GPU sera utilisé. Il est ainsi possible de définir le moment ou les données seront transférées vers le GPU et la nature du codelet (asynchrone, synchrone…). HMPP est capable de réaliser la fonction sur GPU de manière automatique sur un codelet mais dans certains cas il peut être préférable de donner des indications. Il existe ainsi des directives HMPPCG qui permettent de définir les détails de la mise en place de la fonction sur le GPU. On peut préciser les variables partagées ou privées, placer des barrier à la manière d’OpenMP, indiquer le nombre de cellules utilisées ou réaliser des opérations sur les boucles. Figure 5 – Ici la première boucle for est parallélisé alors que la seconde ne l’est pas. Sans les directives HMPPCG les deux boucles auraient étés parallélisées. 8 d) HMPP et STEP La finalité de ce projet est d’intégrer un parser HMPP à l’outil STEP. Celui-ci sera écrit en Flex/Bison. Intégrer un analyseur HMPP à STEP pourrait permettre de détecter les directives HMPP dans un code et de rajouter des directives MPI pour permettre d’utiliser les GPU de plusieurs hôtes en parallèle pour exécuter un programme. Programme principal Appel du codelet MPI sur 4 hôtes GPU 1 GPU 2 GPU 3 GPU 4 Fin du codelet Programme principal Figure 6 – Exemple d’exécution d’un programme HMPP+MPI Sur la figure ci-dessus le programme est exécuté normalement jusqu’à l’appel du codelet. Une fois à l’intérieur, une fonction MPI est appelée ce qui permet de repartir la boucle du codelet sur plusieurs hôtes. Nous obtenons ainsi l’exécution de l’application reparties sur les GPU de 4 hôtes. Un tel outil permettrait de générer des programmes multi-hôtes multi-GPU automatiquement. 9 2 – Travaux réalisés a) Ecriture du parser OpenMP Le premier travail qui nous a été confié fut de produire un parser OpenMP qui aura la possibilité de remplacer celui déjà présent dans STEP. Au départ, nous étions un peu parti à l’aveugle, c'est-à-dire que nous avions écrit une première version du parser qui pouvait détecter un maximum de mot clé d’OpenMP. Cependant, après avoir étudié de plus près le parser OpenMP de STEP, nous avons réalisé des modifications et notre parser maintenant est plus proche du parser de STEP. Ainsi, nous avons donc codé une seconde version du parser OpenMP, plus proche que celui de STEP avec les spécifications suivantes : Utilisation de flex ET de bison. Pouvoir reconnaitre les mêmes instructions que STEP. Utilisation de moins de machine à état que possible et laisser toute la détection de la structure d’une instruction à Bison. Si une instruction est de type non reconnus au départ par STEP, alors cette instruction est recopiée. Spécifions plus précisément comment ont été construit notre code flex et bison. 1) Partie FLEX La partie flex du parser a pour rôle de faire une analyse syntaxique d’une chaine de caractère placé en entrée. Afin de le construire, nous nous sommes beaucoup inspirés des mots clés détectés par le parser de STEP et nous les avons juste repris dans la partie flex (voir le tableau dans la partie 1-b). Cependant, contrairement au parser Flex, nous avons définis ce qu’était qu’une variable, une chaine de caractère composé de lettre majuscule ou minuscule et de chiffres. Cette définition de variable nous sera utile dans le bison lorsque nous souhaiterons définir les variables en arguments des clauses share, private et réduction. D’autre part, nous utilisons que deux machines à état et laissons la detection de la structure de l’instruction à Bison. Ces machines à état sont commencées à la détection du mot clé « omp » ou « step », cela a pour but de reconnaitre juste les directives openMP ou STEP et d’empêcher des mauvaises détections pour certains mots clés utilisés dans d’autres contextes (exemple : FOR). Ces machines a état nous assurent donc que les mots qui suivent vont être liés à une directive openMP ou STEP, en effet, les directives openMP commencent toutes par « !$ omp » dans le cadre du fortran et « #pragma omp » dans le cadre du C et il en ai de même pour step ( #pragma step et !$step ). 10 Ces machines à état se ferment lorsque l’on détecte un « \n », en effet, STEP ferme la plupart de ses machines à états à la détection de ce « \n ». Nous avons juste repris cela dans notre flex et ainsi, nous ne faisons pas attention aux instructions openMP présent sur deux lignes. 2) Partie BISON La partie bison du parser OpenMP que nous avons codé a pour rôle principal de faire l’analyse de la structure de l’instruction qu’on lui rentre en paramètre. Comme pour la partie flex, nous nous sommes grandement inspirés de STEP pour la construction du fichier bison. En effet, nous avons utilisés les structures possibles des instructions analysées par STEP (voir Figure3) et la syntaxe openMP pour construire les grammaires de notre bison. Nous les avons décidés de séparer la structure d’un programme en 4 grands groupes distincts (voir figure X). Trois de ses groupes sont tout d’abord les même que ceux définis lors de l’analyse du parser openMP de STEP dans la partie 1, c'est-à-dire, les instructions STEP (STEPSTATEMENT dans le fichier bison), les directives openMP (DIRECTIVES dans le fichier bison) et enfin les clauses openMP (CLAUSES dans le fichier bison). PROGRAM : | PROGRAM WORDCODE | PROGRAM CLAUSES | PROGRAM DIRECTIVES | PROGRAM STEPSTATEMENT | PROGRAM '\n' ; Figure 7 – Structure d’un programme pour Bison A cela s’ajoute une grammaire appelée WORDCODE qui permet dans le cas l’analyseur lexical détecte un mot clé qui ne fait en rien partie d’une instruction de juste le recopier et de ne pas le traiter. Cela concerne surtout les mots clés ouvrant les machines à états, « omp » et « step », et des symboles telles que « + » « , » … Cette grammaire WORDCODE contient le moins de structure syntaxique que possible afin d’éviter le maximum d’erreur de type décalage réduction que nous avions eu avant l’implémentation des machines à état. Nous avons ensuite défini une grammaire correspondant aux variables contenus dans les clauses openMP shared, private et reduction, cela afin de pouvoir faire l’utilisation des listes comme défini dans STEP. Celle-ci est définie de la manière suivante : GRPVARIABLE : GRPVARIABLE Symbole Variable | Variable ; Figure 8 - Définition des variables dans Bison 11 Cette définition permet l’utilisation des fonctions de STEP qui permettent la gestion des listes. En effet, dans STEP, les variables passés en arguments d’une clause openMP tel que private ou shared sont inclus dans une liste de variable utilisés lors de l’ajout de la clause dans la Représentation Intermédiaire. Afin de pouvoir tester notre parser OpenMP, nous avons placé une macro DEBUG qui si elle est passé à 1 permet d’observer la détection des différentes directives, clauses que le parseur rencontre en les remplaçant par « <nom directive/clause> ». 3) Exemple d’exécution du parser OpenMP Voici un exemple d’exécution de notre parser openMP avec le mode DEBUG. Comme dit auparavant, lorsqu’il rencontre une instruction connu le parser affiche un <Nom> à la place (voir les mots en bleu). S’il rencontre une instruction inconnue, il la recopie sans y faire attention (voir les bouts en rouge). Code de base Code après passage dans le parser #include <stdlib.h> #include <stdio.h> #include <omp.h> #include <stdlib.h> #include <stdio.h> #include <.omp h> int main(void) { int i; int sum=0; #pragma omp parallel private (sum) reduction(+:sum) { #pragma omp for for (i=0;i<5;i++) { sum += i; } #pragma omp task { sum +=i; } int main(void) <Debut Block> int i; int sum=0; #pragma <Parallel> <private> <reduction> <Debut Block> #pragma <For> for (i=0;i<5;i++) <Debut Block> sum += i; <Fin Block> #pragma omp task <Debut Block> sum +=i; <Fin Block> } printf("%d", sum); return 0; } <Fin Block> printf("%d", sum); return 0; <Fin Block> Figure 9 – Exemple d’execution du parser OpenMP A cette étape du projet, le parser openMP ne fait que d’analyser les différentes instructions openMP qu’on lui met en entrée. L’utilisation de cette analyse ne sera faite qu’un peu plus tard, lorsque l’on essayera de faire l’intégration de ce parser openMP dans STEP. 12 b) Etude et prise en main de HMPP Deux postes avec licence HMPP ont été mis à disposition pour pouvoir expérimenter le SDK. Une documentation nous a aussi été fournie. Une série d’exemples labs était présent sur les postes et ont permis de prendre en main HMPP en expérimentant directement sur des parties de code. Avant de réaliser le parser HMPP il était nécessaire d’identifier les fonctions qui seraient utiles dans le cadre du projet multi-hôtes. Il a été définit que ces fonctions seraient des directives HMPPCG. En effet l’appel de codelet se fait sur chaque hôte et ne nécessite pas de traitement particulier. Avec les directives HMPPCG ont obtient un langage qui se rapproche d’OpenMP. Nous avons cherché une correspondance entre les directives OpenMP et HMPP pour trouver un point d’approche semblable lors de l’intégration du parser à STEP. En effet si nous avons des fonctions effectuant la même action sur du matériel différent (CPU pour OpenMP et GPU pour HMPP) il est possible de la repartir sur plusieurs hôtes de manière semblable avec MPI. Nous sommes parvenus au tableau de correspondance suivant : Directive OpenMP Directive HMPP omp parallel for hmppcg parallel hmppcg gridify *** shared *** global *** private *** private *** reduction *** reduce omp barrier hmppcg grid barrier omp master hmppcg noparallel Figure 10 – Tableau de correspondance OpenMP-HMPP 13 c) Parser HMPP 1) Ecriture du parser Le parser HMPP a été écrit en s’appuyant sur ce qui a été réalisé dans le parser OpenMP. Son écriture a donc été relativement rapide, avec des noms de directives à remplacer. Il permet de détecter les directives pertinentes dans le cadre d’une implémentation de fonctions MPI ultérieurement. Les directives suivantes ont étés choisies pour être intégrées dans le parser private parallel global reduce private gridify hmppcg global reduce noparallel barrier grid unguarded Figure 11 – Directives HMPP utilisées dans le parser Parallel et Gridify fonctionnent de manière similaire a Parallel for pour OpenMP. Ces directives permettent de définir la manière dont la boucle est parallélisée en précisant les réductions ainsi que les variables partagées et privées. NoParallel permet de préciser que la boucle sera exécutée sur le CPU et non le GPU. Grid Barrier est similaire à la fonction Barrier d’OpenMP et permet de synchroniser tous les threads actifs. Unguarded permet d’effectuer la fonction inverse. 14 On peut imaginer le scénario suivant : Détection de parallel ou gridify : Répartition sur plusieurs hôtes de la boucle à venir. Détection de noparallel : Arrêt du MPI, la boucle est réalisée sur le CPU d’un seul hôte. Détection de barrier et unguarded : Similaire a OpenMP pour barrier. On obtient ainsi un parser détectant un ensemble de fonctions a première vue limitée mais pertinentes dans la finalité du projet. 2) Exemple d’exécution du parser HMPP Codelet de base void diffNormReduce(const int m, const int n, const int spani, const int spanj, double table[m][n], double tableOut[m][n], double diffNorm[1]) { int i,j; double diffsum = 0.0; /* Fill the data as specified */ #pragma hmppcg gridify, reduce (+:diffsum) for (i=spani; i < m - spani; i++) for (j=spanj; j < n - spanj; j++) { double diff = tableOut[i][j] - table[i][j]; double diffmul = diff*diff; diffsum += diffmul; table[i][j] = tableOut[i][j]; } Codelet après passage dans le parser void diffNormReduce(const int m, const int n, const int spani, const int spanj, double table[m][n], double tableOut[m][n], double diffNorm[1]) <Debut Block> int i,j; double diffsum = 0.0; /* Fill the data as specified */ #pragma <Gridify> <reduce> for (i=spani; i < m - spani; i++) for (j=spanj; j < n - spanj; j++) <Debut Block> double diff = tableOut[i][j] - table[i][j]; double diffmul = diff*diff; diffsum += diffmul; table[i][j] = tableOut[i][j]; <Fin Block> diffNorm[0] = diffsum; } diffNorm[0] = diffsum; <Fin Block> Figure 12 – Exemples d’exécution du parser HMPP 15 d) Préparation à l’intégration des deux parsers Le but principal du projet était bien sur d’intégrer les deux parsers dans STEP. Cependant, cette intégration n’est pas une tâche facile. En effet, la structure de STEP en fait que modifier le modifier afin d’intégrer juste quelques fichiers ne se résume pas à juste ajouter les fichiers et à modifier un makefile. Les dépendances entre les fichiers sont si importantes qu’il est difficile de savoir où exactement sont appelés les fonctions du parser. Ainsi, avec le temps qu’il restait au projet, nous avons décidé de faire une préparation à l’intégration en ajoutant les prototypes des fonctions dont fait appel le parser OpenMP de STEP. NOTRE PARSER STEP Au niveau tout d’abord du parser OpenMP nous avons rajouté des appels de fonctions au niveau du fichier Bison tels qu’il a été réalisé au niveau de STEP. En voici un exemple : {parallel_loop} { yy_push_state(parallel_loop); pips_debug(2,"begin state %s %d\n", yytext, YYSTATE); directive_block_begin(STEP_PARALLEL_DO, yytext); } FOR : Omp Parallel For { if (DEBUG) printf("<Parallel for>"); directive_block_begin(STEP_PARALLEL_DO, "parallel for");} Figure 13 – Exemple comparaison STEP et notre parser D’autre part, afin de pouvoir tester l’appel de ces fonctions, nous avons réalisé des fausses fonctions qui font juste un affichage indiquant que la fonction a été appelée. Seul une des fonctions s’est inspirée de l’originale car elle est l’une des fonctions les plus appelé et elle n’est pas trop dépendantes des structures et des fonctions hors du parser. Cette fonction est directive_block_begin () qui est appelé dès qu’une instruction est repérée. 16 Voici un exemple d’exécution de ce nouveau code avec les nouvelles fausses fonctions. Nous avons pris le même exemple que pour l’exemple de notre parser OpenMP. Code de base #include <stdlib.h> #include <stdio.h> #include <omp.h> int main(void) { int i; int sum=0; #pragma omp parallel private (sum) reduction(+:sum) { #pragma omp for for (i=0;i<5;i++) { sum += i; } #pragma omp task { sum +=i; } } printf("%d", sum); return 0; } Retour après passage dans le parser openMP #include <stdlib.h> #include <stdio.h> #include <.omp h> int main(void) { int i; int sum=0; #pragma <Parallel> --appel de la fonction directive_block_begin avec le type = 0 et un s=parallel---appel de la fonction add_step_directive---appel de la fonction reset_step_transform-<Début de la liste> <Ajout de l'élément "sum" dans la liste> --appel de la fonction init_current_list-<private> --appel de la fonction add_clause_private---appel de la fonction reset_current_list-<Début de la liste> <Ajout de l'élément "sum" dans la liste> --appel de la fonction init_current_list-<reduction> --appel de la fonction set_current_op---appel de la fonction add_clause_reduction---appel de la fonction reset_current_list---appel de la fonction reset_current_op-{ #pragma <For> --appel de la fonction directive_block_begin avec le type = 1 et un s=for---appel de la fonction add_step_directive---appel de la fonction reset_step_transform-for (i=0;i<5;i++) { sum += i; } #pragma omp task { sum +=i; } } printf("%d", sum); return 0; } Figure 14 – Exemple d’exécution du parser OpenMP après ajout des fausses fonctions. 17 L’intégration du parser HMPP s’est fait sur le modèle du parser OpenMP. Nous avons rajouté de fausses fonctions qui permettent d’afficher des informations propres à l’action qui serait effectuée. Les fonctions de base n’étant pas écrites pour HMPP, les fonctions OpenMP ont étés utilisées dans le parser HMPP pour tester l’intégration. Il est nécessaire d’écrire dans STEP des fonctions propres à HMPP pour l’intégration finale. L’exemple suivant permet de voir le résultat du parser sur un codelet : Code de base Retour après passage dans le parser openMP void simpleJacobi(const int m, const int n, const int spani, const int spanj, double table[m][n], double tableOut[m][n]) void simpleJacobi(const int m, const int n, const int spani, const int spanj, double table[m][n], double tableOut[m][n]) <Debut Block> int i,j,k; #pragma <Parallel> --appel de la fonction directive_block_begin avec le type = 0 et un s=parallel---appel de la fonction add_step_directive---appel de la fonction reset_step_transform-, <D�but de la liste> <Ajout de l'�l�ment "i" dans la liste> --appel de la fonction init_current_list-<private> --appel de la fonction add_clause_private---appel de la fonction reset_current_list-- { int i,j,k; #pragma hmppcg parallel, private(i) for (i=spani; i < m - spani; i++) #pragma hmppcg noparallel for (j=spanj; j < n - spanj; j++) { double neighbor =cos(table[ispani][j]) +sin(table[i][j-spanj]) +sin(table[i][j+spanj]) +cos(table[i+spani][j]) ; tableOut[i][j] = neighbor/3; } #pragma hmppcg grid barrier } for (i=spani; i < m - spani; i++) #pragma <Noparallel> for (j=spanj; j < n - spanj; j++) <Debut Block> double neighbor =cos(table[i-spani][j]) +sin(table[i][j-spanj]) +sin(table[i][j+spanj]) +cos(table[i+spani][j]) ; tableOut[i][j] = neighbor/3; <Fin Block> #pragma <Omp Grid Barrier> --appel de la fonction directive_statement avec le type = 5 et un s=barrier-<Fin Block> Figure 15– Exemple d’exécution du parser HMPP après ajout des fausses fonctions. 18 3 – Bilan du projet a) Rendu final du projet Au terme de ce projet, voici les différents rendus du projet : Un parser OpenMP prêt à être intégré dans STEP. Une documentation sur le parser openMP de STEP. Un parser HMPP prêt à être intégré dans STEP. Un poster décrivant le projet. Une fiche résumée du projet. Nous aurions aimé réaliser l’intégration des deux parsers dans STEP par nous même, cependant nous n’en n’avions pas eu le temps. En effet, cela nous demanderai d’étudier plus en détail la manière dont est construit STEP en terme de module. b) Difficultés rencontrées Plusieurs difficultés se sont soulevées durant le projet. La première concerne la compréhension et la prise en main de l’outil STEP. En effet, la taille de l’outil le rend difficile à modifier à sa guise, ainsi, lors des tests de STEP afin de comprendre le fonctionnement du parser, il nous a pris du temps à comprendre à partir de quel répertoire il nous fallait recompiler le programme. D’autre part, l’intégration de notre parser OpenMP n’a pas pu être possible de notre coté pour ce même problème, nous n’avons pas trouvé le fichier où était mis les dépendances de fichier. Ainsi, si nous supprimions le fichier step_omp.l pour mettre nos deux fichiers flex et bison, modifions le makefile dans le même répertoire, nous avions un problème de dépendance que nous n’avons pas réussi à résoudre. C’est pourquoi nous avons plutôt décidé de préparer l’intégration plutôt que d’intégrer nous même, cependant nous n’avons donc pas pu faire de réel test de nos parser. La seconde concerne la prise en main du langage HMPP qui n’est pas évidente de premier abord. En effet il a été nécessaire de différencier les directives pertinentes dans le cadre de notre projet des directives générales. c) Temps de travail passé sur le projet La majorité du projet a été l’étude de la documentation et de code venant de STEP et HMPP et la prise en main des deux en réalisant un grand nombre de test. La partie codage de parser n’a pas été la partie la plus difficile et la partie qui nous a pris le plus de temps. 19 Voici un tableau qui résume le temps de travail que nous avons passé sur le projet : Révison Flex/Bison Etude de STEP Construction de la doc STEP Codage parser OpenMP Etude + prise en main de HMPP Codage parser HMPP Préparation à l’intégration Reunion Rapport Slides Poster + Fiche Total Rémy Thean 3 20 10 25 5 0 7 6 18 3 2 Victor Maréchal 3 15 2 5 30 10 4 6 10 5 4 193 20 Conclusion Ce projet fut très intéressant. Il nous a permis de revoir des notions vues en ASR et d’affirmer nos connaissances sur celles-ci en particulier les technologies permettant d’établir un analyseur syntaxique (Flex, Bison) et les langages de programmation haute performance (MPI, OpenMP). Le projet nous a aussi permis de découvrir de nouveaux outils. HMPP est assez intéressant dans sa façon de fonctionner, sans changer fondamentalement le code original. STEP est un outil prometteur, permettant d’automatiser l’optimisation de code. Nous avons eus quelques difficultés sur ce projet, en particulier sur la compréhension de la structure de STEP. L’aide d’Alain Muller et de Rachid Abel nous a été très utile pour comprendre le fonctionnement de l’analyseur de STEP. Nous sommes parvenus à écrire un parser qui reconnait des directives HMPP et qui peut être aisément modifié pour inclure d’autres directives. Nous espérons que nos deux parsers soient intégrés dans STEP. 21