• Aucun résultat trouvé

4.3 Validation

5.1.2 Exemples synthétiques d’application

Chapitre 5. Introduction du concept de connecteur dans les modèles de composants

hiérarchiques

même modèle, la manière de décrire les mises en œuvres primitives des composants et des

connecteurs diffère. Finalement, la sémantique supportée par chacun des concepts diffère.

Une première solution pour introduire le concept de connecteur dans un modèle de

composants qui correspond à ce qui a été décrit dans les chapitres 3 et 4 consiste à adopter

cette approche. C’est donc ce que nous proposons de faire afin d’évaluer la viabilité d’une

telle approche dans le reste de cette section.

Le modèle évalué comporte des connecteurs qui sont similaires aux composants sauf

qu’ils exposent des rôles au lieu de ports qui sont implicitement des paramètres

géné-riques du connecteur. Les ports des composants ne peuvent pas être connectés entre eux

mais doivent remplir lesrôles de connexions qui sont des instances de connecteurs. Ces

connexions sont instanciées dans l’assemblage de la même manière que les connecteurs.

Chaque rôle d’un connexion est rempli par un ensemble de ports sans contrainte de

nombre ou de types mais les mises en œuvres des connecteurs appelées générateurs

peuvent imposer des contraintes. Il existe deux sortes de générateurs : les générateurs

primitifs supportés par le modèle sous-jacent et les générateurs composites. Un générateur

composite est un assemblage dans lequel les ports qui remplissent ses rôles peuvent être

utilisés pour remplir desrôles de connexion internes.

5.1.2 Exemples synthétiques d’application

Pour évaluer ce modèle, on s’appuie sur deux variations autour d’un exemple

synthé-tique d’application qui sont présentées ici. Elles sont conçues pour être représentatives

d’applications de couplage de code [52] telles qu’on les trouve par exemple dans le domaine

du rendu d’image [7], de l’hydrologie [19] ou de la dynamique moléculaire quantique [14].

Ces applications sont formées par le couplage de codes développés par des équipes

indé-pendantes qui possèdent chacune une expertise d’un domaine différent. Ces codes peuvent

être séquentiels mais ils sont souvent parallèles, de type SPMD. Dans ce dernier cas, il s’agit

d’un ensemble de processus dont le nombre peut être adapté aux ressources d’exécution et

qui communiquent entre eux par passage de message et notamment à l’aide d’opérations

de communication collective. Le choix de la version la plus adaptée des codes ainsi que de

paramètres tels que le nombre de processus dans le cas de la version SPMD doit être fait

en fonction des ressources d’exécution sur lesquelles ils sont déployés.

Pour étudier ce type d’application, on s’intéressera au cas du couplage de deux codes

qui possèdent chacun deux versions :

– une version séquentielle ; et

– une version parallèle pour laquelle le degré de parallélisme est un paramètre.

On s’intéressera à deux types d’interactions différentes pour le couplage entre ces deux

codes.

Dans la première version, les codes interagissent par des appels de méthodes. Ce type

d’interaction est représentatif d’applications [19, 14] où les codent alternent entre phases

de calcul où un pas de temps est simulé et phases de communication où les modifications

de l’état global de l’objet simulé sont échangées. Cet échange d’information est porté par les

paramètres de méthodes appelée entre les différents codes qui forment l’application.

Dans le cas où les versions séquentielles des codes sont utilisés, l’interaction est un appel

de méthode classique. Si ces codes sont déployés sur des ressources d’exécution différentes,

il s’agit toutefois d’un appel de méthode à distance. Quand la version parallèle de l’un des

codes est utilisée, l’interaction devient un appel de méthodes parallèle et dans le cas où les

deux codes sont parallèles il s’agit du cas d’un appel de méthodes parallèleM×N .

Dans une seconde version, les codes interagissent en partageant logiquement un

es-pace de mémoire commun. Ce type d’interaction est représentatif d’applications [7] dans

lesquelles les codes ne suivent pas de motif régulier et peuvent entraîner des modifications

de parties difficiles à prévoir de l’état global. Cet état est généralement stocké dans une

mémoire accessible par l’ensemble des codes qui utilisent des mécanismes de verrou pour

assurer l’intégrité des données qui y sont stockées.

5.1. Analyse préliminaire 59

Ici, le choix des versions séquentielles ou parallèles des codes a peu d’influence sur le

choix de la mise en œuvre de l’interaction. Dans tous les cas, un certain nombre de

proces-sus accèdent à une mémoire logiquement partagée et le fait qu’ils participent logiquement

à un même code ou non influe peu. L’aspect qui est important est le nombre de processus

et leur distribution sur les ressources d’exécution. Les mises en œuvres adaptées à l’échelle

d’une machine à mémoire physiquement partagée et celles adaptées à l’échelle de la grille

de calcul sont en effet très différentes.

Description à base de composants Ce type d’applications se prête tout particulièrement

à un développement basé sur des composants. En effet, il s’agit de codes relativement

in-dépendants et développés par des équipes différentes. L’utilisation de composants facilite

le couplage de ces codes en permettant d’identifier clairement leurs points d’interaction.

Afin de profiter de cet avantage, chaque version de l’exemple est décrite sous la forme d’un

assemblage de deux composants qui encapsulent chacun l’un des codes couplés.

Chacun de ces codes existe en deux versions : l’une séquentielle, l’autre parallèle. Afin

de pouvoir optimiser l’application pour les ressources d’exécution disponibles, il convient de

permettre le choix entre les différentes versions de chaque code sans nécessiter de

modifica-tion de la descripmodifica-tion de l’applicamodifica-tion. C’est dans ce but qu’a été introduite la possibilité de

décrire de multiples mises en œuvres des composants. Les différentes versions de chaque

code sont donc décrites comme des mises en œuvres distinctes d’un unique composant.

Les versions séquentielles des codes peuvent être décrites sous la forme de mises en

œuvres primitives de composants ou bien elles peuvent s’appuyer sur des composants de

grain plus fin regroupés au sein d’une mise en œuvre composite. Les versions parallèles

pourront typiquement être mises en œuvre en utilisant un squelette de composant SPMD

parallèle décrit à l’aide de composites et de la généricité comme présenté au chapitre 4.

Au sein de ces assemblages, il existe trois catégories d’interactions :

– à l’intérieur des composants parallèles, les instances de composant qui supportent le

parallélisme interagissent à l’aide de passage de message et de communication

collec-tive ;

– dans la première version de l’exemple, les deux instances de composant qui forment

l’application interagissent par appel de méthode ; et

– dans la seconde version de l’exemple, les deux instances de composant qui forment

l’application interagissent par partage de mémoire.

Chacune de ces interactions logiques peut être mises en œuvre de différentes manières. Le

choix de la mise en œuvre la plus adaptée dépendant de la mise en œuvre des composants

qui participent à l’interaction et des ressources d’exécution sur lesquelles ils sont déployés.

Ces caractéristiques caractérisent une situation où l’utilisation de connecteurs pour décrire

les interactions est intéressante.

Utilisation des connecteurs Une première catégorie d’interaction utilisée dans ces

exemple est constituée par l’appel de méthode. Cette interaction implique deux participants

et peut être supportée par un connecteur avec deuxrôles. Unrôle usercorrespond à

l’utili-sateur du service et unrôleproviderà son fournisseur du service.

Suivant le modèle sous-jacent, les types de ports qui peuvent participer à une telle

inter-action peuvent varier. On s’intéressera à un modèle qui supporte classiquement des types

de port primitifsUses<T>etProvides<T>oùTest une interface objet. On peut aussi

considé-rer que deux générateurs primitifs existent qui mettent en œuvre le connecteur quand lerôle

userest rempli par un unique port de typeUses<U>et que lerôleproviderest rempli par un

unique port de typeProtides<P>et quePhérite deU. Le premier met en œuvre le connecteur

lorsque les deux composants impliqués sont localisés au sein d’un même processus alors

que le second gère l’appel distant.

Le cas des appels de méthodes parallèles au contraire doit être mis en œuvre par des

générateurs décrits par l’utilisateur. Il est nécessaire de décrire trois générateurs pour

gé-rer les cas 1 vers N, N vers 1 et M vers N. Dans l’exemple du générateur qui supporte

60

Chapitre 5. Introduction du concept de connecteur dans les modèles de composants

hiérarchiques

Rôle user

Rôle provider

Serveur

Serveur

Client

Client

Client

Connecteur

Figure 5.1 – Utilisation d’un connecteur

pour décrire une interaction par appel de

méthode parallèle. Le connecteur

repré-senté par une ellipse possède deux rôles

représentés par des cercles pleins : le rôle

useret lerôleprovider. Chacun de cesrôles

est rempli les ports de plusieurs instances

de composant pour gérer le cas de l’appel

de méthodeM×N dans le cas représenté

de deux codes parallèles de degré 3 et 2.

Rôle accessor

Code B

Code B

Code A

Code A

Code A

Figure 5.2 – Utilisation d’un connecteur

pour décrire une interaction par partage

de mémoire. Le connecteur possède un un

uniquerôle accessorrepli par les ports de

tous les composants qui accèdent à la

mé-moire dans le cas représenté de deux codes

parallèles de degré 3 et 2.

le cas de M vers N, les rôles user et provider doivent être remplis par plusieurs ports

de typeUses<Part<U> >et Provides<Part<P> >respectivement oùPart<P>est une interface

générique dans laquelle seule une partie de chaque argument est passée. Un exemple

d’uti-lisation de ce connecteur est présenté sur le schéma de la figure 5.1.

La seconde catégorie d’interaction utilisée est constituée du partage de mémoire. Cette

interaction implique un nombre quelconque de participants sans qu’on puisse distinguer

de rôle spécifique pour ces participants. Elle peut donc être supportée par un connecteur

ne comportant qu’un seulrôle:accessor.

Ce connecteur peut être mis en œuvre par des générateurs qui nécessitent que ce rôle

soit rempli par des ports de typeUses<MemAccess> où MemAccess est une interface qui

per-met l’accès à la mémoire et la manipulation de verrous pour l’accès concurrent. Ces

dif-férents générateurs peuvent imposer des contraintes différentes concernant la localisation

des composants qui participent à l’interaction. Un exemple d’utilisation de ce connecteur

est présenté sur le schéma de la figure 5.2.

Une troisième catégorie d’interaction est constituée du passage de message à la MPI. Il

s’agit d’un cas très similaire à celui du partage de mémoire qui peut être supporté par un

connecteur comportant un uniquerôle.

Il faut cependant prendre en compte le fait que les composants sont encapsulés dans des

composites. Dans le cas du partage de mémoire entre composants parallèles par exemple, en

instanciant une connexion dans l’assemblage où sont instanciés les composites, on obtient

un assemblage qui ressemble à celui présenté sur le schéma de la figure 5.3. Les composites

doivent exposer les ports de leurs instances de composant internes pour que ces instances

puissent participer à la connexion.

Cette description du composite ne décrit pas toute la sémantique requise. Le fait que les

différentes instances de composant qui composent le composite doivent interagir par une

mémoire partagée n’y est notamment pas spécifié. L’interface du composant est donc fragile

et un utilisateur qui ferait le choix de ne pas connecter ces ports à une même connexion

empêcherait le composant de fonctionner correctement.

De plus, cette approche expose la manière dont le composant est mis en œuvre.

L’inter-face de la version parallèle présentée est ainsi différente de celle d’une version séquentielle

qui n’exposerait qu’un unique port. En nécessitant des interfaces différentes, ces mises en

œuvres ne peuvent donc pas constituer des mises en œuvres interchangeables d’un unique

5.1. Analyse préliminaire 61

Code A

Code A

Code A

Code A

Code A

Figure 5.3 – Couplage de deux

com-posants parallèles avec un

connec-teur instancié entre les composites.

Les composites exposent les ports de

toutes leurs instances de composant

internes pour qu’elles puissent

par-ticiper à la connexion.

Code B

Code B

Code A

Code A

Code A

I I

Figure 5.4 – Couplage de deux composants

pa-rallèles en instanciant des connecteurs entre

les composites et au sein de chaque composite.

Des composants sont introduits pour relier les

connexions qui constituent un goulot

d’étrangle-ment possible.

composant.

Afin de contourner ce problème, il est nécessaire d’insérer à l’intérieur des composites

une connexion qui exprime le fait que les instances de composants qui les composent

in-teragissent. Toutefois, puisque les composants doivent exposer des ports, il devient alors

nécessaire d’insérer en plus une instance de composant responsables de l’adaptation

d’in-terface comme présenté sur le schéma de la figure 5.4.

Dans le cas du partage de mémoire par exemple, ce composant doit assurer que les

mémoires partagées au sein des deux connexions auxquelles il participe sont toujours

syn-chronisées. Ceci peut s’avérer très complexe dans le cas où les connecteurs n’ont pas été

prévus pour ça. De plus, toutes les interactions entre les deux composites transitent par

cette unique instance de composant qui peut alors former un goulet d’étranglement. Il s’agit

d’un problème important pour les performances dans le cas de haut degrés de parallélisme

comme on en trouve dans les applications de calcul à haute performance.