• Aucun résultat trouvé

computeMt1 transpMaster go state ctrl inout inout ctrl computeTsk compute compute transposeMt1 computeMt2 transposeMt2 transposeTsk compute compute compute compute inout inout inout

Figure5.3 – Représentation de l’assemblage Halley du cas d’utilisation TRANSP.

en œuvre). Les assemblages des cas EP-EP et ST-ST contiennent deux instances de métatâches appelées Mt1 et Mt2. Le code complet de ces assemblages est fourni en annexe B.10 pour EP-EP et en annexe B.11 pour ST-ST (page 180). Le type des deux instances de métatâche présent dans chaque assemblage est le même. Le type des métatâches du cas EP-EP est le même que pour EP. ST-ST est équivalent à EP-EP, mais il emploie des métatâches de ST.

L’assemblage Halley du cas d’utilisation TRANSP est décrit par la figure 5.3. Le code complet de l’assemblage est fourni en annexeB.12(page182). L’assemblage est constitué d’un composant qui dirige l’exécution du cas d’utilisation, d’une sec-tion contenant quatre métatâches et de deux composants fournissant des mises en œuvre pour les métatâches. Ce cas d’utilisation a recours à deux types de métatâches définis à la figure5.4: ComputeMt qui applique un calcul sur chaque ligne entière (uti-lisant un partitionnement par blocs de lignes réguliers) et TransposeMt qui effectue une transposition du tableau bidimensionnel (utilisant un partitionnement en blocs réguliers). Ces deux métatâches effectuent des opérations sur un unique buffer de donnée. La métatâche TransposeMt utilise le langage de relations transposition. Celui-ci permet d’appairer des fragments qui se trouvent de chaque côté de la diago-nale d’un buffer de données partitionné en blocs réguliers carrés. Pour chaque tâche, une paire de fragments est simultanément lue et écrite : celui à la position pi, jq et celui pj, iq pour tout i et j tel que i ě j. Il génère autant de tâches qu’il y a de paires de fragments. Les fragments sur la diagonale n’engendrent qu’une tâche par fragment.

5.2 Discussion sur la séparation des préoccupations

Cette section analyse la capacité de Comet et de Halley à offrir des méca-nismes permettant de séparer les préoccupations d’une application. Dans un premier temps, elle évalue comment des algorithmes peuvent être scindés et placés dans des unités de composition distincte et dont les mises en œuvre sont indépendantes. Dans un second temps, elle se concentre tout particulièrement sur le fonctionnement et les bénéfices apportés par la composition à base de flux de données disponible dans Halley et évalue le degré d’indépendance des codes composés.

<metatask id="ComputeMt">

<parameter id="dWidth" type="uint64"/> <parameter id="dHeight" type="uint64"/> <parameter id="bHeight" type="uint64"/> <inoutPort id="inout" type="BlockArray2D"

dataBufferSetter="fData2D"

dataPartitionSetter="fPart2D"/> <relation language="identity"/>

<setting language="python3">

def fData2D(mt):

return dict(width=mt.dWidth, height=mt.dHeight, elemSize=8) def fPart2D(mt):

return dict(blockWidth=mt.dWidth, blockHeight=mt.bHeight)

</setting> </metatask>

<metatask id="TransposeMt">

<parameter id="dWidth" type="uint64"/> <parameter id="dHeight" type="uint64"/> <parameter id="bWidth" type="uint64"/> <parameter id="bHeight" type="uint64"/> <inoutPort id="inout" type="BlockArray2D"

dataBufferSetter="fData2D"

dataPartitionSetter="fPart2D"/> <relation language="transposition">

inout(i,j) = inout(j,i)

</relation>

<setting language="python3">

def fData2D(mt):

return dict(width=mt.dWidth, height=mt.dHeight, elemSize=8) def fPart2D(mt):

return dict(blockWidth=mt.bWidth, blockHeight=mt.bHeight)

</setting> </metatask>

5.2. DISCUSSION SUR LA SÉPARATION DES PRÉOCCUPATIONS 105

5.2.1 Scission algorithmique dans des unités de composition

distinctes

Du point de vue du génie logiciel, le modèle Comet et plus précisément la mise en œuvre, Halley permet de définir des morceaux indépendants d’une même ap-plication. En effet, étant donné que Comet est un modèle de composants, les unités logicielles telles que les métatâches et les composants ont des interfaces bien définies. Ces interfaces sont les seuls points d’interaction qu’une unité logicielle peut avoir avec les autres. Ainsi, par construction, il est possible de tracer les dépendances entre les unités logicielles d’une même application en analysant les connexions d’un assemblage. Par exemple, dans le cas du cas d’utilisation TRANSP, la métatâche de transposition est indépendante des autres métatâches : la métatâche contient seule-ment des ports de données et une expression de relations, tandis que le composant responsable de sa mise en œuvre est dépendant uniquement de ces deux dernières informations. Par conséquent, l’adaptation de la méthode de transposition ne né-cessite pas de compréhension du fonctionnement interne des unités logicielles. La métatâche de transposition ainsi que sa mise en œuvre peuvent être remplacées par une version alternative possédant, a minima, les mêmes interfaces ou même par une portion d’assemblage compatible. Le remplacement peut être réalisé sans aucune modification de la mise en œuvre des autres composants de l’assemblage. Le même raisonnement est applicable sur les cas d’utilisation EP et ST.

On peut noter toutefois qu’un type de métatâche et ses mises en œuvre sont liés entre eux par une interface associée au greffon de relations choisi par le type de métatâche (à travers le langage de relations). Une modification de l’interface des métatâches (p. ex. changement de partitionnement, ajout d’un port de donnée) peut impacter l’interface du port compute des métatâches et donc impacter aussi les composants qui sont responsables de leur mise en œuvre. En effet, l’interface du port compute est définie par les greffons de relations. La description d’une interface efficace et suffisamment généraliste pour s’abstraire du partitionnement des ports de données par exemple est un problème ouvert. Lorsque l’interface du port compute n’est pas généraliste, le concepteur d’une application peut avoir recours à des com-posants d’adaptation. Ces comcom-posants fournissent un service compatible avec le port computede la métatâche et utilisent un service compatible avec le port fournissant une mise en œuvre pour la métatâche. Une telle opération peut être réalisée dans les cas d’utilisation EP, ST et TRANSP en cas de modification mineure de l’interface des métatâches (p. ex. partitionnement).

Pour résumer, Comet et plus précisément, sa mise en œuvre Halley permet d’exprimer différentes parties d’un algorithme dans des unités de composition dis-tinctes assemblées principalement par le biais de flux de données. Par le biais de la composition use-provide (et grâce au port compute), Comet permet décrire la mise en œuvre des tâches issues d’une métatâche. Cependant, les métatâches et leur mise en œuvre contenue dans des composants entretiennent un lien fort décrit par l’interface compute.

5.2.2 Indépendance des codes composés

Les métatâches issues de Comet peuvent être composées ensemble, malgré l’usage de partitionnement différent. Cela est par exemple visible dans le cas d’uti-lisation TRANSP. D’une part, les tâches de calcul opèrent sur des blocs de lignes, étant donné que le calcul requiert des données selon une dimension complète. D’autre part, les métatâches de transposition travaillent sur des blocs carrés. Le découpage en bloc permet d’effectuer la transposition en parallèle (mais aussi en même temps que les opérations de calculs) pour être efficace et l’application d’un traitement effec-tuée sur-place force à utiliser des blocs carrés. Chaque métatâche expose des ports de données partitionnées requis en privilégiant la simplicité et surtout l’efficacité de sa mise en œuvre, indépendamment de l’ensemble de l’application. Elle peut donc être développée et maintenue indépendamment du reste de l’application. La connexion de ces deux types de métatâches (dotées de port de données avec des partitionnements différent) est une opération transparente aux yeux du concepteur d’une application, mais qui peut avoir un impact sur les performances de l’application à l’exécution (pouvant être aussi bénéfique que négatif, p. ex. repartitionnement).

Lorsque deux métatâches composées opèrent sur des buffers partitionnés de diffé-rentes manières, les concepteurs d’une application Halley font face à un dilemme : considérant chaque métatâche isolée, est-il préférable qu’elle opère sur des partition-nements adaptés à une mise en œuvre efficace, au risque d’effectuer des opérations de repartitionnement potentiellement coûteuses, ou est-il mieux que la métatâche opère directement sur un partitionnement potentiellement inadapté, mais n’impli-quant pas de repartitionnement une fois instanciée au sein d’un assemblage ?

Par exemple, dans le cas d’utilisation EP-EP, la taille des blocs peut être adaptée entre les deux codes EP en fonction de la quantité de travail qu’il nécessite de ma-nière à réguler la granularité des tâches (pour permettre une utilisation efficace des ressources tout en modérant les surcoûts liés à la création des tâches). Il en est de même pour ST-ST. Dans le cas d’utilisation TRANSP, plusieurs solutions utilisant le même partitionnement de bout en bout sont possibles. D’une part, les métatâches de transposition peuvent opérer sur des blocs de lignes. Cela a pour effet d’engendrer artificiellement une barrière, car chaque bloc de lignes dépend d’une portion de tous les autres ce qui dégrade fortement les performances (cf. impact du repartitionne-ment évalué en section5.3). D’autre part, les métatâches de calcul peuvent travailler sur des ensembles de blocs bidimensionnels appartenant à un même bloc de lignes. Ces deux solutions sont néfastes pour la séparation des préoccupations, car le choix des interfaces (c.-à-d. partitionnement) prend en compte le contenu d’un assemblage particulier plutôt que de considérer les entités logicielles de manière isolée et indé-pendamment d’un usage spécifique. Ainsi, une modification des autres métatâches de l’assemblage telle que le partitionnement a un impact sur la mise en œuvre des autres métatâches. De plus, ces deux cas ne sont pas supportés par les greffons de relations, car il engendre un nombre variable de dépendances par tâche à l’exécution, ce qui n’est pas actuellement supporté par la version actuelle d’OpenMP (version 4.5 et 5.0). Enfin, bien qu’ils n’engendrent pas de repartitionnement (et donc pas de surcoûts liés à cette opération), ces cas augmentent significativement le nombre de

5.3. ÉVALUATION DES PERFORMANCES 107