• Aucun résultat trouvé

Exemples de langages de programmation parallèle haut niveau

parallèle haut niveau

Nous présentons dans ce chapitre plus en détail deux des langages de programmation parallèle haut niveau précédemment cités, Cilk et Jade.

Pour chacun de ces langages nous présentons tout d’abord le modèle de programma-tion utilisé permettant d’abstraire les caractéristiques de la machine sous-jacente. Nous présentons ensuite l’implantation de ce modèle de programmation sur les architectures SMP et distribuées en insistant plus particulièrement sur l’algorithme d’ordonnancement utilisé. Nous présenterons finalement le modèle de coût associé lorsque qu’il existe et les évaluations expérimentales réalisées.

2.4.1 Cilk

Cilk[11, 104] est un langage destiné à la programmation des architectures SMP (une version distribuée existe, basée sur une mémoire virtuellement partagée) dont le déve-loppement à débuté en 1993 au MIT ; il est actuellement disponible dans sa cinquième version.

2.4.1.1 Modèle de programmation

Le langage Cilk est une extension du langage C qui offre des primitives pour l’expres-sion du parallélisme de contrôle par création explicite de tâches.

La description du parallélisme se fait à l’aide du mot clé spawn placé devant un appel de fonction. Conceptuellement, lors de l’exécution du programme, une tâche sera créée pour évaluer cette fonction. La sémantique de cet appel diffère de celle de l’appel classique d’une fonction au sens où la procédure appelante peut continuer son exécu-tion en parallèle de l’évaluaexécu-tion de la foncexécu-tion appelée au lieu d’attendre sa terminaison pour continuer. Cette exécution étant asynchrone, la procédure créatrice ne peut utiliser le résultat de la fonction appelée qu’avec une synchronisation explicite par utilisation de l’instruction sync. Cette instruction a pour effet d’attendre la terminaison de toutes les fonctions appelées en parallèle par la fonction mère (et sa descendance) avant ce sync:

Exemples de langages de programmation parallèle haut niveau 2.4 le parallélisme exprimé est donc de type série-parallèle (en tenant compte du fait que la tâche mère s’exécute en concurrence avec ses filles et leurs descendances, jusqu’à ren-contrer l’instruction sync). Les tâches sœurs créées sont supposées indépendantes : il y a donc risque de concurrence sur les accès à la mémoire partagée, concurrence qui doit être gérée par l’utilisateur.

En plus de cette génération de parallélisme, le langage Cilk permet d’associer lors de la création d’une tâche une fonction «réflexe» ou callback qui sera exécutée en exclusion mutuelle lors de la terminaison de cette tâche. Il est possible à l’intérieur de cette fonction de demander la destruction de l’ensemble des tâches créées par la procédure mère et non encore exécutées, possibilité communément utilisée dans les algorithmes parallèles de recherche spéculative.

2.4.1.2 Implantation

La base de l’implantation du langage est un algorithme d’ordonnancement de type liste, appelé work-stealing, basé sur le « vol de travail ». Lorsqu’un processeur devient inactif, il tire au sort un autre processeur, la victime, à qui il va voler une tâche prête à être exécutée. Plusieurs techniques d’optimisation [44] permettent de placer le maxi-mum du surcoût d’ordonnancement lors des vols et d’avoir ainsi un coût de création de tâche (spawn) réduit au maximum. Parmi ces techniques, une création paresseuse des tâches [91] permet de ne créer effectivement en mémoire les tâches que lorsqu’elles sont volées. Un protocole d’exclusion mutuelle entre les processeurs permet une prise de ver-rou uniquement lors d’un vol de tâche [30, 44].

Une version distribuée de Cilk pour les réseaux de SMP est proposée dans [104]. Elle est basée sur une mémoire virtuellement partagée implantée par un mécanisme de pagination. Dans cette version, le principe du vol de travail a été maintenu, ainsi que le déterminisme de l’exécution. Cependant pour réduire les communications entre différents nœuds SMP, l’ordonnancement prend en considération une notion de «localité». En effet, pour effectuer le vol, le choix du processeur victime n’est pas totalement aléatoire, les processeurs sur le même nœud SMP qui partagent la même mémoire physique que le voleur ont une plus grande probabilité d’être choisis que tout autre processeur distant. 2.4.1.3 Évaluations théoriques et expérimentales

Au modèle de programmation Cilk est associé un modèle de coût. Chaque programme est caractérisé par les trois grandeurs suivantes :

T

1, son travail, qui est le nombre total d’instructions. Ceci correspond à la durée de l’exécution sur un seul processeur.

T

1, la longueur de son chemin critique, qui est le nombre d’instructions dans un plus long chemin du graphe d’exécution. Ceci correspond à la durée d’exécution

sur un nombre infini de processeurs. – S

1, la consommation mémoire de l’exécution sur un seul processeur lors d’une exécution en profondeur du graphe de tâches.

L’implantation de ce modèle de programmation garantit que la durée d’exécutionT p

d’un programme sur une machine àp processeurs et que la consommation mémoireS p

seront telles que :

T p = T 1 p +O (T 1 ) S p  pS 1

Plusieurs applications ont été portées et expérimentées sur la version SMP de Cilk. Les expérimentations réalisées [104] donnent de très bonnes accélérations, comprises entre 5 et 8 sur une architecture SMP à 8 processeurs, pour toutes les applications testées. Nous verrons cependant dans le chapitre 9.1.1 que l’implantation proposée pour la factorisation de Cholesky creuse est très inefficace en terme de puissance de calcul (au moins d’un facteur 50) par rapport à notre implantation séquentielle.

Sur l’implantation distribuée de Cilk, seule une évaluation d’un programme calculant récursivement un terme de la suite de Fibonacci est proposée. L’accélération obtenue sur une machine constituée de trois triprocesseurs SMP est de 5.8. Cependant ce programme utilise très peu la mémoire virtuellement partagée et ne permet donc pas de bien évaluer l’implantation distribuée de Cilk.

2.4.1.4 Bilan

Le langage Cilk offre de manière simple à l’utilisateur le moyen d’exprimer le parallé-lisme de contrôle de son application. L’utilisateur doit cependant contrôler explicitement les synchronisations entre les tâches qui sont nécessaires au maintien de la cohérence des données partagées.

Les performances obtenues sur architectures SMP permettent d’exploiter efficacement le parallélisme de l’applications à un grain fin. Cependant sur les architectures distribuées, le peu de résultats expérimentaux proposés laisse à penser que les performances ne sont pas à la hauteur de celles obtenues sur architecture SMP.

2.4.2 Jade

Jade [107, 108] est un langage pour la programmation parallèle sur machine à mé-moire partagée et distribuée développé à l’Université de Standford ; la dernière version de Jade date de 1994.

Exemples de langages de programmation parallèle haut niveau 2.4 2.4.2.1 Modèle de programmation

Un programme Jade est un programme C annoté par des instructions Jade définissant des objets partagés, des tâches, ainsi que les accès réalisés par les tâches sur les objets par-tagés. Les annotations ne modifient pas (strictement parlant) la sémantique du programme C. En ce sens, Jade est un modèle de programmation implicite.

Le modèle de programmation de Jade est basé sur trois concepts : des objets partagés (i.e. une mémoire partagée distribuée de niveau objet), des tâches et des mécanismes permettant de spécifier les accès réalisés par les tâches sur les données partagées (i.e. les effets de bords des tâches sur la mémoire partagée distribuée). Les objets partagés et les tâches permettent au programmeur de spécifier respectivement la granularité des données et la granularité des calculs. La spécification des accès réalisés par les tâches sur les données partagées permet au système d’exécution, par analyse des dépendances de données entre tâches, d’extraire le parallélisme entre ces tâches.

La création d’un objet partagé est effectuée de deux manières différentes : création statique au démarrage du programme par l’ajout du mot clé shared dans une déclara-tion d’une variable globale C ; créadéclara-tion dynamique en cours d’exécudéclara-tion par appel de la construction create objet avec en paramètre le type de l’objet partagé à créer. La des-truction d’un objet partagé est effectuée par l’appel de la fonction destroy objet avec en paramètre un pointeur sur un objet partagé.

La définition d’une tâche est effectuée par insertion d’un bloc d’instructions dans la construction withonly :

1 : int fonction C( . . . )

2 : . . .

3 : withonly {[list-spécifications-accès]} do ([list-paramètres]) {

4 : [corps-de-la-tâche]

5 : }

6 : . . .

7 : }

La liste des paramètres contient les données qui seront copiées dans le contexte de la tâche lors de sa création. La liste de spécification des accès contient la liste des objets partagés accédés par la tâche lors de son exécution, chaque objetp de cette liste étant annoté par le type d’accès réalisé. Les types d’accès de bases possibles sont les suivants : la lecture rd(p), l’écriture wr(p), la modification rd wr(p), l’accumulation cm(p)

et la destruction de(p). 2.4.2.2 Implantation

Une tâche est créée lors de chaque exécution d’une instruction withonly. L’extraction du parallélisme du programme (i.e. le parallélisme entre les tâches) est réalisée à l’exé-cution par analyse des dépendances de données entre tâches lors de leurs créations. Cette

analyse des dépendances est rendue possible grâce à la spécification des accès aux objets partagés réalisés par les tâches. Cette analyse des dépendances conduit alors à la généra-tion d’un graphe de flot de données contenant les tâches à exécuter et les synchronisagénéra-tions nécessaires entre ces tâches pour respecter la sémantique séquentielle du programme.

L’ordonnancement des tâches ainsi créées est réalisé dynamiquement de manière cen-tralisée. Notons que cet ordonnancement est optimisé pour le cas où la tâche racinemain

est la seule à créer d’autres tâches. L’algorithme utilisé sur machine à mémoire distri-buée est un algorithme de liste prenant en compte des critères de localité. Plus exactement chaque tâche est associée au premier objet partagé qu’elle déclare accéder dans sa liste de spécifications des accès et chaque objet partagé est associé au processeur sur lequel la dernière modification de l’objet a été réalisée. Ainsi à chaque tâche est associé un pro-cesseur qui contient une version valide du premier objet partagé accédé. L’algorithme d’ordonnancement essaye alors d’exécuter chaque tâche sur ce processeur en évitant ainsi la communication de cet objet.

2.4.2.3 Évaluations théoriques et expérimentales

Aucun modèle de coût n’est associé au modèle de programmation Jade. Cependant, la connaissance du flot de données, et donc d’une description des synchronisations entre les tâches, laisse à penser qu’une telle étude théorique doit être possible sur ce langage et similaire à celle donnée dans cette thèse pour Athapascan-1 (chapitres 4 et 5).

Différentes applications ont été codées en Jade [107]. Nous détaillerons dans la sec-tion 9.1.2 page 155 une implantasec-tion de la factorisasec-tion de Cholesky creuse. Trois de ces applications ont un parallélisme à gros grain avec une granularité moyenne des tâches comprise entre 5 et 42 secondes (rapportée à la puissance de crête d’un processeur de la machine utilisée, cela correspond à une granularité des tâches comprise entre 150 Mflop à 1260 Mflop). Deux autres applications, dont la factorisation creuse de Cholesky, ont un parallélisme à grain fin avec une granularité moyenne des tâches comprise entre 20 et 30 millisecondes (de la même manière, cela correspond à une granularité des tâches comprise entre 0.6 Mflop à 0.9 Mflop). Les résultats obtenus sont relativement bons sur machine à mémoire partagée (DASH) avec des accélérations allant de 9 à 27 sur 32 processeurs et sur différentes applications5. Cependant, sur machine à mémoire distribuée (iPSC/860), aucune accélération n’est obtenue sur les applications à grain fin.

2.4.2.4 Bilan

Jade permet une programmation simple des applications parallèles en offrant un mo-dèle de programmation parallèle implicite basé sur un langage de programmation connu. Cependant, compte tenu des choix effectués lors de l’implantation actuelle où la plupart 5. Parmi celles-ci, les applications Wather, Volume Rendering et Ocean de la batterie de teste SPLASH [118] ainsi qu’une factorisation creuse de Cholesky avec partitionnement monodimensionnel

Conclusion 2.5 des algorithmes mis en œuvre sont centralisés, Jade est plutôt destiné aux applications de gros grain.