• Nenhum resultado encontrado

2.6.1 Analyse . . . 48 2.6.2 Objectifs et approche. . . 49 2.7 Conclusion. . . 49 Cette section présente une des contributions de la thèse : un modèle à composant avec support de l’ordonnancement de tâches. La section 3.1 donne un aperçu du modèle Cometet de sa structure. La section3.2 présente les entités fondamentales deComet. La section3.3montre comment construire une application en composant les entités fondamentales du modèle. Enfin, la section 3.5 conclut ce chapitre.

3.1 Aperçu

Cometest un modèle de composants dédié au calcul haute performance et sup- portant l’ordonnancement de graphes de tâches. Il vise à assembler simplement des codes parallèles indépendants tout en permettant une exécution efficace. Pour cela, Comet se base sur le modèle de composants existant L2C qui supporte la compo- sition use-provide et MPI. Il l’étend en supportant une nouvelle forme d’interaction entre les composants à base de flux de données manipulant des données partitionnées ainsi qu’en ajustant les ports use-provide. Cette extension permet de construire un graphe de tâches en composant successivement des morceaux de graphe de tâches.

Cometcible en particulier les architectures à mémoire partagée. Néanmoins, il supporte aussi les architectures à mémoire distribuée en se basant sur MPI pour la communication entre les nœuds de calcul.

3.2. UNITÉS DE COMPOSITION 53

SampleCompo in port

out port

provide port use port

inout port

Component SampleCompo

provide Interpol pInter use LinearAlg uLinAl indata Array1D input outdata Array1D output inoutdata Array2D inoutput

attribute String aName Attribute

Figure 3.1 – Exemple de définition de composant (gauche) et sa représentation graphique (droite).

interface orientée objet avec un ensemble de méthodes). Un port use décrit un ser- vice requis par le composant (il permet d’exécuter des méthodes sur une interface orientée objet fournie par un autre composant). Un port MPI partage un commu- nicateur MPI entre des composants permettant ainsi des communications point à point ou collectives entre des parties d’une application répartie sur différents nœuds de calculs.

Un port de données permet à un composant de partager des buffers de données d’un type bien défini (p. ex. tableau bidimensionnel). Un buffer de données est un conteneur de données (c.-à-d. information quelconque). C’est une instance d’une structure de données qui à la propriété d’être adressable et d’occuper une zone potentiellement non contiguë en mémoire partagée. Les types de buffers de données disponibles ne sont pas définis par le modèle lui-même. C’est la mise en œuvre de Cometqui est responsable de la définition des types disponibles et de moyens pour les étendre. Ainsi, un type buffer de données est un identifiant qui peut prendre la forme d’une chaîne de caractères telle que “Array2D”. Il dispose d’un mode d’accès qui peut être :inpour un accès en lecture seule,out pour un accès en écriture-seule et inout pour un accès simultanément en lecture et écriture. Un port de données possède un état décrivant l’accessibilité du buffer de données partagé. Les valeurs possibles pour cet état sont disponibles et occupées. Un port de données est disponible si aucune autre unité de composition n’utilise le buffer partagé (lecture ou écriture).

Exemple La figure 3.1 montre un exemple de composant et sa représentation gra- phique. Le langage utilisé pour décrire le composant et plus généralement dans ce chapitre n’est pas celui de la mise en œuvre de Comet. Il est uniquement utilisé à des fins pédagogiques pour présenter de façon concise les informations nécessaires à la spécification d’éléments du modèle ainsi que la manière dont elles sont structurées.

Des exemples de définitions sont donnés dans le chapitre4.

Propriétaire des données Un composant qui partage un buffer de données en est le responsable : il contrôle son cycle de vie. À travers un port de données, un composant transfère temporairement la responsabilité du buffer de données à ceux qui peuvent y accéder (jusqu’à ce que le port soit disponible). Un composant peut sonder l’état d’un port de données ou attendre que le buffer soit disponible sur ce port.

Port use/provide et effets de bords Les ports use et provide possèdent une propriété task-safe permettant d’indiquer qu’ils fournissent ou requièrent un service task-safe. Un service task-safe est utilisable dans un contexte d’une tâche. Cette propriété est fixée explicitement par les concepteurs d’un composant (c.-à-d. valeur booléenne précisant si le port est task-safe ou non). Étant donné que des tâches peuvent s’exécuter en parallèle, cela signifie qu’un service task-safe doit être com- patible avec une utilisation concurrente (c.-à-d. thread-safe). Un port use task-safe indique que le port requiert un service utilisable dans un contexte d’une tâche. Ce- pendant, la mise en œuvre de Cometpeut ajouter des contraintes supplémentaires à ce type de service. En effet, elle peut avoir recours à des supports exécutifs d’or- donnancement de tâches qui imposent des contraintes sur l’implémentation de ces services tels que la non-présence de verrous dans le code exécuté ou encore sa ré- entrance. Le modèle Comet considère que les services task-safe sont sans effet de bord, c’est-à-dire que leur utilisation n’a pas d’impact sur le comportement du com- posant qui les fournit vu de l’extérieur (c.-à-d. pas d’influence sur le comportement de l’ensemble des services proposés par le composant). L’ensemble des services task- safe sont un sous-ensemble des services thread-safe et un sur-ensemble des services sans effet de bord.

3.2.2 Métatâches

Aperçu Une métatâche est une unité de composition décrivant une partie d’un calcul parallèle à base de tâches. Elle peut être perçue comme une abstraction d’une portion d’un graphe de tâches : une métatâche décrit la structure d’un morceau de graphe de tâches (ensemble des tâches et des dépendances entre les tâches) et pos- sède des interfaces bien définies permettant d’assembler cette portion avec d’autres parties. Cette unité a pour but de soumettre, à l’exécution, un ensemble de tâches exécutant un même code. Un port de métatâche est soit un attribut, soit un port use (requis et unique), soit un port de données.

Interfaces de métatâches Un attribut de métatâche est identique à celui d’un composant et remplit le même objectif : configurer la métatâche. Plus particulière- ment, cette configuration peut contenir des valeurs nécessaires au paramétrage des ports de la métatâche. Une métatâche possède aussi un unique portuse computequi requiert un service définissant l’implémentation des tâches soumises à l’exécution.

Ce port use à la propriété d’être task-safe. Une métatâche détient au moins un port de données. Un port de données de métatâche diffère de celui d’un composant sur un seul aspect : il opère sur des buffers de données partitionnés.

Exemple simplifié de métatâche La figure 3.2 présente un exemple simplifié de métatâche, opérant sur un unique buffer de données en entrée-sortie. La figure décrit un cas simplifié où le buffer de données traité est partitionné en un seul fragment (des cas plus complexes faisant intervenir plusieurs fragments sont détaillés dans la suite cette section). La figure montre aussi un exemple de graphe de tâches pouvant

3.2. UNITÉS DE COMPOSITION 55

Metatask SimpleMt inout Array1D data

data T data

Same data buffer SimpleMt

data

Figure 3.2 – Exemple simplifié d’une définition de métatâche (gauche), sa repré- sentation graphique (milieu) et un graphe de tâches associé pouvant être produit à l’exécution par la métatâche (droite).

être généré par la métatâche : les rectangles sont des buffers de données et les cercles des tâches. C’est un exemple minimaliste d’une métatâche produisant qu’une seule tâche qui lit et écrit dans le même buffer de données.

Ports de données et partitionnements Un buffer de données partitionné est un buffer divisé logiquement en sous-ensembles disjoints appelés fragments. Une description plus formelle est donnée par la définition11. Un fragment est accessible par un index : une instance de structure de données dont le type est défini par le partitionnement (p. ex. couple d’entiers représentant la position px, yq d’un bloc dans un tableau bidimensionnel). Il peut aussi détenir des métadonnées (p. ex. taille du bloc, pointeur vers une zone mémoire). Tout comme les définitions de types de données non partitionnés, les types de données partitionnées disponibles et leur extensibilité ne sont pas définis par le modèle lui-même, mais par sa mise en œuvre.

Ainsi, un type de données partitionné est un identifiant qui peut prendre la forme d’une chaîne de caractères telle que “Array2D_Bloc”. Il est associé à un type de données, afin d’assurer la compatibilité entre les types de données partitionnées et non partitionnées.

Définition 1 Soit F l’ensemble des fragments. Soit B l’ensemble des buffers de données. On note PpEql’ensemble des sous-ensembles d’un ensemble E2. Un parti- tionnement est défini par une fonction part qui à tout buffer de données associe un ensemble de fragments :

part:B ÑPpFq tel que:

@b1 PB,@b2 PB :

b1 ‰b2 ùñ partpb1q Xpartpb2q “ ∅

(3.1)

On dénote par f ragof la fonction permettant de déterminer le buffer de données

1. DansComet, une fonction de partitionnement peut ne pas couvrir l’intégralité de l’espace d’un buffer de données.

2. XPPpEq ðñX ĎE

associé à un fragment :

f ragof :F ÑB tel que :

@b PB,@f PF :

f ragofpfq “ bðñf Ppartpbq

(3.2)

Expressions et langages de relations Le partitionnement des buffers de données est la source du parallélisme issu des métatâches (parallélisme de données). Il permet à cette unité de composition de soumettre des tâches opérant sur des fragments de buffers de données tout en évitant des situations de compétition au niveau de l’ap- plication entière grâce aux modes d’accès des ports de données (c.-à-d. cohérence des accès mutuels) : deux tâches issues d’une même métatâche ou de métatâches diffé- rentes ne peuvent pas opérer simultanément sur un même fragment en écriture3 (ou l’une en lecture et l’autre en écriture). Il est aussi et surtout la source de la compo- sition parallèle efficace des métatâches en permettant une exécution entrelaçant les traitements opérant sur chaque fragment. L’ensemble des tâches à soumettre est dé- crit par des relations. L’objectif des relations est de déterminer le nombre de tâches à créer et de spécifier, pour chaque tâche, l’ensemble des fragments (issus de buffers de données partitionnés) en dépendances. La définition2décrit plus formellement ce que sont des relations. Ces relations sont décrites à travers une expression relation- nelle définie dans unlangage de relations. Une expression relationnelle peut prendre typiquement la forme d’une expression mathématique (p. ex. expression algébrique) sérialisée sous la forme d’une chaîne de caractères. Des exemples de langages de relations sont détaillés dans le chapitre 4.5. L’élaboration d’un langage de relations permettant de définir le minimum de dépendances nécessaire (c.-à-d. sans approxi- mation), de manière concise, se révèle très complexe dans le cas général. Pour cette raison, le support des langages de relations disponibles est conçu pour être extensible de telle manière à pouvoir utiliser un DSL adapté aux cas traités. Par conséquent, les métatâches contiennent une expression relationnelle, définie sous la forme d’une simple chaîne de caractères, ainsi qu’un langage de relations associé, spécifié via un identifiant. L’utilisation d’un langage de bas-niveau et Turing-complet tel que le langage C pour définir les dépendances n’est pas exclu. Un tel langage permet d’ex- primer des graphes de tâches complexes, dans les cas inhabituels, à défaut de cibler uniquement des experts deComet. Tout comme les types de buffers de données, les langages disponibles sont implémentés dans la mise en œuvre de Comet et décrits dans la section 4.5.

Définition 2 Une relation est définie par une fonction rel qui à partir d’un en- semble de buffers de données produit un ensemble d’ensembles de fragments représen- tant, pour chaque sous-ensemble de fragments, les dépendances d’une unique tâche

3. Certains supports exécutifs supportent un mode d’accès concurrent ou exclusif aux données, cependant ils ne sont pas supportés par la version actuelle deComet