![]()
Précédent : Architectures de processeurs (cf. 2.2)
Remonter : Résultats nouveaux Suivant :
Communication
dans les calculateurs parallèles
Mots clés : Matlab, programmation parallèle, parallélisation automatique, portage d'applications, optimisation .
Participants : François Bodin, Stéphane Chauveau, Thierry
Lafage, Yann Mével, Erven Rohou, André Seznec, Paul Van der
Mark.
Nous explorons une nouvelle direction de recherche qui consiste à étudier les techniques de production de codes parallèles à partir de langages plus proches d'un domaine d'application. Pour cela nous utilisons Matlab, un système spécialisé pour le calcul numérique et largement diffusé, pour la production automatique d'applications scientifiques séquentielles et parallèles en langage Fortran et C++. Notre approche permet de dériver directement l'application finale à partir du prototype écrit en Matlab. De plus la description des algorithmes en Matlab est de plus haut niveau qu'en Fortran (la gestion des structures de données n'est pas spécifiée dans le code) et laisse donc plus de liberté pour la génération et les transformations de code. Le prototype de compilateur paramétrable Menhir est disponible depuis cette année et est présenté dans la thèse de S. Chauveau [[10]]. Menhir est actuellement utilisé par la société LMS (http://www.lms.be, Louvain, Belgique) dans le cadre du développement d'une application de traitement du signal.
Le portage (parallélisation et amélioration de performance) d'applications sur machines hautes performances est une activité techniquement difficile faisant appel à beaucoup de savoir-faire.
Le système TSF vise à accélérer cette activité grâce à l'utilisation conjointe de techniques issues de deux domaines : la parallélisation automatique et le raisonnement à partir de cas («Case-Based Reasoning»).
La parallélisation automatique fournit une bibliothèque de transformations de programmes à appliquer sous contrôle de l'utilisateur, dans le cas de TSF. Le raisonnement à partir de cas permet de recueillir les fruits des expériences de portage précédentes et fournit un accès au savoir-faire de l'expert. De plus pour aider l'utilisateur, le système permet d'instrumenter les codes afin d'analyser leurs performances. L'intégration des deux méthodes a pour objectif d'aider au choix des transformations adaptées en s'appuyant sur des expériences de portage similaires répertoriées dans la base de cas. Avec la technique de raisonnement à partir de cas, ces choix sont guidés par des calculs de similarité de contexte.
Cette étude est réalisée en collaboration avec la société Simulog qui fournit toute l'infrastructure de base à l'analyse de programmes Fortran grâce à son outil Foresys et le projet Irisa Repco (R. Quiniou).
Le système est actuellement en cours de test à NASA-AMES (USA).
Au cours d'une chaîne de compilation classique, le type d'informations disponibles subit une profonde modification. Les premières phases ont une bonne connaissance de l'algorithme et des structures de données complexes utilisées par le langage de haut niveau. Les dernières phases ont une vision très précise de l'exploitation des ressources matérielles du processeur. Idéalement elles devraient connaître l'information que possédaient les premières transformations et disposer du maximum de connaissances pour appliquer efficacement des optimisations. Toutefois les compilateurs actuels ne propagent pas l'information et chaque étape doit régénérer une bonne partie des informations qu'elle utilise.
Il est important de conserver à chaque étape l'information facile à obtenir dans la mesure où celle-ci peut se révéler importante plus tard. Par exemple le générateur de code est souvent capable de déterminer si des accès à la mémoire sont dépendants par la connaissance qu'il a des variables locales et globales et des indices de tableaux. L'optimiseur bas-niveau a un besoin crucial de ce type d'information mais il ne peut que rarement l'obtenir sans faire appel à des mécanismes complexes comme la résolution d'équations diophantiennes.
Nous pensons aussi qu'il est important de briser la chaîne de
compilation classique qui applique consciencieusement une liste
de transformations dans un ordre immuable. L'information doit
pouvoir remonter vers les optimisations de haut-niveau et
celles-ci doivent être capables de défaire une partie de leur
travail. Par exemple, il est parfois nécessaire de maîtriser les
expansions de code résultant de nombreuses techniques de mise en
uvre du parallélisme à grain fin pour ne pas
dégrader les performances du cache d'instructions [[33]].
Notre activité dans ce domaine se concentre sur l'interaction entre les optimisations au niveau du code source et celles effectuées au niveau du code machine.
Les réflexions sur la nécessité de faire communiquer les différentes phases d'un processus de compilation ainsi que de comparer les performances obtenues par différentes stratégies ont abouti au développement d'un premier prototype de compilateur. Les transformations de haut niveau et de bas niveau sont fortement couplées et s'échangent de l'information via un langage de communication simple appelé IL. Le principal objectif de ce langage est d'établir une carte qui mette en correspondance des portions de code haut-niveau et leur équivalent en assembleur. Ce nommage permet par exemple au générateur de code de décrire des propriétés visibles à haut-niveau qui pourront être exploitées à bas niveau.
Le compilateur dispose d'un certain nombre de transformations. Chacune est capable de modifier un fragment de programme, de quantifier son action, éventuellement de l'annuler et de transmettre de l'information à l'extérieur. Il est alors possible d'établir une véritable stratégie de compilation à partir d'un ensemble de transformations, la stratégie s'exprimant comme un algorithme. De nombreux autres critères, tels que l'allocation des registres, sont pris en compte.
Une partie importante de ces travaux est effectuée dans le cadre du projet Esprit LTR Oceans [[25]].
Dans le cadre de la simulation dirigée par la trace, les outils logiciels d'intrumentation actuellement disponibles (Pixie, ATOM, ...) ont le défaut de ralentir de manière très significative l'exécution des applications (facteur 10-50), même si la trace générée n'est pas utilisée dans sa totalité. En effet, la trace d'une application est très volumineuse (plusieurs giga-octets pour de petites applications) et donc très difficile à utiliser en ce sens qu'une simulation sur une trace entière est très (trop) coûteuse en temps. Alors, des techniques telles que l'échantillonnage de trace sont utilisées pour réduire ce volume. D'autre part, tout programme comporte une phase d'initialisation qui n'est pas représentative de l'exécution totale. De ce fait, les premiers millards d'éléments de trace qui correspondent à cette phase d'initialisation sont, dans la plupart des cas, ignorés.
Le ralentissement induit par l'instrumentation est trop important et pour cela ne permet pas, actuellement, de collecter ne serait-ce que quelques échantillons de traces utilisables (i.e. après la phase d'initialisation) sur de réelles (grosses) applications exécutées dans leur totalité. Par réelles applications, nous entendons par exemple les applications de calcul intensif, les applications de type client-serveur (X, gestion de bases de données), mais aussi le système d'exploitation lui-même qui génère une activité dont on ne tient généralement pas compte.
Afin d'adresser ce problème, nous avons défini une nouvelle méthode de collecte de traces appelée «code cloning tracing» qui permet de ne pas trop ralentir les parties du programme testé dont la trace sera rejetée. Le code original est dupliqué. Les deux copies (clones) sont instrumentées de manière légère. Cette légère instrumentation permet, à l'exécution, de passer dynamiquement d'un clone à l'autre. Un des clones, appelé Pinst est ensuite instrumenté de manière lourde afin de collecter les traces (adresses des données par exemple), alors que l'autre clone, appelé Pexec sera exécuté tel quel: il correspond au mode d'exécution rapide de l'application instrumentée, ou mode «sans trace».
Un premier prototype appelé calvin a été implémenté à l'aide de SALTO [[34]]. En mode «sans trace», les applications que nous avons testées (l'ensemble des benchmarks SPEC95) subissent un ralentissement allant de 1.02 à 2.09. Ceci rend réaliste la collecte de traces sur de réelles applications telles que nous les avons définies.