Brevet FR2865047A1 — publié le 20050715

- 1.- La présente invention a pour objet un système de génération automatique de codes optimisés opérationnels sur une plate-forme matérielle prédéfinie comportant au moins un processeur, pour un domaine d'application prédéterminé, à partir de codes sources soumis par des utilisateurs (ces utilisateurs étant compris ici au sens large et incluant des utilisateurs finaux, mais également des programmeurs d'applications ou des programmeurs système). Depuis l'origine de l'informatique, de nombreux travaux ont été réalisés concernant les compilateurs. Le principe d'un compilateur est d'analyser un code source décrit dans un langage de haut niveau puis de générer son équivalent en code binaire pour la machine cible. En général ce processus est effectué statiquement avant l'exécution. Il existe aussi des interpréteurs mettant en oeuvre des technologies de compilation dynamique qui ont permis de lever cette dernière contrainte en offrant la possibilité de générer le code au dernier moment lors de l'exécution. Le compilateur est un élément de la chaîne de préparation des programmes. Le résultat de la compilation peut être complété par des procédures déjà compilées et générées (compilation séparée ou bibliothèques), liées statiquement au chargement ou dynamiquement à l'exécution. Les compilateurs sont en général organisés en trois phases: 1. Génération de code intermédiaire: à partir du code source, le compilateur reconnaît les idiomes et génère une forme abstraite indépendante du langage source, communément appelée langage intermédiaire. Ce langage est indépendant de la machine cible. 2. Optimisation haut niveau: dans cette phase sont regroupées un certain nombre d'optimisations qui sont en général indépendantes de l'architecture cible: propagation de constantes, réduction de force, expressions communes... Ces optimisations correspondent en général à des métriques simples: diminution du nombre d'instructions, simplification de la structure du code. 2865047 2 3. Génération de code et optimisation bas niveau; dans cette phase sont réalisées toutes les opérations et optimisations propres à la machine cible: génération et sélection des instructions, allocation de registres, ordonnancement des instructions, etc. Les qualités d'un compilateur sont non seulement liées à l'architecture cible (performance du code généré) mais aussi au langage source (plus ou moins facile à compiler) et à ses caractéristiques en tant que logiciel (robustesse, richesse des options, vitesse d'exécution, etc.). Sauf le cas particulier de la compilation dynamique, l'ensemble des trois phases ci-dessus doivent être réalisées avant l'exécution et ce dans un temps raisonnable, ce qui limite d'autant la complexité des algorithmes d'optimisation qui peuvent être implémentés dans un compilateur. Une grande part de la recherche sur les compilateurs est donc en premier lieu liée au choix et à la définition du langage source de haut niveau. Les évolutions des compilateurs sont également liées aux évolutions de l'architecture des processeurs et plus précisément aux modèles de performance décrivant le comportement de ces architectures. Comme dans tout problème d'optimisation, une difficulté importante est la définition d'une fonction coût qui soit représentative du temps d'exécution. Les premiers jeux d'instructions avaient des comportements très simples: le temps d'exécution d'une séquence d'instructions s'obtenait tout simplement comme la somme des temps d'exécution de chacune des instructions de la séquence. En conséquence, le processus d'optimisation était très simple et la principale stratégie d'optimisation consistait à réduire le nombre et la complexité des instructions générées. L'apparition des premiers jeux d'instructions CISC ("Complex Instruction Set Computer") a légèrement modifié la donne dans la mesure où des instructions très complexes devenaient disponibles. Le problème d'optimisation devenait alors essentiellement un problème de reconnaissance d'idiomes ( pattern matching ). Dans cette même catégorie, on peut inclure les jeux d'instructions vectorielles et les vectoriseurs qui étaient capables de reconnaître les boucles qui se prêtaient directement à une génération de code vectoriel. Au besoin le code source pouvait aussi être transformé pour faire apparaître des structures de code vectoriel. 2865047 3 Une rupture dans les stratégies d'optimisation est venue avec l'introduction des pipelines, évolution architecturale qui découpe le traitement des instructions en plusieurs opérations exécutées séquentiellement, à l'image d'une chaîne d'assemblage dans une usine. Cette technique qui permet de superposer l'exécution de plusieurs instructions à un instant donné améliore sensiblement les performances des systèmes, mais introduit des déviations importantes en cas de rupture de pipeline, suite par exemple à la présence d'instruction de branchement. Elle a ainsi mis fin à la prédictibilité du comportement d'un code donné. Une autre rupture importante provient de l'utilisation des hiérarchies mémoires: l'optimisation devait alors s'appuyer sur l'évaluation fine d'une métrique très particulière: la localité (spatiale et temporelle). Cependant l'interaction entre le processeur et le système mémoire étant simple, le processus d'optimisation devait avoir pour principal objectif de minimiser le nombre de "miss" (échecs) car chaque échec rajoutait une pénalité au temps d'exécution. Il faut noter cependant que la minimisation du nombre de "miss" était délicate et essentiellement réalisable pour des structures de code simple de type boucle. Cette difficulté d'estimation de manière statique des propriétés de localité conduisit à utiliser des méthodes d'optimisation par gestion des profils: le code était exécuté une première fois pour déterminer précisément les propriétés de localité (construction d'un profil). Ce profil était alors utilisé dans une deuxième passe pour effectuer les optimisations liées à l'exploitation de la localité. A ce niveau de complexité architecturale, il était déjà très difficile de définir de manière simple une stratégie efficace d'optimisation. Plus exactement, il était extrêmement délicat de savoir combiner différentes optimisations et ce même sur des cas très simples: il n'existait pas de mécanisme permettant de modéliser/prendre en compte de manière efficace les performances. C'est dans ce cadre qu'ont été développées les techniques de compilation itérative qui combinent exécution et optimisation de manière à générer le meilleur code possible. Plus précisément, la compilation itérative consiste en la mise en oeuvre d'une boucle de "transformation de code-mesure de performance (statique ou dynamique)". Il s'agit essentiellement de techniques permettant d'explorer l'espace des transformations de code à moindre coût pour ne conserver que les solutions ayant donné les meilleures solutions. Le coût 2865047 4 très élevé en temps de calcul et de mise au point de ces méthodes itératives a limité leur champ d'application à l'optimisation de bibliothèques. Les architectures RISC ("Reduced Instruction Set Computer") qui utilisaient un jeu d'instructions simple et uniforme (à l'opposé du CISC) ont vu le jour vers le milieu des années 80. Les premières générations de processeurs RISC étaient très limitées en termes de fonctionnalité et la qualité du code généré (en particulier l'ordonnancement des instructions) devenait alors un facteur clé dans la course à la performance. De manière très similaire, les architectures VLIW ("Very Long Instruction Word") utilisaient des techniques de compilation tout à fait semblables pour exploiter au mieux le matériel. Ces deux architectures RISC et VLIW avaient toujours un modèle de performance simple déterministe dans la mesure où globalement les instructions s'exécutaient dans le même ordre que le code du programme généré, ce qui simplifiait considérablement le comportement de ces architectures et par là-même le processus d'optimisation. Toutefois, très rapidement ces architectures ont généralisé l'utilisation du pipeline et des caches mémoire pour obtenir de meilleures performances (plus précisément, ceci a amené une amélioration des performances en moyenne et au détriment de la prédictibilité) L'apparition des architectures superscalaires (capables d'exécuter plusieurs instructions par cycle) et surtout les mécanismes de traitement dans le désordre ("Out of Order Processing") des instructions ont rendu encore plus difficiles les processus d'optimisation des performances. De plus, simultanément les hiérarchies mémoires ont évolué rapidement: le nombre de niveaux a augmenté et surtout divers mécanismes permettant de gérer de manière plus ou moins explicite les différents caches (préchargement, gestion de priorité,...) ont fait leur apparition. L'ensemble de ces mécanismes a rendu le comportement de fractions de code même très simples (boucle avec accès à 2 ou 3 tableaux) extrêmement difficile et par là-même impossible à optimiser en s'appuyant sur des modèles simples de performance. Cette situation n'a fait qu'empirer avec l'écart grandissant entre les performances des processeurs et celles des mémoires. 2865047 5 Globalement, au cours des deux dernières décennies, au niveau de l'architecture des processeurs, la richesse en termes de fonctionnalités s'est considérablement accrue. Ainsi le nombre de registres a considérablement augmenté : de 8 registres en standard sur les architectures CISC, on est passé à 32 registres sur les architectures RISC et même à plus de 80 registres sur les architectures superscalaires. Au premier abord on peut penser qu'augmenter le nombre de registres va simplifier l'optimisation. En pratique, l'évolution des mémoires a rendu l'utilisation des registres encore plus cruciale et sur ce problème d'allocation de registre, le coût des algorithmes efficaces d'allocation de registre a considérablement augmenté car leur complexité est exponentielle en fonction du nombre de registres disponibles. En réponse à ces évolutions récentes, la technologie des compilateurs a peu évolué : le nombre d'optimisations mises en oeuvre a bien sûr augmenté mais la capacité à définir une stratégie globale d'optimisation n'a pas progressé et a même diminué. Enfin, une dernière tendance est apparue avec la compilation "dynamique". Le principe est simple et attrayant: la compilation dynamique (ou encore spécialisation à l'exécution) consiste à effectuer des optimisations de code au dernier moment, c'est-à-dire à l'exécution. Une séquence de code est adaptée ("spécialisée") en fonction des données d'entrée (contexte d'exécution) d'un programme. Un mécanisme à l'exécution "examine" le comportement des programmes suivant la fréquence d'exécution des séquences d'instructions et décide ou non de mettre en oeuvre des versions optimisées pour un contexte d'exécution précis. Ce type de mécanisme intrinsèquement dynamique est limité à des techniques d'optimisation peu coûteuses en temps de calcul pour leur mise en oeuvre car elles ne doivent pas pénaliser l'exécution des codes. On rappelle par ailleurs qu'une bibliothèque est un ensemble de procédures, représentatif d'un domaine ou d'une portion de ce domaine: les composants d'une bibliothèque doivent en fait correspondre à des procédures standard fréquemment utilisées. Le concept de bibliothèque est très ancien, antérieur à la notion de compilateurs et c'est un des grands piliers du génie logiciel (réutilisation de composants). En particulier, Java est l'un des exemples de langages qui utilisent très systématiquement la notion de bibliothèque. - 6 Les bibliothèques peuvent correspondre à différents niveaux d'abstraction du plus simple au plus compliqué : BLAS 1 ("Basic Linear Algebra Subroutines Level I") est un ensemble d'opérations très simples portant sur des vecteurs mais permettant d'exprimer un grand nombre d'algorithmes classiques d'algèbre linéaire. A l'autre extrême, LINPACK (resp. EISPACK) est un ensemble complet de procédures permettant la résolution de systèmes linéaires (resp. le calcul de vecteurs propres et de valeurs propres). Un grand nombre de bibliothèques ont été développées et largement utilisées dans les domaines particuliers suivants: É Calcul scientifique: BLAS1, BLAS2, BLAS3, BLAST, SPARSE BLAS, LINPACK, LAPACK, BLACS, PVM, MPI, etc. É Traitement de signal: FFTPACK, VSIPI, etc. É Graphique: DirectX, OpenGL. Le fait que, dans un domaine d'applications donné, un certain nombre de bibliothèques aient été définies et identifiées traduit le fait que ce domaine puisse être "synthétisé". Le plus gros défaut des bibliothèques est cependant que leur fonctionnalité est très limitée et qu'elles induisent une utilisation manuelle (c.a.d. nécessitent l'insertion dans le code source d'un appel explicite de procédure). Les premières générations de bibliothèques (correspondant à des procédures simples et de petite taille) ont été en général développées à la main en assembleur. Ceci est très souvent resté le cas dès que la performance est le critère majeur (sous réserve bien sûr que les procédures restent d'une taille raisonnable). Par contre, à la différence de la compilation qui est globalement un processus "en ligne" (les temps de compilation doivent rester modérés), l'optimisation de bibliothèques est essentiellement un processus "hors connexion" et peut utiliser des méthodes beaucoup plus gourmandes en temps de calcul. Ainsi la compilation itérative est un excellent outil d'optimisation pour le développement de bibliothèques mais qui ne peut malheureusement être utilisé que pour des codes simples vu sa lourdeur d'utilisation. Dans le même ordre d'idée, la technologie de type "Automatic Tuning Liner Algebra Software" permet de réaliser une partie de l'optimisation (choix des bons paramètres tels que tailles des blocs). 2865047 7 Malheureusement cette technique est d'utilisation très restreinte car elle est très dépendante du type d'application considéré (calcul sur des matrices denses, calculs caractérisés par une très forte localité temporelle). Les outils d'analyse de performance existants sont très variés (en particulier en fonction de l'objectif recherché) : É Tests de performances ( benchmarks ) : ce sont des codes plus ou moins représentatifs d'un domaine d'application et qui permettent de comparer les performances de diverses machines. É Simulateurs: ils permettent d'appréhender le comportement d'une architecture au niveau le plus fin. Malheureusement ils sont très coûteux, délicats à développer, très lents et pas nécessairement fidèles par rapport au processeur cible. É Modèles mathématiques: il s'agit de mettre en équation la performance de la machine. En général leur utilisation est extrêmement limitée et ils ne sont utiles que pour étudier différentes variantes simples autour d'un même code très simple. É Outils de contrôle/gestion de profils: ces outils permettent de récupérer (via des compteurs de matériel spécialisés) différentes informations relatives à l'exécution d'un programme: nombre de cycles, nombre de miss, etc. On peut par ailleurs formuler les remarques suivantes: - Les tests de performance évoluent peu et surtout sont devenus l'objet d'enjeux commerciaux trop importants. Très souvent, leur optimisation acharnée par les constructeurs fausse la portée et la validité des résultats obtenus. - Simulateurs: l'exploitation de leurs résultats (pour l'optimisation de code) devient de plus en plus difficile dans la mesure où l'architecture ciblée est devenue très complexe. - Modèles mathématiques: ils évoluent peu et en dehors de l'usage local mentionné ci-dessus, ils sont inutilisables. Une des raisons en est que les bons outils mathématiques de modélisation présupposent un comportement "uniformément moyenné", ce qui est loin d'être le cas en pratique. - Outils de contrôle/gestion des profils: ils souffrent essentiellement de trois défauts: ils donnent une information globale et non pas une 2865047 8 distribution de l'activité au cours du temps; ils ne permettent pas de corréler code et comportement au niveau fin; enfin, leur exploitation (comme dans le cas du simulateur) est très délicate, en raison notamment de la complexité de l'architecture ciblée. La présente invention vise à remédier aux inconvénients précités et à permettre, pour une plate-forme matérielle prédéfinie comportant au moins un processeur, de générer de façon automatique des codes optimisés opérationnels sur cette plate-forme pour un domaine d'application prédéterminé à partir de codes sources soumis par des utilisateurs. De façon plus particulière, l'invention vise à accroître les performances de systèmes informatiques de façon indépendante du choix du langage source et ceci pour des systèmes mettant en oeuvre des processeurs dont l'architecture peut faire appel à des instructions simples ou complexes et qui peuvent comprendre un nombre plus ou moins important de registres, d'unités fonctionnelles et de niveaux de cache. L'invention a également pour objet d'éviter les limitations de la couverture fonctionnelle des bibliothèques de programmes spécialisées et vise à créer un système de génération automatique de codes optimisés pour un grand nombre de structures de code voisines, qui peuvent présenter différents niveaux de complexité. Ces buts sont atteints, conformément à l'invention, grâce à un système de génération automatique de codes optimisés opérationnels sur une plateforme matérielle prédéfinie comportant au moins un processeur, pour un domaine d'application prédéterminé à partir de codes sources soumis par des utilisateurs, caractérisé en ce qu'il comprend des moyens de réception de séquences de codes symboliques dites séquences-étalons représentatives du comportement dudit processeur en termes de performances, pour le domaine d'application prédéterminé ; des moyens de réception de paramètres statiques définis à partir de la plate-forme matérielle prédéfinie, de son processeur et des séquences-étalons; des moyens de réception de paramètres dynamiques également définis à partir de la plate-forme matérielle prédéfinie, de son processeur et des séquences-étalons; un dispositif d'analyse pour établir des règles d'optimisation à partir de tests et de mesures de performances établis à partir des séquences-étalons, des paramètres statiques et des paramètres 2865047 9 dynamiques; un dispositif d'optimisation et génération de code recevant d'une part les séquences-étalons et d'autre part les règles d'optimisation pour examiner les codes sources d'utilisateurs, détecter des boucles optimisables, décomposer en noyaux, assembler et injecter des codes pour délivrer des codes optimisés; et des moyens pour réinjecter dans les séquences-étalons des informations issues du dispositif de génération et optimisation de codes et relatives à des noyaux. De façon plus particulière, le dispositif d'analyse comprend un générateur de tests relié d'une part aux moyens de réception de séquences- étalons et d'autre part aux moyens de réception de paramètres statiques pour générer automatiquement un nombre élevé de variantes de tests qui sont transférées par des moyens de transfert pour être stockées dans une base des variantes; un exerciseur relié d'une part à des moyens de transfert pour recevoir les variantes de tests stockées dans la base des variantes et d'autre part aux moyens de réception de paramètres dynamiques pour exécuter les variantes de tests dans une plage de variation des paramètres dynamiques et produire des résultats qui sont transférés par des moyens de transfert pour être stockés dans une base des résultats; et un analyseur relié à des moyens de transfert pour recevoir les résultats stockés dans la base des résultats, analyser ceux- ci et en déduire des règles d'optimisation qui sont transférées par des moyens de transfert dans une base de règles d'optimisation. Avantageusement, l'analyseur comprend des moyens de filtrage à un seuil arbitraire de la performance optimale, de manière à considérer une variante de la base des résultats comme optimale dans l'espace des paramètres dès lors qu'elle satisfait aux critères de filtrage. Selon un mode de réalisation préférentiel, l'analyseur comprend en outre des moyens de modification des paramètres statiques et des moyens de modification des paramètres dynamiques. Le dispositif d'optimisation et génération de code comprend un dispositif de génération de code optimisé et un optimiseur, ce dernier comprenant un module de choix de stratégie relié d'une part aux moyens de réception des noyaux identifiés dans les codes sources d'origine, d'autre part aux moyens de réception de séquences-étalons et d'autre part à des moyens de réception de règles d'optimisation pour générer, pour chaque noyau correspondant à une séquence-étalon testée une 2865047 - 10 - pluralité de versions dont chacune est optimale pour une certaine combinaison de paramètres, et un module de combinaison et d'assemblage relié aux moyens de réception de règles d'optimisation, à des moyens de réception d'informations issues du module de choix de stratégie et à des moyens de réception de la pluralité des versions, pour délivrer à travers des moyens de transfert des informations comprenant les versions optimisées correspondantes, leur zone d'utilisation et le cas échéant le test à exécuter pour déterminer dynamiquement la version la plus adaptée. Selon un mode de réalisation optionnel préférentiel, le système comprend une base des noyaux optimisés et le module de combinaison et d'assemblage est relié à la base des noyaux optimisés par des moyens de transfert pour stocker dans cette base des noyaux optimisés les informations comprenant les versions optimisées, leur zone d'utilisation et le cas échéant le test à exécuter pour déterminer dynamiquement la version la plus adaptée. Le dispositif d'optimisation et génération de code comprend un optimiseur et un dispositif de génération de code optimisé, ce dernier comprenant un module de détection de boucles optimisables qui est relié à des moyens de réception des codes sources d'utilisateurs, un module de décomposition en noyaux, un module d'analyse de cas, d'assemblage et d'injection de code qui est relié à travers des moyens de transfert à l'optimiseur pour transmettre l'identification du noyau détecté et des moyens de transfert pour recevoir les informations décrivant le noyau optimisé correspondant, le module d'analyse de cas, d'assemblage et d'injection de code étant en outre relié à des moyens de fourniture des codes optimisés. Le module d'analyse de cas, d'assemblage et d'injection de code peut en outre être relié à la base des noyaux optimisés pour recevoir les informations décrivant un noyau optimisé sans invoquer l'optimiseur si le noyau recherché y a été stocké. Selon une caractéristique avantageuse, le module d'analyse de cas, d'assemblage et d'injection de code comprend en outre des moyens pour rajouter aux séquences-étalons des noyaux qui ont été mis en évidence dans ce module d'analyse de cas, d'assemblage et d'injection de 2865047 - 11 code sans avoir d'identification correspondante dans la base des noyaux optimisés, ni dans les séquences-étalons. Selon un mode de réalisation particulier, le système comprend un compilateur et un éditeur de liens pour recevoir un code source remanié issu du dispositif d'optimisation et génération du code et produire un code binaire optimisé adapté à la plate-forme matérielle. Le système peut comprendre des moyens pour transférer du code source des noyaux optimisés de la base des noyaux optimisés vers le compilateur. Selon une autre variante de réalisation, le système peut comprendre un compilateur et un module d'installation pour installer sur la plate-forme matérielle une bibliothèque dynamique contenant l'ensemble des fonctionnalités des noyaux optimisés. L'invention peut s'appliquer à différents domaines d'application 15 et notamment au calcul scientifique, au traitement de signal et au traitement graphique. Selon une caractéristique particulière, les séquences-étalons comprennent un ensemble de codes de type boucle simples et génériques spécifiés dans un langage de type source et organisés en niveaux de manière hiérarchique par ordre de complexité croissante du code du corps de boucle. Dans ce cas, les séquences-étalons comprennent des séquences-étalons de niveau 0 dans lesquelles une seule opération élémentaire est testée et correspond à un corps de boucle constitué d'une seule expression arithmétique représentée par un arbre de hauteur 0. En supplément, les séquences-étalons peuvent comprendre des séquencesétalons de niveau 1 pour lesquelles sont considérées et testées les combinaisons de deux opérations de niveau 0, les opérations des séquencesétalons de niveau 1 correspondant à des corps de boucles constitués soit d'une seule expression arithmétique représentée par un arbre de hauteur 1, soit de deux expressions arithmétiques, chacune étant représentée par un arbre de hauteur O. Selon un mode de réalisation possible, les séquencesétalons comprennent en outre des séquences-étalons de niveau 2 pour lesquelles sont considérées et testées les combinaisons de deux opérations de niveau 1 ou de trois opérations de niveau 0. 2865047 - 12 - Les paramètres statiques comprennent notamment le nombre d'itérations de la boucle pour chaque séquence-étalon, le pas d'accès aux tableaux et le type d'opérande, le type d'instructions utilisées, les stratégies de préchargement, les stratégies d'ordonnancement des instructions et des itérations Les paramètres dynamiques comprennent notamment la localisation des opérandes tableaux dans les différents niveaux de la hiérarchie mémoire, la position relative des adresses de départ des tableaux et l'historique des branchements. Avantageusement, la base des noyaux optimisés comprend des séquences de code source de type boucle correspondant à des fragments de code réel et utile et organisés en niveaux par ordre de complexité croissante. La plate-forme matérielle prédéfinie peut comporter par exemple au moins un processeur du type dénommé ItaniumTM de la société INTEL ou au moins un processeur du type Power ou Power PCTM de la société IBM. Selon un mode de réalisation possible applicable plus particulièrement aux systèmes comportant un processeur de type ITANIUMTM, les règles d'optimisation comprennent au moins certaines des règles suivantes: (a) minimiser le nombre d'Ecritures en cas de mauvaise performance des Ecritures par rapport aux Lectures, (b) importance de l'utilisation des paires de chargement en virgule flottante, (c) ajuster le degré de déroulage en fonction de la complexité du corps de boucle, (d) utiliser les latences opérationnelles des opérations arithmétiques, (e) appliquer des techniques de masquage pour les vecteurs courts, (f) utiliser des suffixes de localité pour les accès mémoire (Lecture, Ecriture, Préchargement), (g) définir les distances de Préchargement, 2865047 - 13 (h) effectuer une vectorisation de degré 4 pour éviter une partie des conflits de bancs L2, (i) prendre en compte de multiples variantes pour éviter une autre partie des conflits de bancs L2 et les conflits dans la file d'attente des Lectures/Ecritures, (i) prendre en compte les gains deperformances liés aux différentes optimisations, (k) utiliser des chaînes de branchement minimisant les mauvaises prédictions (vecteurs courts), (I) fusionner les accès mémoires (regroupement de pixels), (m) vectoriser les traitements sur pixels. Selon un autre mode de réalisation possible applicable plus particulièrement aux systèmes comportant un processus de type Power ou Power PC-r', les règles d'optimisation comprennent au moins certaines des règles suivantes: (a) réordonnancer les Lectures pour regrouper les défauts de caches, (b) utiliser le préchargement uniquement sur les Ecritures, (c) ajuster le degré de déroulage en fonction de la complexité du corps de boucle, (d) utiliser les latences opérationnelles des opérations arithmétiques, (e) utiliser des suffixes de localité pour les accès mémoire (Lecture, Ecriture, Préchargement), (f) définir les distances de Préchargement, (g) prendre en compte de multiples variantes pour éviter les conflits dans les files d'attente Lecture/Ecriture, (h) prendre en compte les gains de performances liés aux différentes optimisations. D'autres caractéristiques et avantages de l'invention ressortiront de la description suivante de modes particuliers de réalisation, faite en référence aux dessins annexés, sur lesquels: 2865047 - 14 - - la Figure 1 est un schéma-bloc montrant l'ensemble des modules d'un système de génération automatique de codes optimisés selon l'invention, - la Figure 2 est un schéma-bloc montrant de façon plus détaillée la 5 constitution d'un module d'analyse de performances pouvant être mis en oeuvre dans le système de la Figure 1, - la Figure 3 est un schéma-bloc montrant de façon plus détaillée la constitution d'un module d'optimisation et de génération de code pouvant être mis en oeuvre dans le système de la Figure 1, - la Figure 4 est un schéma bloc montrant un premier exemple de module de génération de code source remanié, associé à des moyens d'obtention de code binaire optimisé pour la plate-forme cible concernée, et - la Figure 5 est un schéma bloc montrant un deuxième exemple de module de génération de code source remanié, associé à des moyens d'obtention de code binaire optimisé pour la plate-forme cible concernée. On se réfèrera tout d'abord à la Figure 1 qui montre l'ensemble du système de génération automatique de code optimisé pour fournir, sur une sortie 73 d'un module 80 d'optimisation et de génération de code, des codes optimisés opérationnels sur une plate-forme matérielle 90 prédéfinie comportant au moins un processeur 91. Le système de génération de code optimisé est adapté à un domaine d'application déterminé et reçoit, par une entrée 71 du module 80, des codes sources 17 soumis par des utilisateurs, le terme utilisateurs devant s'entendre au sens large et incluant notamment les utilisateurs finaux, les programmeurs d'applications ou les programmeurs systèmes. Des séquences de code symboliques dites séquences-étalons 1 représentatives du comportement du processeur 91 en termes de performances pour le domaine d'application considéré, sont appliquées sur une entrée 52 du module 80 d'optimisation et de génération de code et sur une entrée 51 d'un module d'analyse 10. L'analyse de l'effet des divers paramètres de l'environnement et de l'interaction entre séquences-étalons permet de situer les zones de bonnes et mauvaises performances et d'en appréhender les raisons. Les séquences-étalons ne représentent pas forcément des séquences de code réel généré par les langages de programmation classiques. Seul un sous- 2865047 - 15 - ensemble des séquences-étalons testées correspond à des noyaux utilisés pour l'optimisation du code utilisateur. Une boucle optimisable est une construction de programme codant la représentation algorithmique d'une opération plus ou moins complexe 5 sur des variables-vecteurs. Un noyau ou boucle élémentaire constitue une forme simple de boucle optimisable. Le module 80 du système selon l'invention permet de générer automatiquement un nombre de noyaux optimisés sensiblement supérieur au. nombre de fonctions offertes par les bibliothèques spécialisées mathématiques. En général, plusieurs versions d'un même noyau peuvent être générées, chaque version étant optimisée pour une certaine combinaison de paramètres d'environnement. La phase d'optimisation dans un optimiseur 12 (Figure 3) consiste ainsi en la génération automatique d'un ensemble ou bibliothèque de noyaux optimisés pour la plate-forme cible 90 représentant des fonctionnalités représentatives du domaine d'application. La phase d'optimisation est associée à une phase de génération de code dans un générateur de code 18 (Figure 3) qui examine le code source du programme utilisateur pour y détecter les boucles optimisables afin de forcer l'utilisation de noyaux optimisés en lieu et place du code qu'aurait généré le compilateur standard. Des moyens 74 sont prévus pour réinjecter dans les séquences-étalons 1 les informations issues du module 80. Les phases d'optimisation et de génération de code sont précédées par une phase d'analyse dans un module d'analyse 10 qui sert à déterminer, pour la plate-forme matérielle cible 90 et le domaine d'application considéré, les règles d'optimisation à respecter pour obtenir des performances optimales. Une sortie 57 du module d'analyse 10 permet le transfert des règles d'optimisation vers une base 9 de règles d'optimisation elle-même reliée, par des moyens de transfert 18, à l'optimiseur 12 du module 80. On décrira maintenant de façon plus particulière le module d'analyse 10 en référence à la Figure 2. Des paramètres statiques 2 et des paramètres dynamiques 7, 35 identifiés en fonction de l'architecture du processeur 91, et plus généralement du système qui est à la base de la plate-forme 90 cible de 2865047 - 16 l'optimisation, et également en fonction des séquences-étalons, sont appliqués par des moyens 53, 54 au module d'analyse 10. Les paramètres statiques 2 peuvent comprendre notamment le nombre d'itérations de la boucle pour chaque séquence-étalon, le pas d'accès aux tableaux et le type d'opérande, le type d'instructions utilisées, les stratégies de préchargement, les stratégies d'ordonnancement des instructions et des itérations. Les paramètres dynamiques 7 peuvent comprendre notamment la localisation des opérandes tableaux dans les différents niveaux de la hiérarchie mémoire, la position relative des adresses de départ des tableaux et l'historique des branchements. Dans le module 10 d'analyse de performances, un générateur de test 3 exploite les données relatives aux paramètres statiques 2 et dynamiques 7, qui lui sont fournies par les entrées 51, 53 pour générer un nombre potentiellement très élevé de variantes qui sont transférées par des moyens de transfert 61 vers une base de données des variantes 4. Un autre outil automatique 5 dénommé exerciseur reçoit, par des moyens de transfert 62, les données des variantes et la base des variantes 4, met en oeuvre les tests ainsi fabriqués, les exécute en faisant varier dans leur plage de variation les paramètres dynamiques 7 fournis par les moyens de transfert 55 et transfère, par des moyens de transfert 63, les mesures pertinentes vers une autre base de données 6 dénommée base des résultats. Les mesures stockées dans la base des résultats 6 sont elles- mêmes transférées, par des moyens de transfert 64, vers un analyseur 8 qui, à partir de l'identification des zones de bonne et mauvaise performance en fonction du paramètrage, permet la formulation des règles d'optimisation 9 qui sont transférées par les moyens de transfert 57 dans la base 9 de règles d'optimisation. L'analyseur 8 comprend également des moyens 54 de modification des paramètres statiques 2 et des moyens 56 de modification des paramètres dynamiques, par exemple s'il a été constaté une faible sensibilité aux variations d'un paramètre donné. L'analyseur 8 peut comprendre des moyens de filtrage à un seuil arbitraire de la performance optimale. Dans ce cas, une variante de la base des résultats qui ne correspond pas à des performances optimales 2865047 - 17 - peut néanmoins être retenue comme optimale dans l'espace des paramètres, dès lors qu'elle satisfait aux critères de filtrage. On décrira maintenant le module 80 d'optimisation et de génération de code en référence à la Figure 3. Le dispositif d'optimisation 12 comprend un module 13 de choix de stratégie relié au module 18 de génération de code par des moyens 92 de réception des noyaux identifiés dans les codes sources d'origine. Le module 13 de choix de stratégie est en outre relié à des moyens 52 de réception de séquences-étalons 1 et à des moyens 58 de réception de règles d'optimisation 9. Le module 13 de choix de stratégie génère à sa sortie 67, pour chaque noyau correspondant à une séquence-étalon testée, un ensemble 15 de n versions dont chacune est optimale pour une certaine combinaison de paramètres. Un module 14 de combinaison et d'assemblage de versions est relié aux moyens 59 de réception de règles d'optimisation 9, à des moyens 66 de réception d'informations issues du module de choix de stratégie 13 et à des moyens 68 de réception de la pluralité 15 de versions 1 à n. Le module 14 délivre à travers des moyens de transfert 93 des informations comprenant les versions optimisées correspondantes, leur zone d'utilisation et le cas échéant le test à exécuter pour déterminer dynamiquement la version la plus adaptée. Le module 18 de génération de code optimisé comprend un module 20 de détection de boucles optimisables qui est relié à des moyens 71 de réception des codes sources 17 d'utilisateurs. La sortie 75 du module 20 est reliée à un module 22 de décomposition en noyaux dont la sortie 77 est elle-même reliée à un module 23 d'analyse de cas, d'assemblage et d'injection de code qui est relié, à travers des moyens de transfert 92, à l'optimiseur 12 pour transmettre l'identification du noyau détecté. Le module 23 reçoit par ailleurs, par des moyens de transfert 93, les informations décrivant le noyau optimisé correspondant. Le module 23 est aussi relié à des moyens 73 de fourniture des codes optimisés 19. Selon une variante de réalisation avantageuse, le module 80 d'optimisation et de génération de code comprend une base des noyaux optimisés 16. Le module de combinaison et d'assemblage 14 est relié à la base des noyaux optimisés 14 par des moyens de transfert 79 pour stocker dans cette base des noyaux optimisés les informations comprenant 2865047 - 18 - les versions optimisées, leur zone d'utilisation et le cas échéant le test à exécuter pour déterminer dynamiquement la version la plus adaptée. Selon cette variante, le module 23 d'analyse de cas, d'assemblage et d'injection de code est en outre relié à la base 16 des noyaux optimisés par les moyens de transfert 72 pour recevoir les informations décrivant un noyau optimisé, sans invoquer l'optimiseur 12, si le noyau recherché a déjà été stocké dans cette base 16. Comme on peut le voir sur la Figure 3, le module 23 d'analyse de cas, d'assemblage et d'injection de code comprend en outre des moyens 74 pour rajouter aux séquences-étalons 1 des noyaux qui ont été mis en évidence dans ce module 23 sans avoir l'identification correspondante dans la base 16 des noyaux optimisés, ni dans les séquences-étalons. La Figure 4 montre un exemple particulier de réalisation pour lequel on n'a pas représenté l'optimiseur 12 qui reste identique à la variante de la Figure 3 selon laquelle il existe une base 16 des noyaux optimisés. Dans cet exemple de réalisation, le module 18 de génération de code produit en sortie 73 du module 23 d'analyse de cas, d'assemblage et d'injection de code, un code source remanié 19 qui est ensuite traité par des outils classiques de préparation de programme 81, 82 pour l'obtention de code binaire 83 optimisé pour la plate-forme cible 90. La Figure 4 représente un exemple de réalisation qui peut être très facilement mis en oeuvre. Le code source utilisateur d'origine 17 a été remanié au sein du module 80 d'optimisation et de génération de code déjà décrit plus haut, de telle manière que les boucles optimisables soient remplacées par des appels à des sous-programmes, le code correspondant à ces sous-programmes étant injecté dans le code source remanié 19 depuis la base des noyaux optimisés 16. Le code source 19 ainsi remanié contient alors tout le nécessaire pour générer un code binaire optimisé 83 adapté à la plate-forme matérielle 90, par un passage dans une chaîne classique comprenant un compilateur 81 et un éditeur de liens 82. Selon une variante possible, du code source des noyaux optimisés de la base des noyaux optimisés 16 peut être utilisé directement dans l'étape de compilation en tant que bibliothèque source d'appoint. Ceci est symbolisé sur la Figure 4 par la flèche en pointillés 85 qui relie la base des noyaux optimisés 16 au compilateur 81. Cette variante permet ainsi 2865047 -19- d'éviter l'injection directe de ce code source des noyaux optimisés dans le code source remanié et allège l'étape de génération au sein du module 18. La Figure 5 représente une autre variante de réalisation de l'exemple de réalisation de la Figure 4. La variante de la Figure 5 exploite la possibilité qu'offrent certains systèmes d'exploitation d'installer des bibliothèques sous forme de codes binaires exécutables et accessibles par des programmes par édition de liens dynamiques au moment de leur exécution. Dans le cas de la variante de la Figure 5, il n'est pas non plus nécessaire d'injecter de code depuis la base des noyaux optimisés 16 vers le code source remanié 19. En revanche, une bibliothèque dynamique contenant l'ensemble des fonctionnalités des noyaux optimisés doit être installée sur la plate-forme cible 90 par l'intermédiaire d'un compilateur 181 et d'un module d'installation 182. On peut utiliser un seul compilateur commun pour les compilateurs 81 et 181 de la Figure 5. Dans cette variante de la Figure 5, l'opération d'installation n'est nécessaire qu'une seule fois pour chaque plate-forme cible, de sorte que cette variante est la plus économe en termes de traitement global du processus d'optimisation. La mise en oeuvre d'un système de génération de code optimisé selon l'invention s'applique particulièrement bien aux trois domaines que sont le calcul scientifique, le traitement du signal et le traitement graphique. En effet, les codes utilisés dans ces trois domaines présentent plusieurs caractéristiques CARI à CAR4 qui sont importantes pour cette 25 mise en oeuvre. o CAR 1: Les structures de type boucle (ou nids de boucle ) constituent les portions de code les plus consommatrices en termes de temps d'exécution o CAR 2: Les structures de données utilisées sont en majeure partie de type tableau multidimensionnel et sont accédées suivant des motifs très réguliers (lignes, colonnes, blocs, etc.) o CAR 3: Les boucles (ou nids de boucles) sont en général constituées d'itérations indépendantes et parallélisables 2865047 - 20 - o CAR 4: Le corps de la boucle est en général constitué d'une séquence d'expressions arithmétiques et correspond à un calcul uniforme (ou quasi- uniforme) sur un grand volume de données. Naturellement, si ces trois domaines du calcul scientifique, du traitement de signal et du traitement graphique possèdent des points communs, ils présentent aussi certaines différences fortes. Ainsi dans le domaine du traitement de signal, les données de type complexe constituent un type de données extrêmement important qui requiert des optimisations spécifiques tandis que l'importance de ce type de données est marginale dans les deux autres domaines. Le traitement graphique quant à lui est fortement marqué par l'utilisation d'un type de données particulier, les pixels, et d'une arithmétique spéciale. De plus en graphique, la structuration des données et des algorithmes par rapport à un écran bidimensionnel est fondamentale. Les quatre caractéristiques ci-dessus (CARI à CAR4) ont des conséquences très fortes pour l'optimisation de code et permettent le développement de techniques complètement spécifiques: o CAR 1 ' > les optimisations vont en fait se concentrer sur les structures de type boucle qui présentent deux avantages majeurs: répétitivité (et prédictibilité) et compacité de représentation. o CAR 2 ' > les accès aux tableaux qui représentent une part importante (voire majeure en raison de l'utilisation croissante des 25 caches) des temps d'exécution vont pouvoir facilement être analysés et optimisés en raison de leur régularité. o CAR 3 ' > l'indépendance des itérations au sein d'une boucle et de nids de boucles, va permettre l'utilisation (l'optimisation) de parcours de l'espace d'itération en fonction des accès aux tableaux et ce suivant les caractéristiques propres de l'architecture cible. On peut remarquer qu'accéder à N éléments donnés d'un tableau peut être réalisé de N! manières (ordres) différentes. 2865047 - 21 - o CAR 4 ' > la structure simple du corps de boucles en termes d'expressions arithmétiques va nous permettre d'utiliser une approche systématique et hiérarchique s'appuyant sur la représentation sous forme d'arbres d'une expression arithmétique. La phase d'analyse reste une phase essentiellement expérimentale à l'issue de laquelle il faut: É avoir déterminé les points forts et les points faibles de l'architecture, É savoir corréler performance et structure de code, É avoir identifié les bonnes stratégies d'optimisation, celles ci pouvant être fonction de différents paramètres liés au code. Comme on l'a déjà indiqué, le point de départ est un ensemble de codes de type source simples mais génériques appelés séquences-étalons . Ces codes ont une structure de type boucle, le terme type source indiquant que les opérations sont spécifiées à haut niveau et non au niveau assembleur. Ces codes sont organisés en niveaux de manière hiérarchique par 20 ordre de complexité croissante du code du corps de boucle de la manière suivante: É SEQUENCE-ETALON NIVEAU 0: à ce niveau, une seule opération élémentaire est testée, c'est-à-dire que le corps de boucle comporte une seule opération: lecture d'un élément de tableau, écriture d'un élément de tableau, addition flottante, etc. Ces opérations correspondent à des corps de boucle constitué d'une seule expression arithmétique représentée par un arbre de hauteur 0. É SEQUENCE-ETALON NIVEAU 1: à ce niveau, sont considérées et testées les combinaisons de deux opérations de niveau 0: lecture et écriture d'un tableau, lecture de deux tableaux différents, lecture et addition sur un tableau etc. Ces opérations correspondent à des corps de boucles constitués soit d'une seule expression 2865047 - 22 - arithmétique représentée par un arbre de hauteur 1, soit deux expressions arithmétiques, chacune étant représentée par un arbre de hauteur O. É SEQUENCE-ETALON NIVEAU 2: à ce niveau les combinaisons de deux opérations de niveau 1 ou de trois opérations de niveau 0 sont considérées et testées: lecture de trois tableaux différents, lecture et addition de deux tableaux composante à composante et écriture du résultat dans un troisième tableau etc. É SEQUENCE-ETALON NIVEAU K: le niveau K peut être aisément défini par récurrence à partir des niveaux précédents. Toutes les séquences-étalons de niveau 0 correspondent à des codes qui sont artificiels , i.e. ne traduisent pas des boucles réelles . Cette organisation en niveaux de complexité croissante est aussi utilisée dans la phase d'optimisation. L'ensemble des séquences-étalons ainsi défini est infini. Ces séquences-étalons utilisent deux classes de paramètres différents: É Paramètres statiques: ces paramètres sont définis de manière statique (c'est-à-dire avant exécution et indépendamment de l'exécution). Ces paramètres statiques sont eux mêmes subdivisés en deux grandes sous classes: paramètres statiques de haut niveau (nombre d'itérations de la boucle, pas d'accès aux tableaux, type d'opérande,...), paramètres statiques de bas niveau (utilisation d'instructions spécifiques, ordonnancement des instructions etc.) É Paramètres dynamiques: ces paramètres sont définis lors de l'exécution de la boucle. Ils comprennent par exemple: localisation des opérandes tableaux dans la hiérarchie mémoire, position relative des adresses de départ des tableaux,... Ces deux classes de paramètres sont exploitées de manière très différente: les paramètres statiques seront utilisés pour générer les 2865047 - 23 différents codes tests en combinaison avec les variantes/optimisations décrites ci-dessous, tandis que les paramètres dynamiques sont exclusivement utilisés lors de l'exécution sur le banc de test. Les paramètres statiques de haut niveau sont relativement limités et correspondent essentiellement aux paramètres classiques d'une boucle et de tableaux exprimés dans un langage haut niveau (Fortran ou C par exemple) sans aucune spécificité provenant du processeur cible. Les paramètres statiques de bas niveau vont permettre de prendre en compte toutes les spécificités liées au processeur (architecture) et à l'ordonnancement des instructions (générateur de code objet). En effet, les séquences-étalons sont de fait des abstractions à haut niveau (définies dans un langage source, indépendantes de l'architecture du processeur visé) et n'intègrent en particulier aucune optimisation. Pour les tester sur un processeur donné, il faut générer et optimiser les codes assembleurs correspondants. Lors de cette génération, plusieurs variantes (séquence d'instructions assembleurs) vont être générées automatiquement. Toutes les variantes associées à une même séquence- étalon sont des codes sémantiquement équivalents à la séquence-étalon de départ. Ce sont ces variantes qui vont être exécutées et mesurées. Ces variantes correspondent en fait à différentes techniques d'optimisation de code (c'est-à-dire à des paramètres statiques de bas niveau). Ces optimisations peuvent être définies de manière abstraite sans référence à la structure particulière d'une séquence-étalon et constituent l'essentiel des paramètres statiques de bas niveau. Les paramètres statiques de bas niveau comportent: É l'utilisation des instructions assembleurs: une même opération au niveau source peut être mise en oeuvre par différentes séquences d'instructions. En particulier, il faut traiter ici les différentes stratégies possibles pour utiliser le pré-chargement des données et des instructions. 2865047 - 24 - É la structure du corps de boucle: déroulage (degré variable) du corps de boucle, É l'ordonnancement du corps de boucle: ordonnancement des instructions du corps de boucle (distances de préchargement, vectorisation), regroupement des défaut de cache, traitement des conflits dans le files d'attente), É l'ordonnancement des itérations: pipeline logiciel (profondeur variable) Dans nombre de compilateurs, les paramètres statiques de bas niveau décrits ci-dessus correspondent à des options de compilation permettant de mettre en oeuvre de manière explicite l'optimisation visée. Le rôle du générateur de test 3 est de générer les différentes variantes décrites ci-dessus correspondant d'une part aux paramètres statiques de haut niveau (par exemple, pas d'accès aux tableaux) et d'autre part aux paramètres statiques de bas niveau. Il faut noter que pour des séquences-étalons de niveau 1, le nombre total de variantes à générer/analyser est extrêmement élevé et se chiffre à plusieurs millions. Malgré cela, le processus de génération et d'analyse peut être très simplement automatisé. Au niveau de l'exerciseur 5 et de l'analyseur 8, il s'agit de tester les performances des différentes variantes et de sélectionner les meilleures variantes/optimisations possibles. Cette phase implique la génération d'un grand nombre de résultats stockés dans la base de résultats 6. Les expérimentations sont effectuées de manière hiérarchique et entrelacée avec les phases d'analyse: ainsi les premières expérimentations sont effectuées sur les variantes des séquences-étalons de niveau O. A l'issue de cette première campagne d'expérimentation, un premier tri sur les différentes variantes peut être effectué en fonction des résultats obtenus. Certaines variantes peuvent ainsi être tout de suite éliminées et ne seront plus considérées pour les 2865047 - 25 - niveaux suivants. Ceci permet de limiter l'explosion combinatoire du nombre d'expériences à réaliser. La phase d'analyse des résultats est à première vue assez simple à effectuer car une seule métrique (performance) est utilisée. En fait une grande partie de la complexité du processus provient du fait qu'en général, le choix des meilleures variantes va très fortement dépendre des paramètres. Un premier tri peut être réalisé de manière très simple en calculant d'après les spécifications de l'architecture, les performances optimales pour chacune des séquences-étalons. Par contre, des difficultés peuvent apparaître rapidement, liées aux interactions complexes entre architecture et codes (y compris pour des codes aussi simples que les séquences-étalons de niveau 0 et 1): ceci se traduit par des figures compliquées décrivant les variations de la performance en fonction des paramètres. Ces comportements complexes peuvent être d'abord analysés en utilisant des algorithmes de traitement d'image puis synthétisés en qualifiant une variante donnée pour une certaine plage de paramètre. Ainsi la phase d'analyse ne génère pas simplement une liste indiquant pour chaque séquence-étalon la meilleure (et unique) variante/technique d'optimisation: de fait pour chaque séquence-étalon, une liste de plages de paramètres est déterminée et, pour chacune de ces plages, la meilleure variante/technique d'optimisation est indiquée: c'est une telle information qui sera appelée règle d'optimisation . L'ensemble des séquences-étalons testées est un sous-ensemble très restreint de l'ensemble des séquences-étalons. Cet ensemble qui est utilisé par la suite pour les optimisations est appelé ensemble des séquences-étalons de référence . En pratique, il est très important de fixer un objectif raisonnable d'optimisation: ainsi chercher à tout prix l'optimum peut générer un nombre très élevé de variantes alors qu'en relâchant la contrainte d'optimum et en se contentant de performances dans un voisinage de 5 à 10% de l'optimum, une seule variante peut être utilisée 2865047 - 26 - pour une grande plage de paramètres. On procède pour cela à un filtrage par exemple à un seuil de 90% de la performance optimale. En pratique, il suffit de tester et d'analyser seulement les niveaux 0, 1 et 2 des séquences-étalons pour dégager et valider les principales techniques d'optimisation. L'ensemble de référence des séquences-étalons ne contiendra pas en général de séquence de niveau supérieur à 3. En effet, le volume d'expérimentations devient rapidement très élevé en particulier au-delà du niveau 2. L'ensemble des expérimentations se prête de manière idéale à la parallélisation: les tests peuvent être exécutées en parallèle sur 100 ou 1000 machines. Cette propriété de parallélisation est extrêmement utile et permet d'effectuer des explorations systématiques et ceci dans des temps acceptables. Cette phase est entièrement automatisable et les procédures de vérification de qualité et de cohérence des résultats peuvent être aussi automatisées. L'intervention humaine n'est requise que pour le relevé d'erreurs/anomalies découlant de l'analyse des résultats produits automatiquement par les procédures de vérification de qualité et de cohérence. A l'issue de la phase d'analyse, l'objectif est de disposer d'un très grand nombre de codes simples dits noyaux spécifiquement optimisés pour l'architecture cible, ce processus d'optimisation s'appuyant de manière essentielle sur les techniques d'optimisation dégagées à l'issue de la phase d'analyse. De manière stricte, les noyaux sont des séquences de code source de type boucle et constituent un sous-ensemble du cas général des séquencesétalons. A la différence de ces dernières, ils correspondent à des fragments de code réel et utile. Comme les séquences-étalons, ils sont organisés en niveaux par ordre de complexité croissante. La génération/optimisation de ces noyaux se déroule suivant les quatre phases ci dessous: 2865047 - 27 - o Corrélation avec une ou plusieurs séquences-étalons de référence: pour les noyaux les plus simples, il y a une correspondance directe entre noyaux et séquences-étalons, pour les noyaux plus complexes, le noyau sera décomposé en plusieurs séquencesétalons de référence. Cette corrélation/décomposition est effectuée au niveau source en fonction des caractéristiques du corps de boucle du noyau: nombre de tableaux, pas d'accès aux tableaux, etc.. o Génération de code/Ordonnancement d'instructions/optimisation d'instructions: il s'agit d'utiliser les techniques d'optimisation détectées lors de la phase d'analyse (en fonction des séquences-étalons correspondantes) et de les appliquer directement à la génération / optimisation de code pour les noyaux. Pour un même noyau, plusieurs versions possibles pourront être générées et ce en fonction des paramètres. o Allocations de registre: bon nombre des techniques d'optimisation utilisées accroissent de manière sensible la pression sur les registres disponibles. Dans ce cas, il convient d'aménager l'allocation de l'ensemble des registres disponibles. o Expérimentation/Validation: le noyau généré et optimisé est testé en utilisant le banc de test de la phase d'analyse. A l'issue de cette phase, un modèle simple des performances du noyau est construit. Par rapport aux optimisations classiques utilisées dans un compilateur, les optimisations utilisées ici sont très différentes: tout d'abord elles sont dérivées directement d'un processus détaillé d'évaluation de performances (effectué lors de la phase d'analyse) ensuite elles sont beaucoup plus complexes et performantes (en particulier pour l'allocation de registres) car elles sont exécutées hors connexion sans contrainte de temps. L'utilisation des séquences-étalons de référence et des règles de génération permettent de prendre en compte d'une part toutes les caractéristiques fines de l'architecture (caractéristiques opérationnelles 2865047 - 28 - mesurées et non théoriques) et d'autre part les différentes versions devant être sélectionnées en fonction des paramètres. A l'issue de cette phase, il a été construit une base de données 16 des noyaux optimisés, contenant non seulement les codes générés mais aussi les informations relatives aux performances et ceci en fonction des différents paramètres. Chacun de ces noyaux est d'ailleurs testé suivant la procédure utilisée pour les séquences-étalons. En pratique, la base 16 des noyaux optimisés comprend de manière systématique et exhaustive tous les noyaux de niveaux 1, 2, 3, 4 et 5. Le coût en termes de volume de calcul de la construction de cette base de données est important mais comme pour la phase analyse de performances, elle se parallélise très efficacement. L'optimisation du code utilisateur se déroule en trois phases: o Détection des boucles optimisables (module 20) : il s'agit de reconnaître au sein du code source les boucles pouvant être décomposées en noyaux. Cette phase utilise des techniques très proches de celle des paralléliseurs/vectoriseurs automatiques. Au besoin, le code source est restructuré pour faire apparaître la boucle sous la forme la plus favorable à l'optimisation o Analyse de la boucle optimisable, décomposition en noyaux (module 22) : en s'appuyant sur des techniques d'appariement de configuration et de décomposition proches de celles utilisées pour l'optimisation des noyaux, la boucle est décomposée en une séquence de noyaux o Assemblage et injection de code (module 23) : les différents noyaux utilisés pour la décomposition sont assemblés et réinjectés dans le code source. La procédure de décomposition est en général paramétrée en fonction de caractéristiques de la boucle source d'origine. Les optimisations proposées peuvent être intégrées 2865047 - 29 - o soit par un pré-processeur dans un chaîne de compilation existante et ceci de manière transparente c'est-à-dire sans qu'il soit nécessaire d'avoir accès au code du compilateur, o soit directement dans un compilateur, ceci nécessitant bien sûr des modifications dans le code du compilateur. Comme on l'a déjà indiqué plus haut en référence à la Figure 3, à l'issue de la phase d'analyse un certain nombre de règles d'optimisation sont disponibles: ces règles sont fonction de la séquence-étalon et de la plage de paramètre. Au lieu de passer par l'étape intermédiaire des noyaux, une variante possible est de corréler directement la boucle optimisable avec les séquences-étalons et d'appliquer directement les règles d'optimisation sur la boucle optimisable sans passer par le stockage de noyaux dans une base 16 des noyaux optimisés. Cette variante est plus simple que le passage par les noyaux et elle permet une utilisation plus souple des règles d'optimisation. En revanche, du fait qu'elle est réalisée essentiellement en ligne, le nombre de variantes explorées sera nécessairement plus restreint et donc les performances obtenues seront a priori un peu moins bonnes. A l'issue de la phase d'optimisation, le système a généré des formes optimisées pour un certain nombre de boucles optimisables qui a priori n'étaient pas directement disponibles dans la base des noyaux car elles avaient nécessité une décomposition. Ces formes optimisées peuvent être stockées dans la base 16 des noyaux optimisés et réutilisées par la suite. Ainsi, la base de données 16 des noyaux est enrichie automatiquement par cette forme d'apprentissage. - 30 - REVENDICATIONS 1. Système de génération automatique de codes optimisés (19) opérationnels sur une plate-forme matérielle prédéfinie (90) comportant au moins un processeur (91), pour un domaine d'application prédéterminé à partir de codes sources (17) soumis par des utilisateurs, caractérisé en ce qu'il comprend des moyens (51, 52) de réception de séquences de codes symboliques dites séquences-étalons (1) représentatives du comportement dudit processeur (91) en termes de performances, pour le domaine d'application prédéterminé ; des moyens (53) de réception de paramètres statiques (2) définis à partir de la plate-forme matérielle prédéfinie (90), de son processeur (91) et des séquences-étalons (1) ; des moyens (55) de réception de paramètres dynamiques (7) également définis à partir de la plate-forme matérielle prédéfinie (90), de son processeur (91) et des séquences-étalons (1) ; un dispositif d'analyse (10) pour établir des règles d'optimisation (9) à partir de tests et de mesures de performances établis à partir des séquences-étalons (1), des paramètres statiques (2) et des paramètres dynamiques (7) ; un dispositif (80) d'optimisation et génération de code recevant d'une part les séquences-étalons (1) et d'autre part les règles d'optimisation (9) pour examiner les codes sources (17) d'utilisateurs, détecter des boucles optimisables, décomposer en noyaux, assembler et injecter des codes pour délivrer des codes optimisés (19) ; et des moyens (74) pour réinjecter dans les séquences-étalons (1) des informations issues du dispositif (80) de génération et optimisation de codes et relatives à des noyaux. 2. Système selon la revendication 1, caractérisé en ce que le dispositif d'analyse (10) comprend un générateur de tests (3) relié d'une part aux moyens (51) de réception de séquences-étalons et d'autre part aux moyens (53) de réception de paramètres statiques pour générer automatiquement un nombre élevé de variantes de tests qui sont transférées par des moyens de transfert (61) pour être stockées dans une base des variantes (4) ; un exerciseur (5) relié d'une part à des moyens de transfert (62) pour recevoir les variantes de tests stockées dans la base des variantes (4) et d'autre part aux moyens (55) de réception de paramètres dynamiques pour exécuter les variantes de tests dans une 2865047 -31 - plage de variation des paramètres dynamiques (7) et produire des résultats qui sont transférés par des moyens de transfert (63) pour être stockés dans une base des résultats (6) ; et un analyseur (8) relié à des moyens de transfert (64) pour recevoir les résultats stockés dans la base des résultats (6), analyser ceux-ci et en déduire des règles d'optimisation qui sont transférées par des moyens de transfert (57) dans une base de règles d'optimisation (9). 3. Système selon la revendication 2, caractérisé en ce que l'analyseur (8) comprend des moyens de filtrage à un seuil arbitraire de la performance optimale, de manière à considérer une variante de la base des résultats comme optimale dans l'espace des paramètres dès lors qu'elle satisfait aux critères de filtrage. 4. Système selon la revendication 2 ou la revendication 3, caractérisé en ce que l'analyseur (8) comprend en outre des moyens (54) de modification des paramètres statiques (2) et des moyens (56) de modification des paramètres dynamiques (7). 5. Système selon l'une quelconque des revendications 1 à 4, caractérisé en ce que le dispositif (80) d'optimisation et génération de code comprend un dispositif (18) de génération de code optimisé et un optimiseur (12), ce dernier comprenant un module de choix de stratégie (13) relié d'une part aux moyens (92) de réception des noyaux identifiés dans les codes sources d'origine, d'autre part aux moyens (52) de réception de séquences-étalons (1) et d'autre part à des moyens (58) de réception de règles d'optimisation (9) pour générer, pour chaque noyau correspondant à une séquence-étalon testée une pluralité de versions (15) dont chacune est optimale pour une certaine combinaison de paramètres, et un module de combinaison et d'assemblage (14) relié aux moyens (59) de réception de règles d'optimisation (9), à des moyens (66) de réception d'informations issues du module de choix de stratégie (13) et à des moyens (68) de réception de la pluralité des versions (15), pour délivrer à travers des moyens de transfert (93) des informations comprenant les versions optimisées correspondantes, leur zone d'utilisation et le cas échéant le test à exécuter pour déterminer dynamiquement la version la plus adaptée. 6. Système selon la revendication 5, caractérisé en ce qu'il comprend une base des noyaux optimisés (16) et en ce que le module de 2865047 - 32 combinaison et d'assemblage (14) est relié à la base des noyaux optimisés (16) par des moyens de transfert (79) pour stocker dans cette base des noyaux optimisés les informations comprenant les versions optimisées, leur zone d'utilisation et le cas échéant le test à exécuter pour déterminer dynamiquement la version la plus adaptée. 7. Système selon l'une quelconque des revendications 1 à 6, caractérisé en ce que le dispositif (80) d'optimisation et génération de code comprend un optimiseur (12) et un dispositif (18) de génération de code optimisé, ce dernier comprenant un module (20) de détection de boucles optimisables qui est relié à des moyens (71) de réception des codes sources (17) d'utilisateurs, un module (22) de décomposition en noyaux, un module (23) d'analyse de cas, d'assemblage et d'injection de code qui est relié à travers des moyens de transfert (92) à l'optimiseur (12) pour transmettre l'identification du noyau détecté et des moyens de transfert (93) pour recevoir les informations décrivant le noyau optimisé correspondant, le module (23) d'analyse de cas, d'assemblage et d'injection de code étant en outre relié à des moyens (73) de fourniture des codes optimisés. 8. Système selon les revendications 6 et 7, caractérisé en ce que le module (23) d'analyse de cas, d'assemblage et d'injection de code est en outre relié à la base (16) des noyaux optimisés pour recevoir les informations décrivant un noyau optimisé sans invoquer l'optimiseur (12) si le noyau recherché y a été stocké. 9. Système selon la revendication 8, caractérisé en ce que le module (23) d'analyse de cas, d'assemblage et d'injection de code comprend en outre des moyens (74) pour rajouter aux séquences-étalons (1) des noyaux qui ont été mis en évidence dans ce module (23) d'analyse de cas, d'assemblage et d'injection de code sans avoir d'identification correspondante dans la base (16) des noyaux optimisés, ni dans les séquences- étalons. 10. Système selon l'une quelconque des revendications 6, 8 et 9, caractérisé en ce qu'il comprend un compilateur (81) et un éditeur de liens (82) pour recevoir un code source remanié (19) issu du dispositif (80) d'optimisation et génération du code et produire un code binaire optimisé (83) adapté à la plate-forme matérielle (90). 2865047 - 33 - 11. Système selon la revendication 10, caractérisé en ce qu'il comprend des moyens (85) pour transférer du code source des noyaux optimisés de la base des noyaux optimisés (16) vers le compilateur (81). 12. Système selon la revendication 10, caractérisé en ce qu'il comprend un compilateur (181) et un module d'installation (182) pour installer sur la plate-forme matérielle (90) une bibliothèque dynamique contenant l'ensemble des fonctionnalités des noyaux optimisés. 13. Système selon l'une quelconque des revendications 1 à 12, caractérisé en ce que le domaine d'application prédéterminé est le calcul scientifique. 14. Système selon l'une quelconque des revendications 1 à 12, caractérisé en ce que le domaine d'application prédéterminé est le traitement du signal. 15. Système selon l'une quelconque des revendications 1 à 12, 15 caractérisé en ce que le domaine d'application prédéterminé est le traitement graphique 16. Système selon l'une quelconque des revendications 1 à 15, caractérisé en ce que les séquences-étalons (1) comprennent un ensemble de codes de type boucle simples et génériques spécifiés dans un langage de type source et organisés en niveaux de manière hiérarchique par ordre de complexité croissante du code du corps de boucle. 17. Système selon la revendication 16, caractérisé en ce que les séquences-étalons (1) comprennent des séquences-étalons de niveau 0 dans lesquelles une seule opération élémentaire est testée et correspond à un corps de boucle constitué d'une seule expression arithmétique représentée par un arbre de hauteur 0. 18. Système selon la revendication 17, caractérisé en ce que les séquences-étalons comprennent des séquences-étalons de niveau 1 pour lesquelles sont considérées et testées les combinaisons de deux opérations de niveau 0, les opérations des séquences-étalons de niveau 1 correspondant à des corps de boucles constitués soit d'une seule expression arithmétique représentée par un arbre de hauteur 1, soit de deux expressions arithmétiques, chacune étant représentée par un arbre de hauteur O. 19. Système selon la revendication 18, caractérisé en ce que les séquences-étalons (1) comprennent des séquences-étalons de niveau 2865047 - 34 - 2 pour lesquelles sont considérées et testées les combinaisons de deux opérations de niveau 1 ou de trois opérations de niveau 0. 20. Système selon l'une quelconque des revendications 16 à 19, caractérisé en ce que les paramètres statiques (2) comprennent notamment le nombre d'itérations de la boucle pour chaque séquence-étalon, le pas d'accès aux tableaux et le type d'opérande, le type d'instructions utilisées, les stratégies de préchargement, les stratégies d'ordonnancement des instructions et des itérations. 21. Système selon l'une quelconque des revendications 16 à 20, caractérisé en ce que les paramètres dynamiques (7) comprennent notamment la localisation des opérandes tableaux dans les différents niveaux de la hiérarchie mémoire, la position relative des adresses de départ des tableaux et l'historique des branchements. 22. Système selon l'une quelconque des revendications 6, 8 et 9, caractérisé en ce que la base des noyaux optimisés (16) comprend des séquences de code source de type boucle correspondant à des fragments de code réel et utile et organisés en niveaux par ordre de complexité croissante. 23. Système selon l'une quelconque des revendications 1 à 12, 20 caractérisé en ce que la plate-forme matérielle prédéfinie (90) comporte au moins un processeur du type Itanium. 24. Système selon l'une quelconque des revendications 1 à 12, caractérisé en ce que la plate-forme matérielle prédéfinie (90) comporte au moins un processeur du type Power ou PowerPC. 25. Système selon l'une quelconque des revendications 13 à 15 et selon la revendication 23, caractérisé en ce que les règles d'optimisation (9) comprennent au moins certaines des règles suivantes: (a) minimiser le nombre d'Ecritures en cas de mauvaise performance des Ecritures par rapport aux Lectures, (b) importance de l'utilisation des paires de chargement en virgule flottante, (c) ajuster le degré de déroulage en fonction de la complexité du corps de boucle, (d) utiliser les latences opérationnelles des opérations arithmétiques, - 35 - appliquer des techniques de masquage pour les vecteurs courts, utiliser des suffixes de localité pour les accès mémoire (Lecture, Ecriture, Préchargement), définir les distances de Préchargement, effectuer une vectorisation de degré 4 pour éviter une partie des conflits de bancs L2 prendre en compte de multiples variantes pour éviter une autre partie des conflits de bancs L2 et les conflits dans la file d'attente des Lectures/Ecritures (j) prendre en compte les gains de performances liés aux différentes optimisations. (k) utiliser des chaînes de branchement minimisant les mauvaises prédictions (vecteurs courts) (I) fusionner les accès mémoires (regroupement de pixels) 15 (m) vectoriser les traitements sur pixels. 26. Système selon l'une quelconque des revendications 13 à 15 et selon la revendication 24, caractérisé en ce que les règles d'optimisation (9) comprennent au moins certaines des règles suivantes: (a) réordonnancer les Lectures pour regrouper les défauts de caches 20 (b) utiliser le préchargement uniquement sur les Ecritures (c) ajuster le degré de déroulage en fonction de la complexité du corps de boucle, (d) utiliser les latences opérationnelles des opérations arithmétiques, (e) utiliser des suffixes de localité pour les accès mémoire (Lecture, 25 Ecriture, Préchargement), (f) définir les distances de Préchargement, (g) prendre en compte de multiples variantes pour éviter les conflits dans les files d'attente Lecture/Ecriture (h) prendre en compte les gains de performances liés aux différentes 30 optimisations. (e) (f) (i)