• Aucun résultat trouvé

Cette section s’intéresse à l’usage de la programmation par composition dans la conception de codes parallèles pour le calcul de haute performance. En effet, dans la section2.3, nous avons vu qu’il existe un ensemble varié de modèles de programma-tion permettant d’exprimer le parallélisme des applicaprogramma-tions HPC. Nous avons ensuite vu dans la section 2.4, qu’indépendamment du paradigme de programmation uti-lisé pour exprimer les traitements réauti-lisés par une application, il est important de mettre en place des méthodes de conception afin de pouvoir mettre au point une application maintenable, en particulier lorsque plusieurs personnes sont impliquées dans le processus de conception. Dans cette dernière section, nous nous sommes plus particulièrement concentré sur les modèles de composants et leur capacité à combiner des codes indépendamment de l’expression du parallélisme. Tout naturel-lement, nous pouvons nous demander si la présence du parallélisme a un impact sur la composition. Plus précisément, est-il possible de composer des codes parallèles comme on compose des codes séquentiels et, dans le cas contraire, comment réaliser une telle opération ?

Cette section reprend les formes de composition étudiées dans la section 2.4.4, analyse leurs capacités à supporter des codes parallèles et présente plus généralement les approches existantes permettant de composer des codes HPC. Elle décrit ensuite le modèle de composants L2

C utilisé dans les chapitres3et4. Puis la section conclut en laissant entrevoir les problématiques abordées dans ce manuscrit.

2.5.1 Forme de composition de codes parallèles

L’approche la plus simple pour composer des codes parallèles dans les modèles de composants consiste à considérer que la présence du parallélisme fait partie inté-grante de la mise en œuvre des composants et ne doit donc pas être nécessairement visible au niveau des interfaces des composants. Cette méthode a l’avantage de per-mettre de remplacer facilement une mise en œuvre parallèle par une autre (disposant d’une nouvelle stratégie de parallélisation ou utilisant un paradigme de programma-tion différent) de manière transparente. Cependant, en cachant le parallélisme lors de la phase d’assemblage, la composition de codes parallèles indépendants est un véritable défi.

Sans information sur l’usage parallèle des interfaces (p. ex. use-provide), il est impossible de déterminer lors de l’assemblage si les interfaces de deux composants

2.5. PROGRAMMATION PARALLÈLE PAR COMPOSITION 43 sont compatibles (c.-à-d. les mises en œuvre des deux composants peuvent interagir par le biais de ces interfaces sans causer de problème). Pour simplifier la composition, des contraintes supplémentaires sur les interfaces peuvent être ajoutées (p. ex. para-digme de programmation parallèle utilisé et contraintes d’utilisation parallèle), soit par les concepteurs des composants, soit directement dans le modèle de programma-tion. Le premier risque est de rendre les interfaces dépendantes d’une mise en œuvre spécifique d’un composant, de réduire la compatibilité des interfaces (p. ex. para-digme de programmation ou approche de parallélisation non compatible) ou encore d’impacter les performances en introduisant des coûts non nécessaires (p. ex. redis-tribution, synchronisations, etc.). Le second a le défaut de réduire potentiellement la classe des paradigmes de programmation supportés par le modèle de composants et laisse au modèle le choix de définir les interfaces.

Une dernière approche consiste à fournir une description du parallélisme au ni-veau de l’assemblage des composants. Le parallélisme est soit exprimé à l’aide de la structure de contrôle explicite, soit implicitement déduit de la structure de l’assem-blage (p. ex. modèle de flux de données). Cette approche peut être utilisée conjoin-tement avec la précédente.

Cette section analyse les différentes formes d’interfaces existantes dans les mo-dèles de composants qui permettent une composition de codes parallèles.

Use-provide parallèle

Un des moyens d’effectuer une composition use-provide dans un contexte pa-rallèle consiste à appliquer une composition use-provide d’un contexte séquentiel sur chaque élément responsable du parallélisme (p. ex. fils d’exécution, tâches, etc.) indépendamment des autres éléments. Cependant, bien que cette variante parallèle soit aussi expressive que son homologue séquentiel, elle limite fortement la compo-sition de codes parallèles. En effet, il est fréquent en HPC de composer des codes n’opérant pas avec le même niveau de parallélisme (c.-à-d. problème M ˆ N, p. ex. nombre de fils d’exécution différent, composition de deux groupes de tâches de taille différente, etc.), ou encore que les éléments responsables du parallélisme des codes composés ne soient pas indépendants (p. ex. besoin de redistribution des données avant l’utilisation d’un service, synchronisation partielle ou totale nécessaire, etc.).

Cette approche est utilisée notamment par CCA [4], un modèle de composants dédié au calcul de haute performance. Dans CCA, les instances de composants et leurs ports sont dynamiquement créés à l’exécution dans un contexte pouvant être séquentiel ou parallèle. Dans le cas d’un code parallèle, une exécution typique d’une application CCA consiste à répliquer un assemblage dans plusieurs processus. L’en-semble des instances d’un même composant répliqué sur tous les processus est ap-pelé cohorte. Tous les processus d’une cohorte exécutent le même code issu de leur composant et peuvent communiquer entre eux à l’aide de bibliothèques de passage de messages quelconques, telles que MPI. CCA fournit uniquement un moyen de connecter des instances de composants à travers des connexions use-provide réalisées localement au sein d’un processus. Lorsque deux instances de composants opèrent sur des données distribuées de manière différente entre les processus, le concepteur

des composants doit lui-même effectuer une redistribution, car CCA n’offre aucun mécanisme pour effectuer une telle opération. Cela est typiquement réalisé en di-visant l’ensemble des processus en deux parties qui contiennent des assemblages différents, mais qui partagent un même ensemble d’instances de composants visant à redistribuer des données. Certaines mises en œuvre de CCA étendent le modèle en définissant comment effectuer une composition de composants dont les cohortes ne comportent pas le même nombre de processus. C’est le cas de SCIRun2 précisant que les cohortes issues de deux composants effectuent des échanges de type round-robin. Cependant, la redistribution des données dans SCIRun2 reste à la charge des concepteurs des applications.

GridCCM [96] étend le modèle de composants CCM dans le but de spécifier comment effectuer une composition de codes parallèles basée aussi sur le concept de use-provide. Dans GridCCM, les composants peuvent disposer d’une implémenta-tion pouvant être séquentielle ou parallèle. Le modèle permet d’assembler ces com-posants en étendant les interfaces use-provide avec une description parallèle (utilisée uniquement par les composants parallèles) qui spécifie pour chaque méthode d’une interface, le type de distribution de chacun des paramètres (p. ex. distribution en blocs réguliers pour un type de données tel qu’une matrice). Le changement de dis-tribution de données est effectué par un code utilisateur injecté lors d’une phase de compilation de l’assemblage en un assemblage CCM. Cette approche a l’avantage de permettre une composition transparente des codes parallèles, notamment ceux qui ont recours à un nombre de fils d’exécution différent. De plus, elle évite d’engendrer des sursynchronisations à l’exécution.

GCM [13], une extension de Fractal, propose des interfaces dérivées du use-provide séquentiel : une interface de diffusion d’appels de méthodes et une interface de rassemblement d’appels de méthodes. Dans cette approche, une mise en œuvre parallèle d’un composite peut disposer d’un ensemble d’instances de composants séquentiels. Un composite peut disposer d’interfaces de diffusion qui ont pour effet de transformer un unique appel de méthode en un ensemble d’appels de méthodes à appliquer sur les ports des instances du composite après avoir traversé la membrane du composite. Tout comme GridCCM, GCM redistribue automatiquement les don-nées de chaque paramètre de la méthode appelée indépendamment. Un composite peut aussi disposer d’interfaces de rassemblement qui ont pour effet de transfor-mer un ensemble d’appels de méthodes effectués en parallèle par les instances du composite en un unique appel de méthodes après avoir traversé la membrane du composite. La composition de deux composites dont la mise en œuvre est paral-lèle (c.-à-d. contenant des instances séquentielles) peut être réalisée en fusionnant les opérations de rassemblement et de diffusion en une unique étape qui consiste à connecter directement les instances des deux composites en fonction des distribu-tions des paramètres des interfaces de rassemblement et de diffusion. Tout comme pour GridCCM, cette dernière opération permet d’éviter des sursynchronisations lorsque le niveau de parallélisme n’est pas le même entre deux composants qui sont composés ensemble.

2.5. PROGRAMMATION PARALLÈLE PAR COMPOSITION 45 Flux de données et flux de travaux

FlowVR [5] est un exemple de modèle de composants (dédié à la visualisation scientifique permettant d’effectuer des traitements in situ) à base d’interfaces de flux de données qui offrent des mécanismes pour composer des codes parallèles. Dans cette approche, les composants peuvent communiquer par le biais de ports d’entrée ou de sortie en utilisant trois opérations (de type push) : put (envoi d’un message sur un port de sortie), get (récupération du message le plus ancien sur un port d’entrée) et wait (attente de données sur un port d’entrée). Le modèle permet de connecter des ports de données entre eux et de filtrer les messages qui transitent sur la connexion résultante. Chaque connexion est associée à une file qui stocke les messages envoyés par les instances de composants. Les composants sont manuelle-ment placés sur des unités de calcul. Le transfert des messages entre des ressources est réalisé de manière transparente. Deux mises en œuvre parallèles de composants utilisant différents niveaux de parallélisme peuvent donc être simplement composées ensemble. Il en résulte deux codes parallèles qui communiquent par échange de flux de messages. Il est important de noter que le type des messages transmis, la taille des messages ainsi que leur ordre d’envoi doivent être judicieusement choisis pour pou-voir composer efficacement les codes parallèles. La redistribution de structures de données complexes entre deux codes parallèles est laissée à la charge des concepteurs d’une application.

Decaf [44] est un modèle très similaire à FlowVR qui se distingue par une concep-tion ciblant particulièrement les supercalculateurs. Il inclut notamment Bredala [43], un modèle de description et de transmission des données, permettant d’effectuer des redistributions de données entre des traitements indépendants facilitant ainsi la composition des codes HPC.

Partage de données

Les modèles de composants supportant le partage de données permettent d’ex-primer des interfaces qui fournissent des données partagées et des interfaces qui accèdent à ces données. La connexion de ces deux types d’interfaces permet d’expri-mer la dépendance d’un composant à une donnée fournie par un autre. Le partage de données met en évidence les accès aux données entre les composants, ce qui se révèle particulièrement intéressant lorsque les composants sont associés à un mo-dèle de ressources (c.-à-d. placement de composants sur des ressources matérielles ou logicielles). Cette forme de composition se distingue de celle des approches de flux de données où plusieurs données transitent d’une source vers une destination. Au lieu de cela, une seule donnée est accessible par plusieurs composants, à un instant donné, ce qui a pour effet de soulever des problèmes d’accès concurrents à une donnée partagée par plusieurs composants. Ainsi, les interfaces des modèles de composants possèdent des mécanismes permettant de contrôler l’accès aux données entre les composants (p. ex. exclusion mutuelle).

Cette méthode de composition a notamment été proposée comme extension de CCM et CCA [8,28].

Squelettes algorithmiques

Comme nous avons pu voir dans les sections 2.3 et 2.4, les squelettes algorith-miques peuvent être utilisés comme paradigme de programmation parallèle permet-tant d’exprimer des traitements parallèles, mais aussi comme méthode de compo-sition de codes en utilisant une stratégie de conception à base de patrons, chacun faisant souvent l’objet d’approches disjointes. Dans le cas de la composition de codes parallèles, les mécanismes offerts dépendent fortement des opérateurs mis à dispo-sition dans les approches existantes.

FastFlow [2] est un exemple de modèle fournissant des squelettes algorithmiques permettant d’écrire et de composer des codes parallèles pour des architectures multi-cœurs à mémoire partagée avec cohérence de cache. Cette approche est segmentée en trois couches : la première offre des mécanismes de synchronisation sans verrou, la se-conde fournit des collectives de communications de type producteur-consommateur, la dernière offre des patrons de codes parallèles principalement à base de flux de données (p. ex. pipeline, maître travailleur, diviser pour régner). Dans FastFlow, un programme parallèle consiste en une agrégation de plusieurs nœuds de traitement séquentiel opérant ensemble en parallèle et communiquant entre eux par des flux de données. Tout comme dans FlowVR, le type des données transmises ainsi que leur taille ou leur ordre d’envoi doivent être judicieusement choisis et la redistribu-tion de structures de données complexes doit être réalisée par les concepteurs d’une application.

Connecteurs MPI

Dans le modèle de composants L2

C [20], les composants peuvent détenir des interfaces MPI. Ces interfaces permettent de partager un communicateur MPI avec d’autres instances de composants (nécessairement placées sur des processus MPI différents). Un connecteur MPI permet le partage d’un unique communicateur MPI par un groupe d’interfaces MPI. Les instances (placées sur des ressources distribuées) peuvent ensuite utiliser le communicateur pour interagir entre elles par le biais de collectives MPI. Une telle interface permet de rendre explicite les moyens de communication utilisant MPI entre les instances au niveau de l’assemblage.

2.5.2 L

2

C : un modèle de composants minimaliste pour le

HPC

Cette section présente L2

C [20], un modèle de composants utilisé comme base pour définir Comet présenté dans le chapitre 3ainsi que pour faire fonctionner sa mise en œuvre Halley décrit dans le chapitre 4.

L2

C peut être vu à la fois comme une extension du processus de compilation modulaire et un modèle de composants de bas niveau proche du système. En effet, chaque composant est tout d’abord compilé en une bibliothèque (statique ou dy-namique). Puis à l’exécution, les composants sont instanciés et les instances sont connectées en suivant un fichier de description d’assemblage.

2.6. DISCUSSION 47