Proposition d'un mod\`ele pour l'optimisation automatique de boucles dans le compilateur Tiramisu : cas d'optimisation de d\'eroulage
Asma Balamane, Zina Taklit

TL;DR
This paper proposes a neural network-based approach to automatically optimize loop unrolling in Tiramisu, a high-performance language, aiming to improve code performance without manual tuning.
Contribution
It introduces a novel method using neural networks to automate loop unrolling optimization in Tiramisu, reducing manual effort and improving performance.
Findings
Successful automation of loop unrolling factor selection.
Improved program performance through optimized unrolling.
Reduction in manual tuning efforts.
Abstract
Computer architectures become more and more complex. It requires more effort to develop techniques that improve the programs of performance and allow to exploit material resources efficiently. As a result, many transformations are applied on various levels of code abstraction. The first level is the high level, where the representation is close to the high level language. The second one is the low level, where the presentation is close to the machine code. Those transformations are called code optimizations. Optimizing programs requires deep expertise. On one hand, it is a tedious task, because it requires a lot of tests to find out the best combination of optimizations to apply with their best factors. On the other hand, this task is critical, because it may degrade the performance of the program instead of improving it. The automatization of this task can deal with this problem and…
Peer Reviews
No public reviews on file for this paper yet. If you reviewed it on a platform where reviews are public (OpenReview, ICLR, NeurIPS, ICML), you can paste yours below so the community can read it here.
Videos
No videos yet. Explain this paper in a talk, walkthrough, or lecture? Add one.
Taxonomy
TopicsParallel Computing and Optimization Techniques · Artificial Intelligence in Games · Video Analysis and Summarization
\MakePerPage
footnote
\ChNameVar \ChNumVar \ChTitleVar
See pages - of FistPagePFE_final.pdf
Dédicaces
*Tous les mots ne sauraient exprimer ma gratitude, et mon profond amour
Merci pour ta tendresse, ton attention, ta patience et tes encouragements
Merci pour tout ma bien aimée MAMAN
Ma chère grand-mère, mon adorable sœur Imane et ma chère tante Saida
Mon cher frère Islam
Aucune dédicace ne peut exprimer la profondeur des sentiments d’amour et d’attachement que j’éprouve à votre égard
Mon aimable amie Selma
Merci d’être toujours à mes côtés jusqu’à la fin
Mes chères amies Yasmine, Chama et Amel
Ensembles nous avons passé les plus agréables moments
Mes amies d’enfance Zineb et Lidya
Mon amie et binôme Zina
Merci…
**-Asma- **
Je dédie ce travail à ma petite famille, qui m’a toujours soutenu et encouragé pour réaliser mes rêves
*particulièrement à ma mère qui me soulage toujours, me remonte le moral, m’encourage pour aller plus loin dans ma vie et prie pour moi jour et nuit.
*Je dédie aussi ce travail a mes amis qui m’ont consolidé psychiquement et m’ont aidé à l’accomplir. Plus particulièrement, Miled, Selma, Amel et Fahima qui ont veillé pour m’aider, m’encourager et me donner des précieux conseils.
À Amina également qui m’a fort orienté pour implémenter le modèle du projet.
*À Mon binôme Asma. Ensembles nous avons travaillé main à main pour réaliser et finaliser ce travail.
Aux membres du club GDG et Code&Share avec lesquels j’ai passé de très bons moments cette année.
*Je ne peux trouver les mots justes et sincères pour vous exprimer mon affection et ma gratitude. Vous êtes pour moi les plus agréables personnes…
Merci d’être toujours à mes cotés.*
**-Zina- **
Remerciment
Nous remercions Allah de nous avoir donné la patience et le courage pour accomplir ce travail.
Nous tenons à remercier notre promoteur Dr. Riyadh BAGHDADI pour son suivi continu, sa patience, sa disponibilité et surtout ses judicieux conseils, qui ont contribué à alimenter notre réflexion et nous orienter pour prendre les meilleurs choix.
*Nous remercions également Dr. Fatima Zohra BENHAMIDA, notre encadrante à l’École nationale Supérieure de l’Informatique, pour son accueil et ses conseils qui nous ont permis d’améliorer considérablement ce mémoire.
*Nous remercions les membres de jury pour l’intérêt qu’ils portent à ce travail et d’avoir accepté de l’examiner et de le juger.
*Nous tenons à remercier également Mme. AIT ALI YAHIA Dahbia, pour tous ses efforts afin d’assurer le bon déroulement des stages de fin d’étude, et pour sa compréhension et sa gentillesse.
Pour terminer nous témoignons nos reconnaissances et notre profonde gratitude envers toutes les personnes qui ont contribué de loin ou de près à la réalisation de ce travail.
Résumé
Les architectures des ordinateurs deviennent de plus en plus complexes. Ceci nécessite des efforts pour développer des techniques d’amélioration de programmes permettant une exploitation efficace des ressources matérielles. De ce fait, des transformations sont appliquées sur divers niveaux d’abstraction de code, à savoir le haut niveau, où la représentation du code est proche du langage haut niveau, et le bas niveau, où la représentation est proche du code machine. Ces transformations s’appellent les optimisations de code.
L’optimisation des programmes requiert une expertise profonde. D’une part, elle représente une tâche fastidieuse, car elle nécessite plusieurs tests pour trouver les meilleures combinaisons d’optimisations à appliquer avec leurs bons paramètres. D’une autre part, cette tâche est critique, car elle risque de dégrader les performances du programme au lieu de les améliorer. L’automatisation de l’optimisation de programmes permet de faciliter cette tâche et d’obtenir de bons résultats.
Notre projet de fin d’étude consiste à concevoir et développer un modèle pour l’optimisation automatique de boucles dans Tiramisu. Ce dernier est un nouveau langage pour créer des codes de haute performance. Il permet de séparer entre le programme et ses optimisations. Nous avons pris comme cas d’étude l’optimisation de déroulage (loop unrooling). Notre contribution vise à automatiser le choix du meilleur facteur de l’optimisation de déroulage de boucles pour un programme écrit en Tiramisu. La solution proposée se base sur les réseaux de neurones profonds.
Pour évaluer notre solution, nous avons d’abord comparé le modèle proposé avec les autres algorithmes du machine learning (K plus proches voisin et les arbres de décision) utilisés dans les travaux précédents. Notre modèle présente une précision compétitive à ces deux méthodes. Une seconde évaluation est effectuée sur un ensemble de benchmarks. Nous avons comparé entre les facteurs prédits par le modèle et ceux trouvés exhaustivement. Le modèle a pu prédire correctement le meilleur facteur de déroulage dans des instances. Nous avons également évalué l’accélération en temps d’exécution des benchmarks sans application de déroulage et avec l’application du déroulage dont le facteur est prédit par notre modèle. Notre méthode a pu améliorer le temps d’exécution dans des instances. Ce résultat confirme que les réseaux de neurones profonds peuvent être utilisés pour résoudre le problème de choix du facteur de déroulage. Le modèle apprend et donne une précision supérieure à la prédiction aléatoire.
Mots clés :
Optimisation automatique de boucles, sélection de paramètres d’optimisation, déroulage de boucle, Tiramisu, approche d’optimisation automatique.
Abstract
Computer architectures become more and more complex. It requires more effort to develop techniques that improve programs’ performance and allow to exploit material resources efficiently. As a result, many transformations are applied on various levels of code abstraction. The first level is the high level, where the representation is close to the high level language. The second one is the low level, where the presentation is close to the machine code. Those transformations are called code optimizations.
Optimizing programs requires deep expertise. On one hand, it is a tedious task, because it requires a lot of tests to find out the best combination of optimizations to apply with their best factors. On the other hand, this task is critical, because it may degrade the program’ performance instead of improving it. The automatization of this task can deal with this problem and permit to obtain good results.
Our end of study project consists on proposing a novel approach based on neural networks to automatically optimize loops in Tiramisu. Tiramisu is a new language to create a code of high performance. It allows to separate between the algorithm and its optimizations. We have chosen loop unrolling as a study case. Our contribution aims to automate the choice of the best loop unrolling factor for a program written in Tiramisu.
Initially, we have compared our model with other machine learning algorithms ( KNN and decesion treee) from previous work. Our model was competitive to those two methods. A second evaluation has been performed on a set of five benchmarks. We compared between the factor predicted by our model and the best one found exhaustively. Our model has been able to predict correctly the best unrolling factor in of the instances. We have also evaluated the acceleration in the execution time of the benchmarks without applying loop unrolling optimization and with the application of loop unrolling with predicted unrolling factor. Our method could improve execution time in of the instances. This result confirms that deep neural networks can be used to solve the problem of choosing the best unrolling factor. The model has been able to learn and it gives greater precision than random prediction.
Key words :
Automatic optimization of loops, selection of the best factor of optimizations, loop unrolling, Tiramisu, automatic optimization approaches.
See pages - of resumezina.pdf
Table des matières
-
II.2.2 Problème du choix de l’ordre des optimisations (phase-ordering problem)
-
II.3.1.4 Comparaison entre les approches d’exploration d’espace des optimisations
-
II.3.2 Approches de sélection des bons paramètres des optimisations
-
II.3.3.4 Comparaison entre les approches d’estimation de coût
-
II.4.1 Autoscheduler de Halide basé sur l’approche analytique
-
II.4.2 Modèle automatique pour le problème de Tile Size Selection (TSS)
-
III.2.2.1 Amélioration de l’optimisation du code en Tiramisu
-
IV.3.1 Module d’extraction des caractéristiques des programmes
-
IV.3.1.1 Extraction initiale des caractéristiques de la computation
-
IV.3.1.2 Mise à jour et exportation des caractéristiques de la computation
-
IV.3.2 Modèle de prédiction du meilleur facteur de déroulage
-
IV.3.3.1 1ère itération : modèle de réseau de neurones de base
-
IV.3.3.2 2ème itération : sélection de bons hyperparamètres du modèle
-
IV.3.3.3 3ème itération : l’entraînement du modèle sur les données finales
-
IV.7.1 Implémentation du module d’extraction des caractéristiques
-
A.2.1 Aspects à considérer dans la conception du modèle de tuilage
-
A.2.2 Approches adoptées de choix du facteur de tuilage optimal
-
B.2 Commandes du mappage des niveaux des boucles sur l’architecture matérielle
-
C.4.2 Propagation de l’information et rétropropagation du gradient
Table des figures
- I.1 Structure générale d’une boucle.
- I.2 Structure générale d’un nid de boucle.
- I.3 Parallélisation des itérations d’une boucle imbriquée sur deux threads.
- I.4 Vectorisation de la boucle y avec un facteur de quatre.
- I.5 Interversion de deux boucles.
- I.6 Fusion de deux boucles adjacentes en une seule boucle.
- I.7 Découpage en bandes d’une boucle avec un facteur de 16.
- I.8 Déroulage d’une boucle avec un facteur de déroulage de quatre.
- I.9 Un tuilage avec facteur : . Après le tuilage, A est accessible en blocs de taille .
- I.10 Exemple de loop skewing, J sert à une nouvelle variable pour remplacer j avec .
- I.11 Exemple de l’interversion de boucle pour montrer la difficulté de choix de la meilleure optimisation.
- II.1 Comparaison entre l’optimisation automatique et manuelle effectuée sur trois benchmarks.
- II.2 Exemple d’espace de recherche d’une combinaison de quatre optimisations.
- II.3 Approches d’optimisation automatique de code.
- II.4 Ensemble des optimisations et des caractéristiques utilisées dans la technique de Agakov \BOthers. (\APACyear2006).
- II.5 Variation du temps d’exécution en fonction des paramètres de tuilage et du déroulement de boucles.
- II.6 Principe général de l’approche d’estimation par exécution.
- II.7 Principe général de l’approche d’estimation du coût utilisant le modèle analytique.
- II.8 Principe général de l’approche d’estimation basée sur l’apprentissage automatique.
- II.9 Vue d’ensemble sur le modèle d’estimation du coût basé sur les réseaux de neurones.
- II.10 Les entrées et la sortie de l’Auto-Schedule de Halide.
- II.11 Comparaison entre les temps d’exécution de quelques benchmarks optimisés manuellement et par Auto-Schedule de Halide.
- II.12 Architecture globale d’Ithemal.
- II.13 Phase de la canonicalisation d’Ithemal.
- III.1 Vue d’ensemble sur Tiramisu.
- III.2 Schéma global de code en Tiramisu.
- III.3 Code Tiramisu équivalent à une boucle d’une profondeur de deux.
- III.4 Code Tiramisu d’un produit suivi d’une somme de deux matrices.
- III.5 Forme générale d’application des commandes de Scheduling sur une computation.
- IV.1 Structure d’un programme en Tiramisu.
- IV.2 Processus du choix manuel du meilleur facteur de déroulage.
- IV.3 Architechture globale du système.
- IV.4 Exemple de programme Tiramisu appartenant à la classe de code visée.
- IV.5 représentation d’une expression en Tiramisu.
- IV.6 Diagramme de classe du module d’extraction des caractéristiques des programmes.
- IV.7 Architecture de base du modèle.
- IV.8 La structure d’un neurone artificiel.
- IV.9 Utilisation des différents datasets pour entraîner le modèle final.
- IV.10 Distribution des données du dataset sur les 64 classes.
- IV.11 L’amélioration de la précision du modèle au cours de génération de données dans le cas de 7 classes à gauche et 4 classes à droite.
- IV.12 Distribution de données dans le dataset final sur les différentes classes du modèle
- IV.13 Architecture technique globale du système.
- IV.14 Variation de la fonction perte durant les tests des algorithmes d’optimisation et des taux d’apprentissage.
- IV.15 La variation de la fonction perte pour les différents algorithmes d’initialisation de poids.
- V.1 Les caractéristiques du GPU offert par la plateforme GoogleColab.
- V.2 Résultats du test sur les différentes cas du schedules du benchmarks.
- A.1 Somme d’un vecteur de 100 entrées en "C".
- A.2 Le code assembleur équivalent au code C de la somme d’un vecteur de 100 entrées.
- A.3 Le code assembleur équivalent au code C de la somme d’un vecteur de 100 entrées après l’application de déroulage avec un facteur de 4.
- A.4 Découpage en bande du code original avec un facteur de 4
- A.5 Le code après avoir effectué la permutation.
- A.6 Illustration de la division de l’espace de données après l’application de tuilage.
- B.1 transformation suite à l’application de la commande split.
- C.1 Comparaison entre la structure d’un réseau de neurones artificiel et un réseau de neurones biologique.
- C.2 La structure d’un réseau de neurones artificiel.
- C.3 Les couches principales d’un réseau de neurones.
- D.1 Dispersion des points dans les différents cas de l’apprentissage (sous-apprentissage, modèle robuste et surapprentissage).
Liste des tableaux
- I Tableau comparatif entre les approches d’exploration d’espace d’optimisations.
- II Comparaison entre les trois approches d’estimation du coût.
- III Les temps d’exécution d’un programme optimisé avec trois différents facteurs (8, 16, 32) de la transformation de déroulage.
- IV Comparaison entre les différentes classes de réseaux de neurones candidates.
- V Sous-ensemble des caractéristiques données par le module d’extraction des caractéristiques d’une computation.
- VI hyperparamètres choisis pour le modèle.
- VII Caractéristiques de l’architecture de test (évaluation par benchmarks).
- VIII Comparaison de la présion de notre modèle de réseau de neurones avec les deux autres modèles de machine learning (KNN et arbre de décsiion).
- IX Modalités de la taille des données en entrée.
- X Liste des benchmarks d’évaluation.
- XI Sous ensemble de caractéristiques du benchmark MMM.
- XII Résultats de tests sur le benchmark MMM.
- XIII Sous ensemble de caractéristiques du benchmark SMM.
- XIV Résultats de tests sur le benchmark SMM.
- XV Sous ensemble de caractéristiques du benchmark RGB_gray.
- XVI Résultats de tests sur le benchmark RGB_gray.
- XVII Sous ensemble de caractéristiques du benchmark Blur.
- XVIII Résultats de tests sur le benchmark Blur.
- XIX Résultats de tests sur le benchmark Conv_layer.
- XX Exemple de Commandes de la parite Schedule de Tiramisu
Glossaire
[TABLE]
Introduction générale
Afin d’atteindre des niveaux de performances avancés, les architectures des ordinateurs deviennent de plus en plus complexes. Dans certains domaines, les applications requièrent une exploitation efficace des ressources matérielles. Les développeurs doivent introduire des transformations (optimisations) sur divers niveaux d’abstraction de codes. Ces optimisations visent à améliorer certaines métriques notamment l’utilisation de la mémoire et le temps d’exécution.
L’optimisation des programmes n’est pas une tâche simple. Elle requiert une expertise profonde afin de donner les bonnes optimisations avec leurs meilleurs facteurs à appliquer. Il s’agit d’une tâche qui est d’une part fastidieuse, car elle nécessite plusieurs tests pour trouver les meilleures combinaisons d’optimisations. D’une autre part, c’est une tâche critique, car elle risque de dégrader les performances du programme au lieu de les améliorer. En effet, L’optimisation dépend de plusieurs contraintes, à savoir les caractéristiques matérielles de la machine d’exécution, les dépendances entre les instructions du programme et les interactions entre les optimisations de codes à appliquer. Ces interdépendances agissent significativement sur l’effet des optimisations.
L’automatisation de l’optimisation des programmes permet de faciliter cette tâche et d’obtenir de bons résultats compétitifs aux optimisations soigneusement établies par des experts. Plusieurs approches ont été proposées afin d’améliorer l’automatisation de l’optimisation de codes donnant naissance à des nouvelles techniques intégrées aux compilateurs. Il existe plusieurs problèmes ouverts dans le domaine d’optimisation automatique : la sélection des optimisations bénéfiques à appliquer, l’estimation des bons paramètres des optimisations et la définition de l’ordre d’application des optimisations pour avoir les meilleures performances.
L’optimisation du code touche principalement deux niveaux, à savoir l’optimisation haut niveau, où la représentation du code est proche du langage haut niveau, et l’optimisation bas niveau où la représentation est proche du code machine. L’optimisation haut niveau du code s’avère primordiale avant toute optimisation bas niveau notamment dans certains langages dit spécifiques au domaine (LSD). Ces langages fournissent des fonctionnalités de programmation spéciales qui ne sont pas facilement offertes par les autres langages de programmation à objectifs généraux Touati \BBA de Dinechin (\APACyear2014).
Tiramisu est un nouveau langage et compilateur qui permet de générer des codes très rapides et de cibler différentes architectures matérielles (multicore, GPU, FPGA et systèmes distribués). Il a été lancé en 2018 par l’équipe COMMIT de CSAIL111Computer Science and Artificial Intelligence Laboratory. du MIT, elle travaille principalement sur le développement de nouveaux compilateurs qui facilitent l’écriture des codes optimisés. Tiramisu offre la particularité de séparer l’algorithme et les optimisations à appliquer sur le code dans une partie appelée Schedule. Cette partie contient des commandes d’optimisation introduites par le programmeur manuellement, c’est-à-dire que le programmeur doit choisir parmi les optimisations, celles qui rendent son code plus rapide. Or, l’optimisation manuelle est compliquée, le programmeur risque de ne pas choisir les meilleures combinaisons d’optimisations.
Dans le cadre de notre projet de fin d’étude, nous visons à contribuer dans l’optimisation automatique de boucles dans Tiramisu. Nous prenons comme cas d’étude l’optimisation de déroulage (loop unrooling). Le problème de sélection du meilleur facteur de déroulage représente le problème principal ciblé par notre projet de fin d’étude.
Concrètement, les objectifs de notre contribution sont :
- –
Concevoir un modèle pour automatiser le choix du meilleur facteur de l’optimisation de déroulage de boucles pour un programme écrit en Tiramisu (un programme déjà optimisé ou non optimisé), et ce, pour des architectures matérielles à base de CPU.
- –
Explorer une nouvelle approche de sélection automatique du facteur de déroulage. La solution proposée doit présenter une précision qui dépasse la sélection aléatoire (random selection) et qui est compétitive aux travaux précédents.
Ce document est organisé comme suit : dans la première partie, nous commençons d’abord par une étude théorique répartie en trois chapitres. Dans le chapitre I nous introduisons l’optimisation du code en donnant une explication sur le fonctionnement des optimisations haut niveau du code les plus utilisées. Le chapitre II présente les approches et les techniques adoptées par les compilateurs pour optimiser automatiquement les programmes. Dans le chapitre III, nous exposons le compilateur Tiramisu ciblé par notre projet. Dans la deuxième partie, nous détaillons notre contribution tout au long de trois chapitre. Dans le chapitre IV, nous expliquons les phases de conception du système proposé en détaillant notre modèle de prédiction du meilleur facteur de déroulage. Dans le chapitre IV.4, nous expliquons les étapes de réalisation du système tout en justifiant les différents choix technologiques considérés.
Pour clore ce mémoire, nous exposons dans le chapitre V, les résultats de comparaison du modèle avec deux autres algorithmes de machine learning (K plus proches voisin et les arbres de décision) utilisés dans les travaux précédents pour le problème de sélection du facteur de déroulage. Nous exposons également dans le dernier chapitre l’évaluation effectuée sur des benchmarks implémentés sur Tiramisu.
Etat de l’art
Chapitre I Généralités sur les optimisations de code
Introduction
Dans certains domaines d’application, il ne suffit pas d’écrire des codes qui répondent aux spécifications, mais aussi des codes qui exploitent efficacement les ressources matérielles avec un temps d’exécution minimal Knijnenburg \BOthers. (\APACyear2002).
Les optimisations des programmes sont des transformations appliquées sur la structure du code dans le but d’améliorer ses performances notamment le temps d’exécution. Cependant, dans certains cas, l’application des optimisations peut les dégrader. En effet, déterminer les transformations susceptibles d’améliorer les performances dépend de plusieurs facteurs parfois incontrôlables par le programmeur, à savoir la gestion des caractéristiques de l’architecture de la machine . D’autre part, la connaissance de l’impact d’application de chaque optimisation à part et celui de sa combinaison avec d’autres est très compliquée ce qui fait de l’optimisation du code une tâche assez complexe et nécessite une expertise profonde.
Dans ce chapitre, nous allons exposer les optimisations de codes utilisées dans le compilateur Tiramisu, expliquer leurs objectifs et déterminer les paramètres à considérer pour les appliquer. Enfin, nous allons clôturer le chapitre par une explication des difficultés rencontrées afin de choisir les bonnes combinaisons d’optimisations à appliquer.
I.1 Niveaux d’optimisation du code
Un compilateur est un programme qui traite les instructions écrites dans un langage de programmation donné pour les traduire en langage machine, utilisé par le processeur d’un ordinateur. Les algorithmes peuvent être exprimés sous divers langages de programmation, les codes seront ensuite transformés en une représentation intermédiaire ou un code machine. De ce fait, un code peut avoir une représentation haut niveau qui est proche aux langages de programmation haut niveau, et une représentation bas niveau proche aux instructions du processeur.
les optimisations de code représentent l’ensemble des techniques utilisées afin d’améliorer les performances d’un programme, à savoir le temps d’exécution, l’utilisation de la mémoire ou encore la consommation de ressources. L’optimisation des programmes s’avère primordiale dans certains domaines d’application nécessitant un calcul immense et donc une grande consommation des ressources. La théorie de la compilation définit des frontières entre l’optimisation haut niveau et l’optimisation bas niveau, cette classification est liée aux niveaux de la représentation de code, à savoir la représentation haut niveau et la représentation bas niveau.
- Optimisation bas niveau (backend optimization): il s’agit de l’ensemble des transformations appliquées sur le code sous sa représentation finale proche aux instructions destinées aux processeurs comme les instructions assembleur et le code à trois adresses. Les métriques optimisées à ce niveau d’abstraction sont généralement liées à l’architecture du processeur : le nombre des instructions générées, la taille du code, l’ordonnancement des instructions, l’allocation et l’affectation des registres, l’optimisation du cache, l’optimisation du mode d’adressage, etc.
- Optimisation haut niveau (front-end optimization): elle représente l’ensemble des transformations appliquées sur le code écrit dans le langage haut niveau directement ou sous sa représentation intermédiaire proche au langage haut niveau, cette représentation contient des structures syntaxiques sophistiquées telles que les boucles et les structures de contrôles ainsi que les structures de données complexes. Parmi ces optimisations se trouvent les transformations des nids de boucles, l’analyse de dépendance de données et de procédures, la parallélisation des instructions et des blocs et l’analyse des alias. L’optimisation haut niveau de code est généralement effectuée par le programmeur, qui gère les entités abstraites et tient compte du fonctionnement du programme en général pour optimiser la conception du système. L’analyse et l’optimisation à ce niveau d’abstraction du programme permettent d’améliorer des métriques indépendamment des architectures d’exécution Touati \BBA de Dinechin (\APACyear2014).
Ce travail s’intéresse à l’optimisation haut niveau des codes qui représente la phase basique de l’optimisation de codes. Dans des langages spécifiques aux domaines (LSD) comme Halide Ragan-Kelley \BOthers. (\APACyear2013), Tiramisu Baghdadi \BOthers. (\APACyear2019), PolyMage Mullapudi \BOthers. (\APACyear2015) qui sont utilisés dans le domaine de la programmation de hautes performances, l’optimisation haut niveau est primordiale avant toute optimisation bas niveau. En effet, les LSD fournissent des fonctionnalités spéciales de programmation optimisée qui ne sont pas fournies par les autres langages de programmation comme C, Java, etc.
I.2 Optimisations de boucles
Les outils de profilage111Code profiling consiste à analyser l’exécution d’un programme pour connaître son comportement à l’exécution. de code ainsi que les démonstrations basées sur la théorie de la complexité des algorithmes ont affirmé que les programmes passent plus de 90% du temps d’exécution au niveau des boucles, il est donc plus intéressant de focaliser sur l’optimisation des nids de boucles. De plus, ils utilisent voracement les ressources matérielles, tel que la mémoire cache et les unités de calcul Elloumi (\APACyear2013).
I.2.1 Nids de boucles
Une "boucle" est considérée comme étant une structure itérative d’un programme fini. La structure algorithmique est schématisée dans la figure I.1 Ciorba (\APACyear2008).
Un nid de boucles est une imbrication d’un nombre fini n (n>1) de boucles (voir figure I.2). Il est dit "parfait" si toutes les instructions appartiennent à la boucle la plus interne.
Une grande partie du temps d’exécution est consommée au niveau des boucles. De ce fait, il est très utile d’optimiser ces parties de codes, ceci permet d’améliorer efficacement les performances globales des programmes dans nombreuses applications.
Différentes techniques sont utilisées pour augmenter les performances de boucles. Cependant, les optimisations ne sont pas toujours valides et certaines peuvent détériorer les performances du programme ou même fausser ses résultats. L’analyse de dépendance est une des théories qui permettent de décider la possibilité d’appliquer une optimisation sur un programme sans avoir changé sa logique. Nous allons aborder ici les optimisations de boucles les plus importantes et les plus utilisées, à savoir la parallélisation, la vectorisation, la fusion de boucles, l’interversion de boucles, le déroulage, tuilage, etc.
I.2.2 Parallélisation (Parallelization)
Le parallélisme de boucle est un type très courant de parallélisme du code. Il a comme principal objectif de détecter et d’exploiter le parallélisme dans des programmes séquentiels. Cette transformation consiste à affecter chaque itération indépendante à des threads parallèles puis lancer l’exécution de ces derniers de manière asynchrone (voir figure I.3). Cela nécessite de trouver la bonne transformation de boucle qui permet de lancer le parallélisme.
Pour effectuer cette transformation d’une manière efficace, il faut prendre en considération les paramètres de la machine. En effet, plus que la machine dispose d’unités fonctionnelles plus la parallélisation est meilleure, de ce fait, la machine doit avoir au moins deux unités de traitement pour pouvoir exécuter les instructions en parallèle.
Il est intéressant de noter que la gestion des threads peut engendrer un coût supplémentaire. Donc, pour que l’optimisation soit vraiment utile, il faut que le temps perdu dans la gestion des threads soit strictement inférieur au temps gagné par la parallélisation. D’autre part, pour que la parallélisation soit valide, il faut s’assurer qu’elle n’inverse pas le sens de dépendance222Il y a une dépendance de données entre deux instructions, S1 et S2, lorsque elles tentent d’accéder à la même localité mémoire M, et au moins l’une de ces deux instructions tente de modifier M. entre les itérations Allen \BBA Kennedy (\APACyear2002).
I.2.3 Vectorisation (Vectorization)
La vectorisation est le processus permettant de transformer un programme qui opère sur des données élémentaires à un programme qui opère sur des vecteurs de données chacun porte sur deux éléments ou plus Jang \BOthers. (\APACyear2010).
Dans une boucle, cette transformation consiste à regrouper un ensemble de éléments qui subissent le même traitement à travers les itérations pour les rediriger vers des registres vectoriels afin d’effectuer un traitement commun Allen \BBA Kennedy (\APACyear2002). La figure I.4 montre un exemple de vectorisation de boucles.
L’objectif de cette transformation est d’utiliser efficacement les ressources matérielles notamment les registres. La vectorisation est une technique très utile pour exploiter le parallélisme au niveau de données. La taille des registres vectoriels affecte le choix du facteur dans la transformation de vectorisation, car plus la taille des registres est grande plus il est possible d’appliquer la vectorisation pour gagner en temps d’exécution Jang \BOthers. (\APACyear2010).
I.2.4 Interversion de boucles (Loop reordering)
Cette transformation permet de changer l’ordre d’exécution de niveaux de boucle dans une boucles imbriquée i.e la boucle la plus interne devient la plus externe et vice versa (voir la figure I.5). L’interversion de boucles permet d’exploiter le parallélisme et d’améliorer la localité de données Bacon \BOthers. (\APACyear1994) et par conséquent, d’utiliser le cache efficacement et d’améliorer l’accès aux données.
Cette transformation permet d’excellentes améliorations, mais elle n’est pas toujours légale. Pour effectuer cette transformation de manière à préserver le sens original du programme, il faut s’assurer qu’elle soit valide à appliquer, ceci est fait en analysant la dépendance entre les instructions des boucles à intervertir.
I.2.5 Fusion de boucles (Loop fusion)
Cette transformation consiste à fusionner deux boucles adjacentes en une seule boucle (voir la figure I.6) afin de réduire la surcharge de la boucle et d’améliorer les performances d’exécution.
Il est important de noter que cette transformation n’améliore pas toujours le temps d’exécution, car cela dépend des contraintes architecturales de la machine. Par exemple, l’architecture de la mémoire peut offrir de meilleures performances si les deux tableaux sont initialisés dans des boucles séparées, plutôt que de les initialiser simultanément dans une seule boucle. D’autre part, cette transformation n’est pas toujours légale, elle doit être soumise aux conditions de validité en utilisant l’analyse de dépendance.
I.2.6 Découpage en bandes (Loop splitting)
Le découpage en bandes est l’une des transformations de boucles les plus puissantes qui consiste à découper la dimension par un facteur pour créer de nouvelles dimensions en divisant la variable d’itération en sous variables internes et externes. Ce facteur est connu sous le nom de "facteur de découpage". Il transforme la boucle ayant par exemple l’indice i et la dimension N en une boucle imbriquée : la boucle la plus interne avec la dimension k et la boucle externe avec la dimension qui est le résultat de la division de l’étendue de sur le facteur de découpage k (voir la figure I.7). Après le découpage, les références à l’index d’origine deviennent : () Ragan-Kelley \BOthers. (\APACyear2012).
Cette transformation est utilisée pour les nids de boucles qui manipulent les données multidimensionnelles (tableaux à 2 dimensions ou à 3 dimensions). L’objectif principal de cette transformation est de séparer les itérations indépendantes de la boucle principales, ce qui permet de les exécuter en parallèle.
L’optimisation de découpage en bandes ne change pas l’ordre d’évaluation des instructions. Cette transformation ne fait rien en elle-même, mais cela ouvre d’autres possibilités d’optimisations comme le déroulage lorsqu’il est combiné avec d’autres transformations. La vectorisation et le déroulage l’utilisent souvent, ils sont précédés par une division de la dimension de boucles par le facteur de vectorisation ou de déroulage. Ensuite, la vectorisation ou le de déroulage de la boucle interne est effectué.
I.2.7 Déroulage de boucle (Loop unrolling)
L’optimisation de déroulage (loop unrolling) est une transformation qui consiste à répliquer les instructions de la boucle la plus interne une ou plusieurs fois, selon le facteur de déroulage k donné. Ce qui permet de réduire le nombre d’itérations. Par exemple, si alors le nombre d’itérations (initialement égal à ) est réduit à (voir la figure I.8). Le déroulage est appliqué lorsque plusieurs itérations de boucles rechargent les mêmes valeurs.
La transformation de déroulage permet la réutilisation des registres, si les instructions déroulées sont indépendantes entre elles, il est possible de les lancer en parallèle à condition que la machine dispose d’un nombre suffisant de registres pour appliquer la parallélisation. Un autre avantage de cette transformation est qu’elle peut être appliquée sur n’importe quelle boucle sans restriction sur le langage source. Le déroulage améliore les performances presque dans tous les cas où il est appliqué d’une manière significative Bacon \BOthers. (\APACyear1994).
Il est recommandé d’appliquer cette transformation sur la boucle la plus interne, car le fait de l’appliquer sur la boucle externe risque d’augmenter la surcharge du contrôle de la boucle au lieu de la diminuer (à cause de la réplication de la boucle interne). Le déroulage de la boucle interne dans le cas des boucles partageant des données qui se chevauchent réduit le nombre total de chargements (load). Il permet aussi de réduire le nombre de tests d’instruction de branchements : conditions d’arrêt seront testées fois, les pénalités de branchements seront réduites d’un facteur de dans les architectures à base de pipeline.
Le facteur de déroulage doit être bien choisi en prenant en considération les contraintes architecturales de la machine : si est grand, le chargement de toutes les instructions dans le cache à la fois ne sera pas possible, ainsi le taux de défauts de cache augmente et de même le temps d’exécution. L’optimisation de déroulage peut être intégrée automatiquement dans les compilateurs grâce à sa simplicité Bacon \BOthers. (\APACyear1994). Pour plus de détails sur cette optimisation voir l’annexe A.
I.2.8 Tuilage de boucles (Loop tiling)
Le tuilage ou encore le tiling ou le blocking est une combinaison de découpage en bandes et d’interversion de boucles. Tout d’abord, nous découpons et avec un facteur de et respectivement ( est appelé le facteur de tuilage). Ensuite, nous réordonnons les variables pour exprimer le tuilage (voir la figure I.9). Le tuilage est une transformation qui permet de découper la matrice en petits blocs rectangulaires : il itère à l’extérieur sur les blocs, et à l’intérieur, il itère sur les instructions de chaque bloc Baghdadi \BOthers. (\APACyear2019).
Le tuilage est la généralisation multidimensionnelle de découpage en bandes. Le tuilage est principalement utilisé pour améliorer la réutilisation du cache en divisant l’espace d’itération333L’ensemble de toutes les instances d’exécution d’une instruction. en blocs Bacon \BOthers. (\APACyear1994). Le tuilage permet aussi de maximiser la localité de données (localité spatiale et temporelle). Il permet de créer de nouvelles boucles internes de sorte que les données accessibles dans les boucles internes s’insèrent dans le cache. Le tuilage peut augmenter le taux de parallélisme, car si les données des blocs ne dépendent pas l’une des autres, il est possible de les paralléliser. Cette transformation s’avère indispensable pour atteindre des hautes performances dans les calculs de multiplication sur les matrices denses ou encore dans l’algèbre linéaire. Elle constitue aussi un facteur clé pour améliorer les performances des réseaux de neurones convolutifs444Les CNN estun type de réseaux de neurones connus par l’opération convolution. Ils s’adaptent bien pour des données organisées en grille telles que les images..
Le tuilage est une transformation complexe. Le facteur de tuilage doit être choisi soigneusement. D’une part, il doit être assez petit pour s’assurer que le bloc peut être chargé entièrement dans le cache et pour que la parallélisation soit bénéfique. D’une autre part, il doit être assez grand pour bien exploiter l’espace du cache. Il n’est pas toujours possible d’appliquer cette transformation comme elle se base sur la transformation de l’interversion de boucles qui n’est pas toujours légale. Elle doit être soumise au processus de validation en utilisant l’analyse de dépendance avant son application. Pour plus de détails sur cette optimisation voir l’annexe A.
I.2.9 Torsion de boucles (Loop skewing)
la torsion de boucle ou Loop skewing est une transformation qui redéfinit l’espace d’itération pour permettre d’exploiter la parallélisation par la suite. Elle est principalement utile en combinaison avec la transformation d’interversion de boucles Bacon \BOthers. (\APACyear1994).
Cette optimisation est effectuée en ajoutant l’index de la boucle externe multipliée par le facteur d’inclinaison , aux bornes de la variable de la boucle interne, puis soustraire la même valeur pour chaque utilisation de la variable de la boucle interne à l’intérieur de la boucle (voir la figure I.10). Elle change les bornes de la boucle et change aussi l’utilisation des index correspondants pour garder le même sens que le programme original. Cette transformation ne change pas le sens du programme et elle est toujours légaleBacon \BOthers. (\APACyear1994).
Les boucles qui résultent de la transformation d’inclinaison sont extrêmement trapézoïdales555Trapézoïdal est un adjectif qui désigne quelque chose en forme de trapèze, c’est-à-dire avec quatre côtés dont deux sont parallèles. ce qui produit des bornes complexes. Les pénalités résultant d’une telle boucle irrégulière sont plus sévères sur les machines vectorielles mais moins graves sur les multi-processeurs asynchrones. Cependant, même sur ces architectures, l’inclinaison de boucle doit être appliquée avec précaution pour éviter des déséquilibres de charges significatifs Allen \BBA Kennedy (\APACyear2002).
I.2.10 Calcul redondant
Cette transformation permet de calculer la donnée à chaque fois que c’est nécessaire au lieu d’aller la charger à partir de la mémoire. En effet, la donnée doit être recalculée d’une manière redondante à chaque fois qu’elle est utilisée même si le résultat existe déjà dans la mémoire. Cette optimisation est très utile lorsque le coût arithmétique (le temps pris) de calcul est inférieur par rapport au coût de son chargement à partir de la mémoire Manseri (\APACyear2018).
I.2.11 Réorganisation de calcul
Cette transformation permet de réordonner les instructions du programme Bacon \BOthers. (\APACyear1994). Elle consiste à rapprocher les instructions consommatrices (celles qui utilisent la donnée produite par d’autres instructions) des productrices (celle qui produit ces données). Cette transformation assure que la donnée consommée se trouve toujours dans le cache de données, ce qui permet d’améliorer la localité des données par conséquent Manseri (\APACyear2018).
I.3 Difficultés du choix des bonnes optimisations
Plus les architectures des processeurs deviennent complexes, plus le nombre de transformations possibles à appliquer augmente et donc le processus de sélection des bonnes optimisations devient très compliqué aussi Bacon \BOthers. (\APACyear1994).
Ainsi, la structure de l’architecture matérielle est un facteur déterminant pour le choix des optimisations à effectuer, car il faut maximiser l’utilisation des ressources, à savoir les processeurs, les unités fonctionnelles et les registres, etc. D’autre part, il faut minimiser le nombre d’opérations effectuées, les défauts de cache et la taille mémoire requise.
Parfois, ces objectifs ne peuvent pas être réunis. Considérons la figure I.11(a), supposons que la machine stocke les tableaux colonne par colonne, l’accès à chaque élément de la matrice "a" nécessite de charger entièrement la colonne contenant cet élément à chaque itération de la boucle interne. Par contre, en inversant l’ordre des niveaux de la boucle, les éléments seront accessibles séquentiellement colonne par colonne ce qui correspond à l’ordre de stockage défini par la machine. Ceci permet de réduire la distance entre deux accès successifs de la mémoire et donc, de minimiser les défauts de cache. D’autre part, la version (a) du programme exprimé dans la figure I.11 permet au tableau "total[i]" d’être chargé dans le registre une seule fois pour chaque ligne de la matrice "a". Mais, la version optimisée (b) augmente le nombre des opérations de chargement/stockage de (de à ) car l’élément est chargé dans le registre pour chaque itération de la boucle interne de la matrice Bacon \BOthers. (\APACyear1994).
Dans la Figure I.11 (a), la boucle interne accède aux éléments du tableau a avec un stride666Le nombre de locations mémoires entre deux éléments successifs dans un tableau.-n. En inversant l’ordre des boucles, l’accès devient en stride-1, comme le montre la figure I.11 (b).
Une autre difficulté peut se poser aussi, la transformation peut être illégale, donc il faut d’abord étudier la validité d’appliquer cette transformation, une tâche qui n’est pas simple.
Nous avons vu que le choix d’une seule optimisation est une tâche assez complexe. La difficulté se multiplie énormément lorsqu’on veut avoir une bonne combinaison d’optimisations.
Considérons la multiplication généralisée de matrice (gemm), qui calcule (avec , et sont des matrice, et sont des scalaires). Elle représente un bloc de construction de nombreux algorithmes, notamment les RNCs (Réseau de Neurones Convolutifs). Les implémentations hautement optimisées nécessitent la fusion des boucles de multiplication et d’addition, ainsi que l’application du tuilage de boucles à deux niveaux, de la vectorisation, du déroulage de la boucle, etc. Baghdadi \BOthers. (\APACyear2019).
Générer un code de telle complexité est très difficile. En effet, pour la plupart des programmeurs, il est difficile d’obtenir ce niveau de performance, car l’effort requis pour explorer l’espace d’implémentations possibles est insoluble lors du codage manuel de transformations de code complexe. L’optimisation du code nécessite des connaissances très approfondies dans le domaine ainsi que de l’expertise. À titre d’exemple chez Google, il y a plus de 150 ingénieurs qui écrivent des algorithmes en langage Halide777Halide Ragan-Kelley \BOthers. (\APACyear2013) est un nouveau compilateur et langage spécifique au domaine de traitement d’images, la principale particularité de ce language est la séparation entre les optimisations (schedule) et l’algorithme. non optimisés alors qu’il y a uniquement deux personnes qui disposent de l’expertise nécessaire pour les optimiser. Baghdadi \BOthers. (\APACyear2019).
Par conséquent, le choix des optimisations à appliquer sur un programme est une tâche très fastidieuse qui nécessite la connaissance de l’architecture matérielle, le recensement des avantages et des inconvénients de chaque optimisation. Elle nécessite également des tests sur l’effet de combiner les optimisations et s’assurer que la logique de l’algorithme reste correcte après l’application des optimisations.
Conclusion
Dans ce chapitre, nous avons abordé les principales optimisations de boucles qui permettent d’améliorer les performances et de réduire le temps d’exécution. Nous avons exposé principalement les optimisations de boucles les plus importantes dans l’optimisation de code.
Il est important de noter qu’il ne faut pas appliquer les optimisations sans avoir effectué une étude au préalable, car elles peuvent détériorer les performances. Il faut effectivement prendre plusieurs contraintes en considération comme l’impact de l’utilisation de chaque optimisation à part et l’effet de les combiner. Il faut aussi prendre en considération les propriétés de l’architecture d’exécution (nombres d’unité de traitements, la taille des registres, etc). Quelques optimisations n’ont aucun effet sur le programme mais s’avère très utiles comme prétraitement pour appliquer d’autres. Ainsi, il se trouve que l’optimisation du programme n’est pas une tâche simple. C’est une tâche très complexe et fastidieuse pour les programmeurs. Elle nécessite beaucoup de temps pour trouver la meilleure combinaison d’optimisations qui utilise efficacement les ressources matérielles et améliore davantage le temps d’exécution. C’est pour cette raison, des compilateurs qui permettent d’optimiser le code d’une manière automatique sont apparus. Dans le chapitre suivant, nous allons aborder les approches et les techniques utilisées par les compilateurs pour optimiser le code d’une manière automatique.
Chapitre II Optimisation automatique dans les compilateurs
Introduction
Dans le chapitre précèdent, nous avons exposé les différentes optimisations de codes qui permettent d’améliorer ses performances et d’exploiter efficacement les caractéristiques qu’offrent les architectures matérielles. La tâche de sélection de bonnes optimisations à appliquer est très complexe, elle nécessite une compréhension profonde des optimisations, de leurs effets sur le code ainsi que leurs interactions. Ce qui rend cette tâche très difficile voire infaisable manuellement pour des programmes assez complexes. Des techniques d’optimisation automatique ont été intégrées dans les compilateurs afin d’alléger cette tâche et de trouver les meilleures combinaisons d’optimisations à appliquer sur les programmes. Cependant, l’automatisation mène à résoudre un problème NP-difficile où l’espace de recherche est immense.
Dans ce chapitre, nous allons exposer les différentes approches d’optimisation automatique en comparant les avantages et les défis de chacune d’elle. Nous allons également cité quelques techniques de pointes basées sur ces approches.
II.1 Motivation pour adopter l’optimisation automatique
Pour bien illustrer l’effort nécessaire lors de l’optimisation manuelle par un développeur expert comparativement à l’optimisation automatique, Mullapudi \BOthers. (\APACyear2016) ont recruté deux développeurs professionnels dans le langage Halide Ragan-Kelley \BOthers. (\APACyear2013) pour mettre en défi l’Auto-scheduler de Halide. Les experts ont sélectionné trois benchmarks qu’ils n’ont jamais optimisés auparavant111LENSBlur , NLMeans et MAXFilter. et ils ont implémenté les algorithmes originaux de ces benchmarks sur Halide. Chaque expert a défini des optimisations qu’il a jugées optimales pour chaque benchmark. Tout au long du processus d’optimisation manuelle, les experts ont pris des mesures de performances de leurs optimisations actuelles. Ensuite, ils ont comparé les performances de leur code optimisé manuellement à celui optimisé automatiquement par l’Auto-scheduler de Halide222Le test est effectué sur une machine de 4 cœurs d’Intel E5-2690 proposée par les deux experts..
Les résultats de la comparaison sont illustrés par la figure II.1. L’axe des dans chaque graphe indique le temps de développement (en minutes) des optimisations manuelles. L’axe des montre les performances des benchmarks333Mesuré en tant que débit en pixels.. La ligne horizontale correspond aux performances des optimisations générées par l’Auto-scheduler (en secondes). Les lignes jaune et grise correspondent chacune au progrès de chaque programmeur.
Pour deux benchmarks parmi trois, même après presque une heure de travail, l’optimisation manuelle donne des résultats moins performants que ceux obtenus par l’optimisation automatique (générés en quelques secondes). L’optimisation automatique de codes est compétitive et parfois dépasse l’optimisation manuelle en termes de performances.
II.2 Défis de l’optimisation automatique du code
Le défi majeur du choix des bonnes combinaisons d’optimisations réside essentiellement dans leurs dépendances aux langages de programmation, à la logique des algorithmes et aux architectures matérielles. De plus, les optimisations peuvent dégrader les performances du code dans certains cas. Comprendre le comportement de ces optimisations, leurs effets sur le code source et leurs interactions est un problème de modélisation très complexe qui donne naissance à des thématiques de recherche de pointe. En effet, il existe plusieurs problèmes ouverts dans le domaine d’optimisation des compilateurs : la sélection des optimisations bénéfiques à appliquer, l’estimation des bons paramètres des optimisations (le facteur de tuilage, facteur de déroulage de boucles, etc.) et la définition de l’ordre d’application des optimisations pour avoir les meilleures performances.
II.2.1 Problème de sélection des bonnes optimisations
Le problème est défini en se limitant à la sélection des optimisations bénéfiques pour un programme donné tout en ignorant l’ordre d’application de ces optimisations. Les chercheurs ont montré que l’interdépendance et l’interaction entre l’activation et la désactivation des optimisations dans une combinaison peuvent altérer considérablement les performances du programme en cours d’exécution, même en ignorant leur ordre d’application Ashouri \BOthers. (\APACyear2018).
L’espace de recherche : l’ensemble des optimisations peut être modélisé sous forme d’un vecteur booléen (soit ). Un élément d’optimisation peut avoir deux valeurs : si l’optimisation est activée, sinon () quand elle est désactivée. L’espace de toutes les combinaisons d’optimisations est (voir la figure II.2).
L’espace d’optimisation du problème de sélection est un espace exponentiel. Si les facteurs que peut prendre chaque optimisation sont pris en considération, la version étendue de l’espace d’optimisation dépasse le choix binaire (entre activer/désactiver l’optimisation) à une variante à choix multiples pour chaque optimisation. De plus, certaines optimisations peuvent prendre plusieurs paramètres. Pour simplifier, si nous considérons comme étant le nombre total des options d’optimisations, l’espace devient .
II.2.2 Problème du choix de l’ordre des optimisations (phase-ordering problem)
L’ordre dans lequel les optimisations sont appliquées influence sur l’accélération apportée : le choix d’un ordre d’optimisations prédéfini peut dégrader certaines optimisations qui auraient pu être plus efficaces si un autre ordre a été choisi.
L’espace de recherche : l’espace des choix d’ordre possibles des optimisations est factoriel ( permutations) tel que est le nombre d’optimisations à appliquer. Si de plus, le cas d’une taille variable d’une séquence d’optimisation est pris en considération444Possible de considérer cinq optimisations ou se restreindre à trois ou encore deux, de plus, une optimisation peut être réappliquée plusieurs fois, donc le nombre des optimisations dans la séquence est variable., le cardinal de l’espace devient alors , où représente le nombre d’optimisations possibles et représente la taille maximale que peut prendre une séquence d’optimisations. Même avec des valeurs raisonnables de et de , l’espace de recherche d’optimisations formé est immense. Par exemple, en supposant que et , l’espace de recherche d’optimisations est formé de plus de 11 billions séquences d’optimisations différentes. Le problème de trouver le bon ordre d’optimisations est non déterministe étant donné que la taille d’une séquence d’optimisation est non bornée.
Le problème du choix du bon ordre d’optimisations reste un problème ouvert dans le domaine d’optimisation des compilateurs. L’incapacité des chercheurs à résoudre complètement le problème les conduit à concentrer leurs efforts sur le problème de la sélection de l’ensemble des bonnes optimisations sans prendre en considération le problème d’ordre Ashouri \BOthers. (\APACyear2018).
II.3 Approches d’optimisation automatique du code
L’optimisation automatique du code traite de la génération automatique des codes optimisés en utilisant différents scénarios et architectures. Elle vise à choisir différents facteurs de code qui influencent en maximisant ou en minimisant une fonction objectif Ashouri \BOthers. (\APACyear2018). Différentes approches ont été proposées, chaque approche traite une problématique d’optimisation de code pour répondre à la fonction objectif en favorisant certaines contraintes par rapport à d’autres. Cependant, elles reposent toutes sur deux principales notions : l’espace des optimisations à appliquer et le coût d’application des optimisations. Nous avons donné une classification des approches d’optimisation de code en se basant sur la problématique traitée par chaque classe. La figure II.3 donne une vision globale sur les approches d’optimisation automatique du code.
L’espace des optimisations à appliquer représente les différentes optimisations de code connues, chacune vise à améliorer certaines caractéristiques de performance définies au niveau de la fonction objectif, certaines optimisations nécessitent des paramètres à définir. Cet espace de transformations crée de nombreuses versions du programme. L’exploration de cet espace des optimisations et leurs paramètres peut être coûteuse ce qui mène à définir des approches dédiées pour une exploration plus efficace. D’autre part, le coût d’application d’un sous-ensemble d’optimisations , représenté sous forme d’une instance mesurée de la fonction objectif, permet de décider l’efficacité de et aide à orienter l’exploration.
II.3.1 Approches d’exploration d’espace d’optimisations
L’espace d’optimisations est constitué de différentes optimisations de boucles pouvant améliorer les performances d’un programme si une "bonne combinaison" a été choisie.
L’espace est très souvent grand ce qui impose de définir des approches d’exploration permettant de trouver la solution en un temps raisonnable.
Les approches exposées sont : (1) l’approche exhaustive, (2) l’approche basée sur les heuristiques et les métaheuristiques et (3) l’approche basée sur l’apprentissage automatique.
II.3.1.1 Approche exhaustive
Cette méthode consiste à explorer tout l’espace de recherche en essayant toutes les optimisations possibles afin de trouver la meilleure combinaison. Par exemple, ATLAS une bibliothèque spécialisée dans l’optimisation automatique des programmes de multiplication de matrices. Elle utilise la méthode exhaustive pour trouver certains paramètres d’optimisation pré-élaborés ce qui permet d’améliorer la multiplication de matrices sur l’architecture d’exécution cible. ATLAS explore exhaustivement des plages de valeurs des paramètres d’optimisations pour différentes tailles de matrices. Ensuite, cette bibliothèque sauvegarde les meilleurs modèles d’implémentation du programme pour les différentes tailles de matrices afin de les utiliser ultérieurement pendant l’exécution Whaley \BBA Dongarra (\APACyear1998).
Cette approche permet donc d’essayer toutes les possibilités pour choisir la meilleure avec précision. Cependant, elle requière un temps de traitement énorme exponentiellement lié à la dimension de l’espace d’exploration. Cette approche est utilisée alors pour des espaces restreints qui ont été déjà réduits grâce à un certain mécanise de présélection.
II.3.1.2 Approches d’heuristiques et de métaheuristiques
Plusieurs algorithmes basés sur des heuristiques555Les heuristiques sont des règles empiriques simples basées sur l’expérience. Elles ne donnent pas forcément la solution la plus optimale mais plutôt des solutions assez proches de l’optimale en un temps raisonnable. Elles sont définies pour un type de problème d’optimisation. et des métaheuristiques666Des stratégies d’exploration d’espace de recherche (qui peut être très grand). Elles permettent d’explorer cet espace d’une manière efficace pour se rapprocher de la solution optimale à des problèmes d’optimisation plus généraux. ont été proposées pour résoudre les problèmes d’optimisation. Dans le cas où l’espace de recherche est vaste, la recherche exhaustive, les méthodes itératives ou les méthodes heuristiques simples ne sont pas assez pratiques, alors que les métaheuristiques permettent souvent de trouver de bonnes solutions avec moins d’effort de calcul. L’algorithme génétique (AG) est l’une des métaheuristiques les plus utilisées dans les problèmes d’optimisation automatique du code. Les méthodes d’optimisation automatique basées sur l’algorithme génétique génèrent d’abord une population aléatoire d’individus. Ensuite, les différents opérateurs de l’algorithme génétique (mutation, croisement, sélection, etc.) sont appliqués sur ces individus pendant itérations. La sortie de cet algorithme est la meilleure combinaison d’optimisations explorées.
Cooper \BOthers. (\APACyear1999) ont utilisé l’algorithme génétique pour la sélection des optimisations avec la compilation itérative. Kisuki \BOthers. (\APACyear2000) ont aussi proposé une technique itérative où ils ont utilisé l’algorithme génétique pour la sélection du facteur de tuilage et de déroulage des boucles et ils ont pu montrer que leur méthode peut fonctionner sur plusieurs différentes architectures. Ragan-Kelley \BOthers. (\APACyear2012) ont aussi utilisé l’algorithme génétique pour permettre l’optimisation automatique des programmes écrits dans le langage Halide.
Cependant, les méthodes approchées peuvent dans certains cas donner des solutions qui ne sont pas proches de la solution optimale. Ceci est dû souvent au mauvais choix des hyperparamètres caractérisant les heuristiques et les métaheuristiques notamment. D’autre part, le choix de la méthode la plus appropriée au problème est souvent difficile.
II.3.1.3 Approche d’apprentissage automatique
L’apprentissage automatique est utilisé pour concevoir des modèles de résolution aux principaux problèmes d’optimisation de code : le choix de la meilleure optimisation à appliquer, l’estimation des paramètres pour les optimisations sélectionnées et le choix de l’ordre d’application des optimisations. Les méthodes basées sur cette approche consistent à construire des modèles de prédiction utilisant différentes classes d’algorithmes d’apprentissage automatique. Elles prennent en entrée les caractéristiques du code à optimiser et donnent en sortie la prédiction associée Park \BOthers. (\APACyear2011).
Caractéristiques du programme (program features)
Pour construire le modèle d’apprentissage, le concepteur doit décider les caractéristiques les plus représentatives du programme et qui aident le modèle à apprendre mieux pour donner des bonnes estimations. Les caractéristiques sont représentées sous forme d’une structure (vecteurs, graphes) identifiant distinctement les programmes : plus les caractéristiques sont précises et détaillées plus elles sont représentatives. La représentation sous forme de graphe permet de mettre en relief les dépendances entre les instructions. Par exemple Koseki \BOthers. (\APACyear1997) ont utilisé les graphes comme structure de représentation pour trouver les bons facteurs de déroulage à appliquer sur les boucles. Or, la construction d’une structure volumineuse des caractéristiques est inefficace et peut ralentir les processus ML. Plusieurs projets basés sur l’apprentissage automatique utilisent des techniques d’extraction des caractéristiques des programmes : l’analyse statique des caractéristiques, l’analyse dynamique et l’analyse hybride Ashouri \BOthers. (\APACyear2018).
a) Extraction statique : il s’agit d’extraire les caractéristiques d’un programme à partir de son code source seulement, sans prendre en considération les fonctionnalités du programme en cours de son exécution. Ces caractéristiques peuvent être simples comme le nom de la fonction où plus complexes telles que le nombre des opérations de chargement/stockage en mémoire dans les boucles. Il existe de nombreux extracteurs de caractéristiques de code source utilisés. Par exemple, le framework Milepost GCC MilePost (\APACyear2018) est utilisé en tant qu’un plugin pour le compilateur GCC pour extraire les caractéristiques du code source Ashouri \BOthers. (\APACyear2018).
Agakov \BOthers. (\APACyear2006) focalisent sur l’optimisation de boucles, ils proposent 33 caractéristiques statiques pour décrire les boucles à optimiser (voir figure II.4 (a)), dans le but de sélectionner les cinq meilleures optimisations parmi un ensemble d’optimisations proposé (voir figure II.4 (b)).
Cependant, la caractérisation statique ne décrit pas suffisamment le programme, certains détails relatifs à l’exécution du programme ne sont pas pris en considération alors qu’ils influencent sur le choix des optimisations. L’utilisation de la caractérisation statique génère des modèles qui ne sont pas très précis Park \BOthers. (\APACyear2012).
b) Extraction dynamique : cette analyse consiste à collecter des informations sur le programme en cours d’exécution ce qui permet de caractériser son comportement dynamique sur l’architecture d’exécution. Elle est aussi utilisée pour déterminer comment plusieurs ressources du système sont utilisées. Les machines actuelles offrent des registres pour extraire les caractéristiques des programmes au cours d’exécution : le comportement du cache (nombre de défauts de cache, nombre de succès de cache), le nombre d’erreurs de prédiction de branchement, etc. Pour pouvoir aboutir à cette analyse, il faut exécuter le programme plusieurs fois pour extraire les caractéristiques nécessaires ce qui est gourmand en termes de temps d’exécution. D’autre part, la nature des registres qui diffèrent d’une machine à une autre rend cette analyse non portable entre les plateformes d’exécution. De ce fait, des chercheurs ont proposé d’autres façons de collection dynamique qui peut être portable sur plusieurs plateformes à condition qu’elles disposent de la même structure de jeu d’instructions. Cette nouvelle façon de caractérisation s’appelle l’instrumentation et elle est réalisée à l’aide des outils d’analyse dynamique de programmes Ashouri \BOthers. (\APACyear2018).
Cavazos \BOthers. (\APACyear2007) ont proposé un modèle basé sur ML (un modèle basé sur l’apprentissage automatique hors ligne777Il se fait en temps de compilation.) qui peut être utilisé pour prédire la bonne séquence d’optimisations. Leur méthode utilise cette analyse pour construire le vecteur de caractéristiques des programmes en entrée du modèle.
c) Extraction hybride : la caractérisation hybride consiste à combiner les deux techniques précédentes ce qui permet d’extraire plus d’informations sur le programme au cours d’exécution. Elle s’avère très intéressante car elle prend en considération les différents niveaux de caractérisation. Park \BOthers. (\APACyear2012) ont utilisé la caractérisation hybride pour construire un modèle de prédiction qui aide à choisir efficacement une bonne combinaison d’optimisations. Ashouri \BOthers. (\APACyear2018) ont aussi utilisé le framework MilePost MilePost (\APACyear2018) pour extraire les caractéristiques statiques et MICA888MICA est un outil pour l’extraction des caractéristiques dynamiques des programmes indépendantes de la machine comme la moyenne d’accès aux registres par instruction, la prédiction de branchement. pour extraire les caractéristiques dynamiques.
Cependant, il n’est pas évident de connaître les caractéristiques à retenir et qui aident le plus pour choisir des bonnes optimisations. Parfois, des algorithmes d’apprentissage automatique (tel que l’analyse en composantes principales ) sont utilisés pour réduire la dimension des caractéristiques et se restreindre aux caractéristiques fondamentales pour la construction du modèle Ashouri \BOthers. (\APACyear2018).
Algorithmes d’apprentissage automatique
Plusieurs algorithmes d’apprentissage automatique sont utilisés pour concevoir les modèles de prédiction des bonnes combinaisons d’optimisations. Ils sont classés en trois grandes catégories : apprentissage supervisé, apprentissage non supervisé et autres méthodes (y compris l’apprentissage par renforcement, techniques basées sur les graphes et méthodes statiques). Dans cette section nous allons exposer brièvement l’utilisation de certains algorithmes dde l’apprentissage automatique.
a) Apprentissage supervisé
L’apprentissage supervisé permet l’apprentissage d’une fonction à partir des données étiquetées de l’ensemble d’apprentissage : le modèle reçoit un ensemble d’exemples étiquetés en tant que données d’apprentissage pour apprendre à déterminer les étiquettes de classe pour les nouvelles instances non vues (unseen points). Les modèles linéaires et les SVMs999SVM (Support Vector Machine ou Machine à vecteurs de support) appartient à la catégorie des classificateurs linéaires (qui utilisent une séparation linéaire des données). ainsi que les arbres de décision et les forêts aléatoires sont les algorithmes les plus adoptés pour les problèmes de classification et de régression modélisant l’optimisation automatique du code.
- –
Modèles linéaires et SVMs : les modèles linéaires, à savoir la régression linéaire, l’algorithme des K-plus proches voisins, etc. représentent les méthodes d’apprentissage supervisé les plus populaires. Ils sont généralement très stables i.e les sorties n’enregistrent pas des fluctuations importantes par rapport aux modifications mineures apportées à l’ensemble d’apprentissage. D’autre part, les SVMs utilisent une fonction noyau pour assurer une transformation non linéaire des données vers un espace intermédiaire (feature space). Ceci permet d’appliquer une classification linéaire qui sépare les points vers les différentes classes. Ils sont utilisés dans la construction des modèles pour estimer si une optimisation est bénéfique pour le programme, dans ce cas la sortie appartient à la classe "vrai". Dans le cas contraire la sortie sera "faux" Ashouri \BOthers. (\APACyear2018).
- –
Arbres de décision et forêts aléatoires (Decision Trees and Random Forests) :
il s’agit de trouver un partitionnement des individus à représenter sous la forme d’un arbre de décision. L’objectif est de produire des groupes d’individus les plus homogènes possibles du point de vue de la variable à prédire qui prend une valeur binaire.
Les forêts aléatoires d’arbres décisionnels désignent une famille de méthodes de classification, composée de différents algorithmes d’induction d’ensemble d’arbres de décision. La méthode commence par construire de nombreux arbres de décision au moment de l’apprentissage et fournit en sortie la classe correspondante dans le cas de la classification ou encore, la moyenne des classes dans le cas de la régression. Les forêts aléatoires viennent pour corriger le problème de surapprentissage des arbres de décisions. Monsifrot \BOthers. (\APACyear2002) ont utilisé des arbres de décision pour suivre le comportement de l’optimisation de déroulage afin de décider si l’application de l’optimisation de déroulage de boucles est bénéfique sur une architecture donnée101010Sur les machines UlraSPARC et IA-64..
- –
Réseaux de neurones artificiels : les réseaux de neurones artificiels (RNA), ou Artificial Neural Network en anglais, sont des réseaux constituent d’un ensemble de couches dont chacune est constitue de nombreuses unités élémentaires appelées les neurones. Chaque couche calcule une sortie sur la base des informations qu’elle reçoit. Les réseaux de neurones artificiels constituent, entre autre, une alternative intéressante aux statistiques traditionnelles pour le traitement des données. Les réseaux de neurones sont une façon de construire des modèles paramétriques, c’est-à-dire pour lesquels la fonction objectif est explicite. Contrairement à d’autres algorithmes paramétriques comme la régression linéaire, ils permettent de construire facilement des modèles très complexes et non linéaires.
Les réseaux de neurones sont souvent utilisés pour la construction de modèles de prédiction des optimisations de code. Kulkarni \BBA Cavazos (\APACyear2012) ont proposé une technique qui utilise la neuro-évolution (NEAT)111111NEAT est un algorithme génétique pour la génération de réseaux de neurones artificiels (ANNs). Il représentent des modèles puissants pour l’apprentissage des problèmes complexes, car ils sont capables de changer la topologie du réseau et les paramètres de pondération pour trouver la fonction de coût (fitness) la plus équilibrée. pour construire un réseau de neurones artificiel capable de prédire un ordre des optimisations bénéfique pour une partie de code en cours d’optimisation.
b) Apprentissage non supervisé
L’apprentissage non supervisé regroupe l’ensemble des algorithmes d’apprentissage automatique permettant d’identifier des groupes d’objets ou d’individus similaires (clusters) à partir d’un ensemble de données sans en connaître au préalable la structure (à partir de données non étiquetées) ce qui les distinguent des algorithmes d’apprentissage supervisé.
Les méthodes de partitionnement de données (clustering) représentent la classe des algorithmes les plus utilisés pour l’apprentissage non supervisé, elles permettent de construire des classes automatiquement en se basant sur un critère de similarité entre les données. Le clustering est utilisé pour regrouper les nids de boucles indépendantes afin de préparer le programme à la phase d’optimisation. Il est également utilisé pour réduire l’espace d’optimisation. Par exemple, Martins \BOthers. (\APACyear2016) proposent une technique basée sur le clustering, elle consiste à regrouper les fonctions d’un programme pour réduire l’espace d’exploration des combinaisons d’optimisations, pour chaque groupe, l’espace contient les optimisations précédemment suggérées pour les fonctions qu’il inclut.
Comme toutes les approches déjà exposées, les modèles basés sur le ML présentent des défis relatifs. D’une part, à leurs implémentations car la précision du modèle implique sa complexité. D’une autre part, des défis relatifs à la complexité de la phase d’apprentissage (training) des données, à savoir le type et la masse importante des données et les problèmes du mauvais apprentissage (le surapprentissage121212Le modèle s’adapte bien aux données de traitement (Training Set) et se généralisera mal pour d’autres données non déjà vues. ou encore le sous-apprentissage131313Le modèle s’adapte mal aux données de traitement (Training Set) et n’arrive même pas à capturer ses corrélations : le coût d’erreur en phase d’apprentissage reste grand. du modèle).
II.3.1.4 Comparaison entre les approches d’exploration d’espace des optimisations
Dans le tableau I, nous comparons entre les différentes approches en se basant sur leurs degrés de précision et le temps de calcule pris. Cette comparaison vise principalement à mettre en relief les avantages et les inconvénients de chaque approche.
L’approche exhaustive parcourt tout l’espace de recherche pour avoir la solution. Elle est simple, mais vu qu’elle prend beaucoup de temps, d’autres approches sont apparues. Les méthodes rapprochées (basées sur les heuristiques et les métaheuristiques) permettent de réduire l’espace de recherche par rapport à l’approche exhaustive. Cependant, elles sont lentes et ne garantissent pas de trouver la solution la plus optimale. L’approche basée sur l’apprentissage automatique vient pour permettre la génération des modèles d’une manière automatique grâce à l’apprentissage sur un grand ensemble de programmes pour assurer davantage de précision.
Il est à noter que l’hybridation entre les approches permet de bénéficier à la fois de plusieurs avantages des approches Agakov \BOthers. (\APACyear2006). Très souvent, il s’agit d’utiliser une approche pour résoudre une partie du problème comme la sélection ou l’ordre des optimisations, combinée avec une autre approche pour résoudre d’autres parties du problème comme l’estimation des bons paramètres pour les optimisations sélectionnées.
II.3.2 Approches de sélection des bons paramètres des optimisations
Certaines optimisations de boucles nécessitent un ou plusieurs paramètres qui influencent sur l’efficacité de l’optimisation. La définition de ces paramètres dépend de différents critères, à savoir la hiérarchie mémoire, la complexité du cache, la taille et le nombre des registres vectoriels, etc. D’autre part, l’interdépendance et l’interaction entre les optimisations peuvent être renforcées à cause des paramètres choisis, la figure II.5141414Pour la multiplication de matrices sur une architecture Pentium II et UltraSPARC. montre qu’un léger écart par rapport aux "bons" paramètres des optimisations de tuilage de boucles et de déroulement peut entraîner une augmentation considérable du temps d’exécution voire même un ralentissement par rapport au programme initial Kisuki \BOthers. (\APACyear2000).
L’espace des paramètres des optimisations peut être intégré dans l’espace des optimisations, la technique d’optimisation automatique effectue alors une exploration générale de l’espace résultant, cette approche prend en considération l’interaction entre les paramètres des différentes optimisations. Cependant, l’espace résultant est considérablement grand. La technique risque d’être complexe et lente. De ce fait, les recherches de pointe se sont orientées vers une approche "séparatrice" entre l’espace des optimisations et l’espace de leurs paramètres, traitant ainsi séparément le problème de sélection/ordre des optimisations et le problème d’estimation des bons paramètres pour les optimisations sélectionnées.
Des modèles ont été conçus afin de prédire le meilleur paramètre à utiliser pour une ou plusieurs optimisations en entrée, Les approches adoptées sont principalement : (1) l’approche empirique (estimation par exécution), (2) l’approche statique basée sur un modèle analytique et (3) l’approche basée sur l’apprentissage automatique.
II.3.2.1 Approche empirique (estimation par exécution)
Dans cette approche, une exploration de l’espace des paramètres est effectuée et pour chaque solution une version du programme est générée, ensuite exécutée. Le paramètre permettant d’enregistrer le meilleur temps d’exécution est sélectionné. La bibliothèque d’optimisation automatique des programmes d’algèbre linéaire (ATLAS) effectue une recherche empirique afin d’obtenir le meilleur paramètre dans des plages de valeurs pour des optimisations pré-élaborées.
Kisuki \BOthers. (\APACyear2000) ont utilisé la compilation itérative afin de choisir le meilleur facteur de tuilage et de déroulage de boucles. Cependant, la taille de l’espace de recherche impose des limites sur cette approche. Par exemple, il est impossible de l’utiliser pour trouver le paramètre de tuilage sur des espaces rectangulaires car l’espace de recherche pour le tuilage de boucle rectangulaire est exponentiellement plus grand que celui de tuilage carré Yuki \BOthers. (\APACyear2010).
II.3.2.2 Approche statique basée sur un modèle analytique
Le modèle analytique est conçu en se basant sur l’architecture matérielle et de certains détails sur le comportement d’un ensemble de programmes. Ce modèle statique préconçu donne directement en sortie le paramètre d’une optimisation appliquée sur un programme donné pour une combinaison (architecture, compilateur) spécifique Yuki \BOthers. (\APACyear2010).
Les modèles visent principalement à améliorer l’utilisation de la mémoire cache et les registres. Par exemple, Sarkar \BBA Megiddo (\APACyear2000) ont présenté un algorithme à temps constant qui estime le facteur de tuilage le plus optimal pour les nids de boucles. Ils ont formulé une fonction du coût sous forme d’une équation quadratique qui dépend de la taille du problème et de la taille du cache. Ils ont pu essayer tous les candidats pour les minima locaux de cette fonction en un temps constant.
L’inconvénient majeur de cette approche est la nature statique des modèles analytiques. Les modèles analytiques sont développés sur la base d’une analyse détaillée du programme et de l’architecture. Lorsqu’un nouveau facteur de l’architecture est ajouté, les modèles doivent être mis à jour suite à une analyse exhaustive de ce nouveau facteur. Ceci est très coûteux, car cela nécessite des connaissances spécialisées et éventuellement change toute la conception du modèle Yuki \BOthers. (\APACyear2010).
II.3.2.3 Approche basée sur l’apprentissage automatique
Dans cette approche, un modèle est conçu grâce à des algorithmes d’apprentissage automatique. Cette approche permet de mettre en place plusieurs techniques de pointe d’estimation des paramètres d’optimisations.
Des modèles basés sur la classification utilisant l’algorithme des K-plus proches voisins (KNN) et les SVMs ont été proposés comme outils d’estimation du meilleur facteur de déroulage de boucles, le modèle considère un sous ensemble des paramètres après une restriction en se basant sur les caractéristiques du programme en entrée. Yuki \BOthers. (\APACyear2010) ont utilisé l’apprentissage supervisé, à savoir un réseau de neurones artificiel pour prédire le meilleur facteur de l’optimisation de tuilage. La technique prend en entrée un vecteur de caractéristiques du programme et donne en sortie l’estimation du meilleur paramètre du tuilage de boucles (voir la section II.4.2). Li \BBA Garzaran (\APACyear2008) ont proposé un système de classification basé sur le machine learning pour optimiser la multiplication de matrices. Leur système permet de déterminer le nombre de niveaux de tuilage et la taille de tuilage à chaque niveau selon la plateforme ciblée.
II.3.3 Approches d’estimation de coût
L’estimation du niveau de performance atteinte, suite à l’application des optimisations, est un facteur décisif pour mesurer l’efficacité de ces optimisations sur le programme. Le coût d’application d’une combinaison d’optimisations est relatif aux objectifs151515Autres objectifs tels que la taille du code ou la consommation d’énergie. visés notamment le temps d’exécution du programme optimisé qui représente le principal objectif définissant le coût de l’application des optimisations. De ce fait, tout au long de cette rubrique, le coût des optimisations fera référence exclusivement au temps d’exécution du programme optimisé.
Pour estimer le coût, plusieurs approches ont été proposées, il n’existe pas de meilleure approche puisque chacune adopte un compromis entre certaines contraintes, à savoir la précision, le temps d’exécution et la complexité de l’approche d’estimation Mendis \BOthers. (\APACyear2018).
II.3.3.1 Estimation par exécution
Cette approche permet d’estimer le coût en exécutant réellement le programme. Elle est souvent utilisée dans des domaines exigeant une précision assez élevée. Le principe de cette approche est simple : chaque instance de programme optimisé générée161616Code avec ses optimisations. par l’unité d’exploration d’espace des optimisations, sera compilée et exécutée pour mesurer réellement son temps d’exécution qui sera renvoyé vers une unité de comparaison et de décision afin de classer cette instance par rapport aux autres. Ce processus est répété autant de fois qu’une nouvelle instance est générée. Le critère d’arrêt peut être après un nombre prédéfini d’itérations ou après avoir atteint un coût d’optimisation prédéfini Knijnenburg \BOthers. (\APACyear2002). La figure II.6 illustre le principe de cette approche.
En théorie, cette approche peut être utilisée pour n’importe quel ensemble d’optimisations de compilateur. Cependant, il est recommandé de l’utiliser pour des espaces d’exploration qui ont été déjà réduits. Plusieurs techniques d’optimisation automatique adoptent cette approche telles que les techniques de compilation itérative171717Les transformations successives d’optimisation sont appliquées sur un programme, leurs coûts sont déterminés par l’exécution réelle du code résultant. Plusieurs versions différentes du programme sont générées et exécutées, la version la plus optimale en termes de temps d’exécution est sélectionnée. Knijnenburg \BOthers. (\APACyear2002). Elle est aussi très utilisée pour estimer le coût pour des parcours partiels d’espace de paramètres pour certaines optimisations, notamment les facteurs de tuilage de boucles et du déroulage de boucles Kisuki \BOthers. (\APACyear2000). Elle est également utile dans les cas où l’architecture visée change, car cette stratégie n’impose pas de connaissances sur les plateformes d’exécution.
Cependant, cette approche impose des restrictions parfois bloquantes dans la pratique, les espaces de recherche peuvent être extrêmement grands, d’ailleurs c’est le cas le plus fréquent, ce qui impose un prétraitement de sélection pour restreindre l’espace de recherche. De ce fait, l’efficacité de cette approche dépend étroitement de la technique d’exploration de l’espace des optimisations.
II.3.3.2 Estimation basée sur des méthodes analytiques
Cette approche est basée sur la conception d’un modèle analytique qui peut être un algorithme ou une fonction mathématique capable de prédire automatiquement le coup d’application d’une optimisation sans avoir besoin de l’exécuter.
Le principe général d’un prédicteur analytique consiste à construire une fonction prenant en entrée un tuple , où est le vecteur caractérisant le programme (soit ) à optimiser, représente une des combinaisons d’optimisations possible à appliquer sur . La sortie du modèle est le coût prévu que la séquence T devrait apporter suite à son application sur le programme Ashouri \BOthers. (\APACyear2018). La figure II.7 résume le fonctionnement général de cette approche.
Le modèle est synthétisé par des expressions mesurant le coût en se basant sur les caractéristiques statiques ou dynamiques du code en entrée (voir section II.3.1.3 (A)) ainsi que la taille des données qui influence considérablement le modèle. Le temps d’exécution total est décomposé en général comme suit :
[TABLE]
Où est le temps de calcul passé par le processeur lui-même, est le temps nécessaire pour accéder aux hiérarchies de la mémoire, est le temps de communication interprocessus (les threads) et est le temps écoulé pour effectuer les . Chaque terme de cette équation constitue une formule mathématique exprimée en fonction de valeurs d’entrée du programme, de caractéristiques du programme et éventuellement de quelques paramètres caractérisant la machine cible en se basant, très souvent, sur la documentation du fournisseur Cascaval \BOthers. (\APACyear2000).
Chaque sous-système de la formule principale est traité en détail. En effet, pour estimer , le sous-système compte le nombre d’opérations exécutées dans les unités fonctionnelles du CPU. Les opérations ensuite sont regroupées en classes (blocs) dont l’estimation du coût d’exécution est définie selon les modèles proposés des architectures d’exécution. Le terme est estimé en comptant le nombre des accès pour chaque niveau de la hiérarchie mémoire dont la pénalité est calculée selon différents modèles. Ces derniers prennent en considération les caractéristiques de chaque niveau de la hiérarchie mémoire et se basent essentiellement sur le microbenchmarking181818Des programmes benchmarks utilisés pour estimer les performances du compilateur et de la plateforme d’exécution. Smith \BBA Saavedra (\APACyear1995).
Par ailleurs, les architectures modernes ont des organisations internes très complexes. Les modèles de machine simplifiés, qui prennent en compte des abstractions des architectures réelles, fournissent des estimations de performances souvent très approximatives. En effet, plusieurs contraintes s’opposent à l’approche analytique :
- –
Extension des micro-opérations : chaque instruction est étendue aux micro-opérations dans le processeur. Ainsi, les pipelines, les dépendances et la gestion des ressources se produisent au niveau des micro-opérations, ce qui représente un niveau plus granulaire que les instructions considérées dans les modèles analytiques.
- –
Exécution dans le désordre et les architectures superscalaires : les processeurs qui adoptent une exécution dans le désordre191919En anglais out of order execution consiste à réorganiser l’ordre dans lequel les instructions vont s’exécuter dans le processeur. Ces instructions ne sont alors pas forcément exécutées dans l’ordre dans lequel elles apparaissent dans le programme. exploitent la structure de dépendance des données d’un bloc de base pour mapper son ordre d’exécution optimisant le parallélisme des micro-opérations au niveau des instructions. Les unités d’exécution superscalaires202020Un processeur est dit superscalaire s’il est capable d’exécuter plusieurs instructions simultanément parmi une suite d’instructions. Pour cela, il comporte plusieurs unités de calcul et il est capable de détecter l’absence de dépendances entre instructions. permettent l’exécution simultanée de plusieurs instructions d’une même opération. Le problème d’estimation du coût qui en résulte est donc non linéaire.
- –
Caractéristiques non spécifiées : la documentation vague de certaines caractéristiques micro-architecturales pose un défi supplémentaire. Par exemple, lorsque l’exécution dans le désordre est spécifiée, la taille du tampon de réorganisation peut ne pas l’être. Ce qui mène à considérer des estimations vagues pour les modèles proposés Mendis \BOthers. (\APACyear2018).
II.3.3.3 Estimation basée sur l’apprentissage automatique
Cette approche permet de prédire le coût des optimisations sans exécution du programme optimisé. En général, l’approche prend en entrée un tuple où est une structure caractérisant le programme (voir section II.3.1.3 (A)) et est l’une des séquences possibles d’optimisations à appliquer, la sortie ( soit ) est le coût du tuple estimé grâce à un ou plusieurs algorithmes de l’apprentissage automatique.
Pour concevoir l’estimateur, plusieurs algorithmes du machine learning ont été utilisés. Nous exposons une approche basée sur les réseaux de neurones artificiels qui ont donné de bon résultats d’estimation dans plusieurs techniques de pointe.
Les modèles basés sur les réseaux de neurones exploitent les avantages qu’ils offrent. En effet, les réseaux de neurones permettent de prédire le temps d’exécution d’un programme en entrée dont les caractéristiques sont modélisées sous forme d’une structure (vecteur, graphe, etc.). La phase d’apprentissage (training) permet au réseau d’apprendre en corrigeant ses différents coefficients. L’ensemble de données d’apprentissage (DataSet) est composé des couples de (caractéristiques de , temps d’exécution de ) avec un programme généré aléatoirement (pour éviter le surapprentissage ou encore le sous-apprentissage du modèle).
Le passage d’une couche du réseau à une autre se fait grâce à des fonctions dont le choix influence sur les performances du modèle, la profondeur et le nombre des nœuds dans chaque couche présentent aussi des hyperparamètres critiques, ces deniers peuvent être réglés grâce à des tests de performance du modèle effectué pour chaque hyper-paramètre. La figure II.9 résume les principales composantes du modèle.
Mohammed \BOthers. (\APACyear2010), dans leur technique de sélection du meilleur paramètre de l’optimisation du tuilage de boucles (TSS), ont utilisé les réseaux de neurones pour estimer le temps d’exécution des programmes. Le modèle de prédiction est constitué de trois couches. Il reçoit en entrée les caractéristiques du programme ainsi que les trois facteurs de tuilage et donne en sortie le temps d’exécution prédit du programme pour chaque facteur. Cette estimation permet donc de sélectionner le meilleur facteur de tuilage.
D’autres techniques de pointe basées sur l’utilisation des réseaux de neurones utilisent les réseaux de neurones récurrents (RNNs) pour prédire le débit d’un ensemble d’instructions de bloc de base (voir la section II.4.3). Le bloc d’instruction est représenté par un graphe orienté acyclique (DAG212121Directed Acyclic Graph un graphe orienté qui ne possède pas de circuit. Un tel graphe peut être vu comme une hiérarchie.). Ce mécanisme de représentation est inspiré des techniques de traitement de langage naturel.
Les modèles basés sur l’apprentissage automatique ont pu atteindre des performances remarquables. Cependant, ces modèles sont très sensibles aux hyperparamètres qu’ils utilisent, les ensembles de données d’apprentissage (problème de sous- apprentissage et surapprentissage) ainsi que la définition des caractéristiques représentatives du programme ( Features engineering). La conception de ces modèles doit être méticuleusement étudiée au niveau de chaque phase.
II.3.3.4 Comparaison entre les approches d’estimation de coût
Afin de comparer les trois approches d’estimation du coût, il faut préciser les critères à considérer. Il s’agit de la précision, le temps d’exécution et la complexité de l’estimateur.
- –
La précision : l’estimateur du coût doit être précis. Tout le processus du choix des optimisations dépend de cette estimation. En effet, à une échelle d’ordre aussi petite, l’erreur doit être minimale car si l’estimateur présente un taux d’erreur important, la prédiction des bonnes optimisations sera erronée et la méthode d’automatisation sera par la suite fausse.
- –
Temps d’exécution de l’estimateur : L’estimateur doit être rapide. En effet, un estimateur fait partie de tout un processus d’automatisation. L’exploration d’espace de recherche, qui est assez grand, risque de prendre un temps important. Si de plus, l’exécution de l’estimateur prend un temps considérable, la méthode d’automatisation sera trop lente et inutile.
- –
Complexité de l’estimateur : l’estimateur ne doit pas être trop complexe, c’est-à-dire que l’estimateur ne doit pas dépendre de plusieurs prétraitements et paramètres qui risquent d’influencer sa précision. D’autre part, la complexité d’un estimateur implique l’intervention de plusieurs contributeurs et nécessite très souvent des outils plus complexes.
Pour concevoir une méthode d’optimisation automatique, le choix de l’approche d’estimation du coût se fait selon les contraintes favorisées. La taille de l’espace des optimisations à appliquer et leurs paramètres peuvent orienter le concepteur. Ceci impose des limitations sur la précision de l’approche. De ce fait, plusieurs travaux ont adopté une phase de présélection pour réduire l’espace de recherche Triantafyllis \BOthers. (\APACyear2005). Le tableau II résume la comparaison entre les trois approches d’estimation de coût.
II.4 Exemples de techniques d’optimisation automatique
Plusieurs techniques d’optimisation automatique ont été intégrées dans les compilateurs. Elles ont permis d’apporter des améliorations remarquables sur leurs fonctionnements et ce, sur différents niveaux, à savoir l’accélération de l’exploration d’espace des optimisations, la sélection des meilleurs paramètres des optimisations et l’estimation du coût des programmes optimisés. Dans cette section, nous allons exposer trois techniques. Chacune propose une solution pour automatiser un de ces trois niveaux.
II.4.1 Autoscheduler de Halide basé sur l’approche analytique
L’auto-scheduler Mullapudi \BOthers. (\APACyear2016) est une technique de génération automatique des Schedules222222Équivalent à ”Planning” en français, ce terme représente l’ensemble des optimisations à appliquer sur le code Halide.. Elle permet de résoudre le problème du temps perdu lors de la compilation et d’exécution de chaque schedule candidat pour un espace de recherche immense. Il s’agit alors d’une estimation des performances de chaque schedule. En entrée, l’auto-scheduler reçoit le programme à optimiser, des informations complémentaires sur le programme tel que l’étendue des boucles de chaque fonction et la taille estimée des images ainsi que l’architecture de la machine (taille du registre vectoriel, coût du chargement mémoire) (voir la figure II.10).
Le processus de l’Auto-scheduler passe par quatre phases. D’abord, la phase de pré-calcul de chaque fonction, à savoir l’estimation de son coût arithmétique. Ensuite, l’Auto-scheduler forme des groupes de fonctions232323Une fonction Halide est équivalente à une boucle imbriquée dont la profondeur est supérieure ou égale au nombre de variables manipulées par cette fonction. liées par des relations producteur-consommateur242424Le résultat de calcul de la fonction productrice est utilisé dans le calcul de la fonction consommatrice., chaque groupe est optimisé indépendamment afin d’augmenter la localité et maximiser la réutilisation de données dans chaque groupe. Il s’agit d’appliquer des transformations de tuilage sur chaque fonction et de définir les points optimaux pour fusionner les fonctions producteur-consommateur. Enfin, il faut définir l’ordonnancement des groupes pour assurer une meilleure localité. Puis, dérouler les fonctions, les vectoriser et enfin paralléliser les boucles externes des fonctions dans chaque groupe. L’algorithme suivant résume les phases du modèle de l’auto-scheduler Mullapudi \BOthers. (\APACyear2016).
L’Auto-Scheduler de Halide a été testé par un ensemble de 14 benchmarks différents sur la même architecture d’exécution252525Intel Xeon E5-2620 v3 CPU.. Dans 8 parmi 14 des benchmarks, la version optimisée à la main a donné des performances meilleures que celle optimisée par l’Auto-Scheduler, mais les écarts entre les performances sont réduits, l’Auto-Scheduler reste très compétitif à l’optimisation manuelle (voir la figure II.11).
II.4.2 Modèle automatique pour le problème de Tile Size Selection (TSS)
Une bonne estimation des paramètres des optimisations améliore profondément l’accélération qu’elles peuvent apporter. Yuki \BOthers. (\APACyear2010) présentent une technique de création des modèles assez précis de prédiction des meilleurs paramètres pour l’optimisation de tuilage de boucles (TSS)262626Tile Size Selection est le problème de sélection du meilleur paramètre pour l’optimisation de tuilage des boucles. en se basant sur les réseaux de neurones artificiels. La technique a été conçue pour une classe de programmes spécifiques souvent utilisés dans l’algèbre linéaire (des nids de boucles d’une profondeur allant jusqu’à trois dimensions et des matrices de données à deux dimensions). Le principe de cette technique est résumé dans les deux sections suivantes.
II.4.2.1 Caractéristiques du code considérées
Le tuilage de boucles vise principalement à maximiser la réutilisation des données et à éviter les accès mémoire coûteux. La technique prend en entrée des caractéristiques des programmes qui décrivent la localité temporelle et spatiale en s’appuyant sur l’aspect du pré-chargement (prefetching) qui consiste à récupérer les données avant la demande en parallèle avec le chargement des données voisines, ce qui permet de gagner un temps d’exécution considérable.
Les caractéristiques sont basées sur le nombre de références dans les instructions les plus internes du nid de boucle. Les références sont classées en trois types : les références pré-chargées272727(Prefetched references) qui sont les références bénéficiant de la localité spatiale créée grâce au prefetcher., les références non pré-chargées282828(Non-prefetched references.), à savoir les références qui ont besoin de la localité temporelle pour obtenir de bonnes performances. et les références constantes dans la boucle la plus interne (invariant), c’est-à-dire celles qui sont réutilisées pour toutes les itérations de la boucle la plus interne. Chaque type de référence est ensuite classé selon son mode d’accès : en lecture ou en écriture, ce qui fait un total de six types de caractéristiques.
II.4.2.2 Phases de conception et principe de fonctionnement
La technique utilisée comporte les quatre étapes suivantes :
- –
Génération aléatoire des programmes correspondant à la classe de programmes définie.
- –
Collecte de données qui consiste à extraire les caractéristiques des programmes et les exécuter pour concevoir l’ensemble d’apprentissage, et ce, sur différentes architectures.
- –
Entraînement du modèle TSS grâce à l’ensemble d’apprentissage en réduisant l’erreur de prédiction. Les entrées du réseau de neurones sont données à la première couche cachée et les sorties de chaque couche sont données à la couche suivante. Les sorties des couches cachées sont en fonction de la somme pondérée des entrées de cette couche, où la fonction est la tangente hyperbolique. La couche de sortie effectue la somme pondérée des sorties de la dernière couche cachée, mais n’applique pas la fonction de tangente hyperbolique. Compte tenu des pondérations (coefficients synaptiques) de la couche de sortie, de la dernière couche cachée , de la sortie souhaitée et du nombre de données d’apprentissage , chaque nœud de la couche de sortie tente à minimiser l’erreur calculée par de l’équation .
- –
Une fois le modèle TSS conçu, il peut être utilisé en tant que partie du compilateur pour prédire les tailles de tuilage optimales sur le plan interne, ou en tant que partie d’une méthode d’exploration pour trouver les tailles de tuilage optimales.
II.4.3 Ithemal, estimateur du coût
Ithemal (Instruction Throughput Estimator using Machine Learning) est un estimateur de débit d’un ensemble d’instructions de bloc de base à l’aide de l’apprentissage automatique, il peut être intégré dans les compilateurs afin d’estimer le temps d’exécution pour choisir les meilleures optimisations du code.
Ithemal utilise une nouvelle approche basée sur un réseau de neurones récurrents sous forme de graphe acyclique dirigé (DAG-RNN292929Recurrent Neural Networks. en anglais, c’est une classe de réseaux de neurones artificiels ayant des connexions récurrentes, qui dote le réseau de mémoire.). Il modélise l’estimation du débit à l’aide d’un réseau de neurones profonds (DNN). Il adopte donc une approche guidée par les données303030Data Driven Approach en anglais, regroupe les différentes approches de résolution qui se basent sur l’étude des données. ce qui permet de basculer facilement d’une microarchitecture à une autre avec un minimum de paramétrage manuel Mendis \BOthers. (\APACyear2018).
II.4.3.1 Architecture et fonctionnement d’ithemal
La technique permet d’estimer le débit d’un bloc de base d’instructions en assembleur. Les blocs passent par trois principales phases : la canonicalisation, le plongement et la prédiction (voir la figure II.12).
canonicalisation (canonicalization)
Dans cette phase, Ithemal prend un bloc en assembleur spécifié en tant que texte (syntaxe Intel) et le mappe en tant que texte aussi à une liste d’instructions. Chaque instruction consiste en un code opération, une liste d’opérandes de source et une liste d’opérandes de destination. La canonicalisation rend explicite les opérandes qui sont généralement implicites dans la représentation en code assembleur. La figure II.13 expose un exemple de cette transformation.
Plongement (Embedding)
Cette phase prend un bloc de base canonique et produit une représentation du bloc de base compréhensible par un réseau de neurones. En effet, les réseaux de neurones prennent typiquement en entrée une séquence d’entrées à valeurs réelles. Dans les domaines structurés, tels que le texte ou, comme dans notre domaine, les programmes et les entrées sont de nature discrète (tels que les mots et les blocs de base). Il est donc nécessaire de mapper chaque entrée structurée sur une représentation pouvant être consommée par un réseau de neurones. Ithemal mappe un bloc de base sur un graphe acyclique dirigé avec des vecteurs à valeurs réelles comme contenu de chaque nœud. Chaque nœud du graphe correspond à une instruction du bloc de base. Chaque nœud est associé à un vecteur (à n dimensions) à valeur réelle. Un arc dirigé lie un nœud à un nœud si l’instruction de dépend de (déterminée grâce à l’analyse des opérandes de source de et des opérandes de destination de ). La création du vecteur à dimensions pour chaque nœud est basée sur la technique de traitement de langage naturel pour mapper une séquence de jetons textuels (en l’occurrence le code opération, les opérandes de source et les opérandes de destination) en une représentation détaillée sous la forme d’un vecteur Mendis \BOthers. (\APACyear2018).
Prédiction
La phase de prédiction prend en compte le graphe acyclique dirigé d’un bloc de base et prédit son débit. Les réseaux de neurones récurrents (DAG-RNN) sont les plus adéquats pour cette structure en entrée. Le DAG-RNN d’Ithemal parcourt le graphe dans l’ordre topologique, en calculant une représentation vectorielle profonde à valeur réelle de chaque sous-graphe connexe dans le graphe. Étant donné les vecteurs de chaque sous-graphe, Ithemal réduit ces vecteurs en un seul vecteur, puis effectue la prédiction à l’aide d’une régression linéaire.
II.4.3.2 Utilisation du réseau de neurones récurrents (RNN)
Un RNN prend en entrée une séquence de vecteurs et produit en sortie une séquence de vecteurs. Un RNN est dit récurrent si son exécution est définie de manière récursive tout au long de la séquence. Les nœuds interconnectés interagissent non-linéairement. Les unités sont reliées par des arcs (synapses) qui possèdent un poids. La sortie d’un neurone est une combinaison non linéaire de ses entrées. À chaque étape, le RNN applique une cellule313131Cell en anglais est une fonction avec des paramètres internes apprenables qui, lorsqu’ils sont évalués à la position dans la séquence, consomment un vecteur d’entrée et un vecteur d’état caché , à partir de la position précédente, puis produisent un nouveau vecteur caché (vecteur d’état ). pour produire le vecteur de sortie de cette étape. Le calcul des cellules dépend du vecteur de sortie de l’étape précédente. Le dernier vecteur qu’un RNN calcule résume donc la séquence entière. Dans son implémentation, Ithemal utilise une cellule LSTM323232Long Short-Term Memory (LSTM) est un type de réseaux de neurones récurrents. Hochreiter \BBA Schmidhuber (\APACyear1997) qui mémorise de manière sélective les informations qui lui ont été transmises par la cellule précédente Mendis \BOthers. (\APACyear2018).
La dernière étape d’Ithemal consiste à calculer l’estimation de débit à l’aide d’une régression linéaire. Etant donné un vecteur d’état caché réduit obtenu après la réduction de la sortie du RNN, Ithemal calcule la prédiction du débit par la formule , où est le vecteur de paramètres et est le biais.
Conclusion
Dans ce chapitre, nous avons expliqué les différentes approches adoptées pour résoudre les trois principaux problèmes liés à l’optimisation automatique, à savoir la sélection des meilleures combinaisons d’optimisations, l’estimation des meilleurs paramètres pour les optimisations sélectionnées et l’estimation du coût d’application des optimisations (le temps d’exécution en l’occurrence).
Chacune des approches présente des avantages et des défis. Une bonne définition des spécifications du problème donne plus de priorité à certaines contraintes, ce qui oriente le choix de l’approche à utiliser.
Les techniques d’optimisation automatique du code dépendent très souvent du compilateur visé. Dans la partie suivante, nous allons exposer le compilateur Tiramisu qui fera sujet de notre contribution par la suite.
Chapitre III Le compilateur Tiramisu
Introduction
Générer des codes efficaces dédiés aux systèmes de hautes performances devient de plus en plus difficile. Ceci dû aux contraintes d’hétérogénéité des plateformes d’exécution d’une part et de la complexité des algorithmes utilisés d’une autre part.
Afin de remédier à ces difficultés, maints langages spécifiques au domaine (LSD) ont été proposés. Ces langages définissent des spécifications pour répondre en particulier aux contraintes d’un domaine d’application précis.
Tiramisu est un Langage Spécifiques au Domaine développé par l’équipe de recherche COMMIT de MIT Baghdadi \BOthers. (\APACyear2019) capable de générer des codes optimisés dans lesquels les optimisations n’affectent pas le fonctionnement ni la lisibilité du code grâce à la séparation entre les algorithmes et les optimisations appliquées.
Dans ce chapitre nous allons présenter le langage et le compilateur Tiramisu en exposant ses avantages. Nous donnons une vue d’ensemble sur la compilation des codes en Tiramisu afin de mettre en relief l’effet de la représentation du code sur son optimisation et aussi sur sa portabilité. Ensuite, nous expliquons sa logique en exposant comment les algorithmes sont définis et comment les optimisations sur le code sont introduites.
III.1 Tiramisu
Tiramisu est un LSD (Languages Spécifiques au Domaine) embarqué111Langage spécifique à un domaine défini en se basant sur un ”langage hôte” plus puissant à usage général. il s’appuie sur l’infrastructure du langage hôte (analyse syntaxique, vérification typographique, modularité), le langage hôte peut être utilisé pour la métaprogrammation (l’écriture de programmes manipulant des programmes en DSL). sur le C++ permettant d’exprimer des algorithmes dits de données parallèles222Ces algorithmes sont appelés des algorithmes de données parallèles car leur parallélisme provient d’opérations simultanées sur de grands ensembles de données, plutôt que de plusieurs threads de contrôle. qui utilisent des tableaux denses et des nids de boucles. Ces algorithems sont souvent utilisés dans des systèmes de hautes performances, à savoir l’algèbre linéaire dense, l’algèbre tensorielle, le traitement d’images et les réseaux de neurones (RNCs). Tiramisu a été conçu afin de couvrir quatre principales caractéristiques d’optimisation de codes BenRomdhane (\APACyear2017) qui sont :
- –
Cibler différentes architectures matérielles.
- –
Gérer la dépendance de données. En effet l’optimisation d’un programme est limitée par les dépendances entre les représentations des données en mémoire, notamment dans le cas des programmes ciblant différentes architectures.
- –
Générer un code optimisé de hautes performances. En effet, les programmeurs doivent optimiser leurs codes manuellement ou encore automatiquement afin d’obtenir des résultats comparables aux codes optimisés soigneusement par des experts du domaine d’optimisation.
- –
Assurer une représentation compréhensible et lisible du code optimisé. Très souvent, l’application des optimisations sur un programme risque d’affecter sa représentation, obombrer sa logique voire même la changer, d’où la nécessité d’une vérification du programme après optimisation.
III.1.1 Modèle en couche de Tiramisu
Tiramisu est basé sur le modèle polyédrique333Permet d’avoir une représentation mathématique abstraite pour modéliser un programme. Chaque instruction du programme est représentée par 3 principales informations : domaine d’itération, relations d’accès (en lecture, écriture) et un Schedule. Pour davantage de détails voir PRADELLE (\APACyear2011).Bondhugula \BBA Hartono (\APACyear2008). Il utilise une représentation intermédiaire (RI)444Représentation intermédiaire que prend le code de haut niveau. multicouche assurant une séparation complète entre l’algorithme pur, les optimisations de code, le mappage et la structuration des données ainsi que la gestion de la communication et la synchronisation. L’avantage de cette représentation gît essentiellement dans la séparation entre les couches, ce qui définit un ordre spécifique dans lequel les optimisations sont appliquées. Cette représentation garantit que le compilateur passe d’une couche à une autre sans vérifier les modifications ou annuler des décisions déjà prises dans des couches précédentes.
En effet, l’optimisation de code pour différentes architectures est restreinte par certains facteurs liés notamment à la dépendance en mémoire. Toutes les opérations de synchronisation, de communication et de mappage des données vers les différentes hiérarchies mémoire ne doivent pas être effectuées avant l’application des optimisions de code. Pour illustrer cette complexité, prenons l’exemple du mappage des buffers vers la mémoire partagée et la mémoire cache des GPU, les quantités de données à transmettre et la synchronisation des opérations dépendent étroitement des optimisations de code appliquées comme les niveaux de l’optimisation de tuilage de boucles.
Tiramisu sépare la représentation du code en quatre couches que nous détaillons dans les sections suivantes.
III.1.1.1 Couches d’algorithme abstrait
Dans cette première couche, l’algorithme pur est spécifié indépendamment de la localité temporelle et spatiale : aucun ordre des calculs n’est donné et aucune restriction sur le stockage des données n’est imposée. Les valeurs sont communiquées grâce à une relation producteur-consommateur.
III.1.1.2 Couche de gestion des computations
Cette couche permet de définir l’ordre d’exécution des calculs, l’architecture d’exécution cible ainsi que les optimisations à appliquer sans pour autant préciser la structuration de données. Ceci, facilite considérablement l’application des différentes optimisations. En effet, les transformations requises pour l’application des optimisations sont plus souples puisque elles ne nécessitent pas des transformations complexes sur la structure de données.
III.1.1.3 Couche de gestion de données
Au niveau de cette couche, les emplacements mémoire pour stocker des valeurs calculées sont définis grâce aux commandes de mappage des données, à savoir l’allocation/libération des tampons et les relations d’accès aux données en lecture ou en écriture pour chaque opération de calcul. Les mappages de données possibles en Tiramisu sont représentés par des structures de tableaux, des tableaux de structures et des tableaux multidimensionnels réduits en tableaux de dimensions minimales ou en scalaires.
III.1.1.4 Couche de gestion de communication
Les commandes de synchronisation et de communication sont ajoutées au niveau de cette couche. Il s’agit d’annoter d’une dimension de temps la représentation obtenue après la couche de gestion de données. Ceci est réalisé grâce à des commandes de synchronisation spécifiées manuellement par l’utilisateur. La Figure III.1 donne une vue globale sur Tiramisu en mettant en relief le flux entre les différentes couches de la représentation intermédiaire en Tiramisu.
III.1.2 Avantages de Tiramisu
Le compilateur Tiramisu permet une application plus souple des différentes optimisations de code et sur des niveaux de code séparés, spécifiques et ordonnés BenRomdhane (\APACyear2017). Ceci est grâce à la séparation entre l’algorithme et les optimisations à appliquer sur le code. De ce fait, Tiramisu offre la possibilité de tester les différentes combinaisons d’optimisations sur le même code ou encore d’automatiser cette exploration, ce qui n’est pas du tout facile dans d’autres langages comme le langage C.
En effet, la composition de deux optimisations nécessite la réécriture de l’optimisation résultante. Ceci s’avère complexe à fortiori dans le cas de plusieurs optimisations. Tiramisu permet une simple composition des optimisations dans une partie séparée complètement de l’algorithme sous forme de commandes d’optimisations. Cette séparation permet d’empêcher la réécriture du résultat de la composition des optimisations, la gestion se fait automatiquement par Tiramisu.
Pratiquement, tous les autres compilateurs polyédriques imposent des restrictions afin d’assurer que les codes après optimisations sont justes. Tiramisu permet de vérifier la validité des optimisations appliquées grâce à l’analyse de dépendance. De ce fait, il permet d’utiliser sans restrictions des optimisations souvent considérées difficiles à appliquer notamment sur des espaces d’itération non rectangulaires, ou encore sur des graphes de flux de données cycliques.
Actuellement, Tiramisu est capable de générer des codes optimisés pour différentes architectures matérielles à savoir, CPU, GPU, FPGA ou encore des systèmes distribués. Ceci en utilisant la même syntaxe tout en s’assurant de tirer profit des avantages qu’offre chaque architecture.
III.2 Programmer en Tiramisu
Tiramisu est un générateur de code. L’objectif d’un programme Tiramisu est de générer des codes censés être appelés à partir d’autres programmes (les programmes utilisateurs). Chaque programme Tiramisu commence par l’initialisation du compilateur Tiramisu. Cela permet aussi de définir le nom de la fonction. Cette fonction sera donc appelée dans un programme appelant (wrapper) qui peut être écrit en Tiramisu ou encore dans un autre langage Ray (\APACyear2018).
Afin d’assurer une représentation compréhensible des programmes écrits en Tiramisu, il est recommandé d’organiser le code en deux principales sections correspondant à la forme générale du modèle en couches de la représentation intermédiaire. Dans la première étape, le programmeur définit l’algorithme pur. Dans la deuxième étape, (le Schedule555Équivalent à ”Planning” en français, ce terme représente l’ensemble des optimisations à appliquer sur le code, Schedule est le terme à utiliser tout au long du rapport.) il décrit comment le code sera optimisé. Ensuite, il détermine les allocations et le stockage des résultats dans les buffers. Et pour clore chaque programme Tiramisu, il faut lancer la commande de génération du code. Pour décrire une vue globale, un code Tiramisu peut être représenté par le schéma de la figure III.2.
III.2.1 Algorithmes dans Tiramisu
Dans cette première partie, le programmeur décrit la logique de son algorithme grâce à des instructions particulières appelées computations. Ces dernières représentent des nids de boucles (voir la section I.2.1) dont la profondeur est égale au nombre des variables (itérateurs) qui lui ont été affectées. Chaque itérateur affecté à un niveau de boucle possède une borne fixée lors de sa déclaration.
Une computation peut être vue comme étant une expression associée à un domaine d’itération. L’expression représente le calcul à effectuer. Le domaine d’itération est défini grâce aux bornes affectées aux itérateurs du domaine Baghdadi \BOthers. (\APACyear2019). Dans la figure III.3, la computation permet d’exprimer une boucle d’une profondeur de deux avec et comme itérateurs du domaine ayant la borne et respectivement.
Tiramisu est destiné aux algorithmes de données parallèles qui manipulent les tableaux denses et les nids de boucles. Ainsi, les programmes Tiramisu sont constitués de plusieurs computations chacune assure des traitements particuliers dans une boucle "pour" (for) imbriquée, souvent de grande profondeur. Notons que seules les boucles "pour" et la structure conditionnelle peuvent être exprimées. Les boucles while et les goto ne sont pas encore utilisables en Tiramisu, ce qui oriente d’autant la spécialisation au domaine caractérisant les algorithmes exprimés en Tiramisu Baghdadi \BOthers. (\APACyear2019).
L’ordre d’exécution des computations est indépendant de l’ordre de leurs déclarations. C’est au niveau de la partie Schedule où le véritable ordre est défini et donc les relations entre les computations sont prescrites. La définition de l’ordre des computations ne doit pas briser les relations producteur – consommateur qui existent entre les computations.
Par exemple, pour calculer le produit de deux matrices et soit , puis calculer la somme du résultat et la matrice D soit . les deux computations présentent une relation de producteur – consommateur, avec C comme producteur et E comme consommateur. La figure III.4 représente le code Tiramisu (partie algorithme) équivalent. Notons que pour initialiser les inputs, il faut créer des computations dédiées de type Input.
Dans cette première partie du code, seuls les traitements relatifs à la logique de l’algorithme sont définis, aucune spécification est donnée sur l’ordre d’exécution ni sur la structuration des données ni encore sur les optimisations à appliquer. Pour définir ces critères, l’utilisateur doit les préciser dans la partie Schedule Ray (\APACyear2018).
III.2.2 Schedule dans Tiramisu
Tiramisu propose un ensemble de commandes de scheduling666Commandes de planification en Français, nous optons pour le terme ”scheduling” pour désigner Planification tout au long du rapport. de haut niveau pour définir l’ordre des computations et optimiser leurs exécutions. Les commandes d’optimisation permettent d’effectuer des transformations sur le domaine d’itération d’une façon transparente pour le programmeur. Cette opération facilite la combinaison de commandes d’optimisation. Le programmeur prescrit pour chaque computation, les commandes d’optimisation à appliquer avec les paramètres nécessaires (voir figureIII.5).
Les commandes de scheduling sont classées en quatre principaux types : les commandes de transformation des nids de boucles, les commandes pour mapper les niveaux des boucles sur l’architecture matérielle, les commandes de manipulation des données et les commandes de synchronisation Baghdadi \BOthers. (\APACyear2019). Pour davantage de détails, nous proposons dans l’annexe B une description des types de commandes de scheduling. Le tableau XX dans l’annexe B expose quelques commandes de scheduling, en définissant pour chaque commande sa syntaxe d’application, ses paramètres, son principe et son type également.
III.2.2.1 Amélioration de l’optimisation du code en Tiramisu
L’optimisation de code en Tiramisu est basée sur un ensemble de commandes paramétrables de haut niveau assurant la flexibilité et le contrôle total au programmeur. Or, la spécification d’un Schedule optimal manuellement nécessite d’avoir une bonne expertise et d’effectuer plusieurs tests sur les optimisations choisies pour différents paramètres.
La définition automatique du Schedule s’avère intéressante pour alléger cette tâche. D’ailleurs, plusieurs compilateurs des langages spécifiques au domaine proposent une gestion automatique des optimisations tel que Pencil Baghdadi \BOthers. (\APACyear2015) et Halide Ragan-Kelley \BOthers. (\APACyear2013). Ce dernier, étant proche à Tiramisu puisque il sépare entre l’algorithme et le Schedule, propose un auto-scheduler générant automatiquement le Schedule susceptible d’être le meilleur. Le compilateur Tiramisu supporte la scalabilité de ses fonctionnalités et permet d’adopter des méthodes pour gérer la partie Schedule automatiquement.
Conclusion
Tiramisu est un nouveau langage qui offre l’avantage de séparer entre l’algorithme, les optimisations à appliquer et la structuration des données. Ceci permet de gérer des codes rapides visant plusieurs architectures matérielles.
Dans ce chapitre, nous avons présenté le compilateur Tiramisu, expliqué sa logique et ses fondements. Nous avons aussi exposé les principales notions relatives à la programmation en Tiramisu, à savoir la séparation entre les parties de l’algorithme et de Schedule.
La partie état de l’art est close. Nous entamons par la suite la partie contribution, dans laquelle nous allons expliquer en détails les différentes phases de conception, implémentation et tests du système proposé.
Contribution
Chapitre IV Conception et réalisation
Introduction
Notre contribution s’inscrit dans le cadre des projets de recherche lancés par l’équipe COMMIT du laboratoire CSAIL111CSAIL : laboratoire d’informatique et d’intelligence artificielle (Computer Science and Artificial Intelligence Laboratory). à MIT visant à optimiser le compilateur Tiramisu.
L’objectif principal de notre travail est la réalisation d’un modèle de prédiction des meilleurs facteurs de l’optimisation de déroulage (loop unrolling) pour des programmes déjà optimisés (manuellement ou automatiquement) ou encore pour des programmes naïfs sans aucune optimisation préalable. Le modèle permet donc d’automatiser le choix du meilleur facteur de déroulage pour faciliter la tâche d’optimisation et améliorer le temps d’exécution du programme. La classe des programmes Tiramisu visée dans notre travail présente un taux assez élevé d’opérations de chargement mémoire considérées comme opérations gourmandes en temps d’exécution.
Dans ce chapitre, une description détaillée du problème traité est donnée suivie des détails sur les différentes phases de conception de notre solution.
IV.1 Description du problème
Dans le chapitre III, une explication détaillée de la structuration de programmes Tiramisu est donnée. Pour rappeler, les programmes en Tiramisu sont composés de deux principales parties : la première partie contient le code de l’algorithme et la deuxième partie (Schedule) contient l’ensemble des optimisations à appliquer sur le programme (voir la figure IV.1).
Différentes combinaisons d’optimisations peuvent être appliquées dans la partie Schedule. Les optimisations appliquées peuvent prendre des facteurs comme l’optimisation de déroulage de boucles, l’optimisation de tuilage, etc. Le programmeur doit sélectionner les optimisations ainsi que les meilleurs facteurs à appliquer pour améliorer le temps d’exécution.
IV.1.1 Portée de la contribution
Notre contribution se focalise sur l’optimisation de déroulage de boucle (voir section I.2.7 et l’annexe A pour plus de détails sur l’optimisation de déroulage de boucles) qui améliore les performances dans pratiquement tous les cas si elle est appliquée d’une manière significative, à savoir appliquée avec un bon facteur Bacon \BOthers. (\APACyear1994).
Le tableau III montre la différence entre le temps d’exécution pour trois facteurs de déroulage (8, 16, 32) appliqués sur le même programme présenté dans la figure IV.1
Actuellement, en Tiramisu, la définition du bon facteur de déroulage se fait manuellement. Le programmeur exécute le programme pour différents facteurs de déroulage et choisit le facteur qui donne un temps d’exécution meilleur. L’exécution du programme pour les différents facteurs coûte énormément de temps. Il s’agit d’explorer exhaustivement les différents facteurs et d’exécuter pour chaque facteur plusieurs exécutions afin de mesurer le temps pris pour chaque configuration (voir figure IV.2).
L’objectif principal de notre travail est d’automatiser le choix du meilleur facteur de déroulage et d’éviter l’exécution répétitive. La solution proposée doit permettre de prédire le meilleur facteur de déroulage pour un programme donné qui peut être déjà optimisé ou naïf sans aucune optimisation préalable. Pour atteindre cet objectif, il est nécessaire de définir une formalisation (codification) du problème ainsi qu’une fonction objectif.
IV.1.2 Formulation du problème
Considérer le problème traité comme étant un problème d’optimisation combinatoire222Les problèmes d’optimisation combinatoire traitent le choix d’une meilleure alternative dans un ensemble très grand mais fini d’alternatives dites solutions réalisables. La solution permet de satisfaire une fonction objectif. Une évaluation est associée à toute solution réalisable à l’aide d’une fonction dont la minimisation/maximisation définit la fonction objectif. permet de définir le cadre formel du problème. Soit le problème d’optimisation traité, caractérisé par un ensemble réalisable ou admissible non-vide et une fonction qui associe un scalaire dans à chaque élément (solution réalisable) pour un programme donné. Soit le programme à optimiser, est composé de deux partie : la partie qui constitue l’algorithme et la partie qui constitue les optimisations (Schedule). Le programme est représenté par un ensemble de caractéristiques .
est donc l’ensemble des facteurs de l’optimisation de déroulage de boucles (loop unrolling) du programme , tel que : . Les paramètres et dépendent de . Ils donnent les contraintes qui permettent de définir l’ensemble des solutions réalisables du problème.
La fonction associe à chaque solution réalisable pour le programme son temps d’exécution. Résoudre le problème revient à trouver parmi les solutions réalisables, une qui minimise i.e. trouver une solution telle que pour tout élément . Une telle solution est dite optimale, c’est la solution qui permet de minimiser le temps d’exécution de .
La fonction objectif est définie comme suit:
[TABLE]
IV.1.3 Choix conceptuels étudiés
Afin de proposer une conception qui vérifie aux objectifs du projet, il est important de répondre à certaines questions pour mieux cerner nos choix conceptuels :
- –
Quel est l’espace de recherche à considérer ?
- –
Quelle approche à adopter pour la prédiction du meilleur facteur de déroulage ?
- –
Quel est le type de sortie à prédire ?
IV.1.3.1 Espace de recherche
Les facteurs de déroulage sont des valeurs discrètes (des entiers) qui doivent être des multiples de deux. L’optimisation de déroulage fait partie d’un Schedule regroupant plusieurs autres optimisations comme le tuilage (Loop tiling). Les facteurs de l’optimisation de tuilage appliqué dans Tiramisu sont impérativement des puissances de deux. Si nous appliquons le déroulage avec autres facteurs que les multiples de deux, des problèmes lors de la compilation dans Tiramisu peuvent apparaître. D’autre part, selon les experts, les facteurs de déroulage qui donnent les meilleures performances varient entre 2 et 64. Le facteur 64 est le plus grand facteur exploré manuellement jusqu’à maintenant.
Comme nous allons prédire automatiquement, nous pouvons aller au-delà de cette valeur. Cependant, d’après les tests que nous avons effectués, les valeurs qui dépassent 128 détériorent les performances, et risquent de créer des bugs dans certaines architectures d’exécutions.
En effet, l’optimisation de déroulage réplique le code plusieurs fois selon le facteur donné, elle crée autant d’instructions que le facteur de déroulage. Donc, de grands facteurs augmente la taille du code. Ainsi, les instructions ne peuvent pas être chargées d’une façon optimale dans le cache, les registres aussi ne seront pas exploités efficacement, ce qui détériore les performances au lieu de les améliorer (pour davantage de détails voire l’annexe A).
Conclusion : l’espace de recherche que nous devons explorer est représenté par les valeurs discrètes paires , avec : < < avec = 128 et = 0 ( 0 représente le cas où l’optimisation de déroulage n’est pas appliquée).
IV.1.3.2 Approche de prédiction du facteur de déroulage
La définition de l’approche d’exploration d’espace de recherche constitue une phase de conception cruciale. Dans la section II.3, plusieurs approches ont été exposées : la recherche exhaustive, les heuristiques et les métaheuristiques ainsi que les approches analytiques. Elles ont été déjà utilisées dans plusieurs travaux traitant ce problème. Chacune présente des avantages et des inconvénients. Le concepteur doit favoriser soit la précision ou le temps d’exécution de la méthode.
L’apprentissage automatique a prouvé son efficacité dans la résolution de plusieurs problèmes. Il propose un compromis entre la précision et le temps d’exécution de la méthode. La disponibilité d’un nombre de données suffisant et la puissance de calcul des machines actuelles ont permis d’intégrer ces techniques dans divers problèmes complexes d’optimisation dans les compilateurs.
En effet, Stephenson \BBA Amarasinghe (\APACyear2005) et Georgios Zacharopoulos (\APACyear2018) ont exploré les méthodes d’apprentissage automatique pour le choix du meilleur facteur de déroulage et ils ont pu atteindre une bonne précision (jusqu’à 60%). Les méthodes précédentes utilisent des techniques basées sur les réseaux de neurones peu profonds (Shallow Neural Networks) ou basées sur l’apprentissage automatique classique.
De notre part, nous voulons explorer un nouvel angle du problème de prédiction du meilleur facteur de déroulage par apprentissage automatique afin d’améliorer la précision. Nous avons opté pour les réseaux de neurones profonds qui ont fait preuve d’une bonne précision dans divers domaines. Nous souhaitons donc les explorer pour résoudre notre problème.
De plus, contrairement aux techniques classiques d’apprentissage automatique, les réseaux de neurones profonds ont la particularité de créer automatiquement des caractéristiques haut niveau (high-level features) à partir des caractéristiques bas niveau (low-level features) que nous allons utiliser pour représenter les programmes.
Conclusion : le modèle de prédiction du facteur de déroulage est appuyé sur l’approche basée sur l’apprentissage automatique, plus précisément les réseaux de neurones profonds (pour plus de détails sur les réseaux de neurones voir annexe C).
IV.1.3.3 Classe de réseau de neurones profond
Le théorème du no-free-lunch montre qu’aucun algorithme ou modèle résout parfaitement tous les problèmes. Dans notre cas, le choix du type de réseau de neurones dépend de plusieurs paramètres notamment la nature des contraintes du problème ainsi que les données d’apprentissage. La meilleure approche de sélection du type de réseaux de neurones est d’essayer d’identifier dans le modèle des contraintes présentes dans le problème traité, puis tester les types qui sont plus susceptibles de résoudre le problème. Nous avons effectué une étude comparative entre les trois classes de réseaux de neurones les plus stables et utilisées (voir annexe C), le tableau IV résume les résultats obtenus.
Les entrées de notre modèle sont de tailles variables, si nous avons par exemple une boucle avec quatre niveaux et une autre avec deux niveaux, nous aurons au moins deux entées supplémentaires pour la boucle à quatre niveaux.
Les RNNs supportent les entrées de tailles variables, ils sont utilisés pour des données séquentielles, ce séquencement est défini par le temps. Les RNNs utilisent la sortie prédite par l’entrée précédente et l’entrée actuelle pour produire la sortie actuelle. Cependant nos données ne présentent aucun séquencement, chaque entrée de notre dataset est indépendante de l’autre. En effet, le facteur de déroulage prédit pour le programmei ne sera pas utilisé avec les caractéristiques du programmei+1 pour prédire son meilleur facteur de déroulage. L’architecture des RNNs ne convient pas à notre problématique.
La taille variable des entrées peut être fixée à une taille maximale, en utilisant la technique du rembourrage où l’entrée est remplie par des valeurs bidons jusqu’à atteindre la taille maximale. Le rembourrage ou le padding permet de résoudre le problème de la taille variable des données en entrée des réseaux de neurones. Le zéro padding est la solution la plus reconnue pour la résolution du problème de la taille variable des entrées. Cependant, d’autres valeurs peuvent être utilisées. Généralement les deux valeurs "0" et le "-1". Le « -1 » peut remplacer le "0" si le "0" présente une valeur significative dans les données d’entrée du modèle.
Conclusion : selon le type de problème traité et la représentation des caractéristiques des programmes constituant les données utilisées, les Réseaux de neurones multicouche MLPs semblent être la solution la plus convenable.
IV.1.3.4 Type de sortie du modèle
Il existe plusieurs types de sorties possibles pour un modèle de prédiction de déroulage. Le modèle peut prédire le temps d’exécution des instances de programmes en prenant en entrée les caractéristiques du programme. Une fonction doit estimer le minimum des temps d’exécution pour les facteurs dans l’espace d’exploration. Le modèle doit aussi apprendre cette fonction. Cependant, dans le cas des réseaux de neurones, la fonction est difficile à entraîner et elle risque très souvent d’être piégée par les optimums locaux.
L’autre sortie possible est le facteur de déroulage optimal lui-même ; trouver le meilleur facteur de déroulage directement permet de remédier au problème des optimums locaux.
Conclusion : nous avons décidé de prédire directement le meilleur facteur de déroulage.
IV.1.3.5 Classification ou régression ?
Le modèle de prédiction du meilleur facteur de déroulage peut représenter un problème de classification ou un problème de régression.
a) Classer les programmes selon le facteur optimal de déroulage : la classification444Le problème de classification consiste à attribuer à chaque individu (objet) une classe ou une étiquette. est utilisée lorsque la variable de sortie est une valeur discrète qui représente une catégorie (les différents facteurs de déroulage : 2, 4, 6, 8, etc. dans notre cas). Cependant, la classification ne permet de classer les programmes que dans un ensemble prédéfini de classes. Il faut avoir suffisamment de programmes qui couvrent les différentes classes afin d’atteindre une bonne prédiction pour des nouveaux programmes.
b) Utiliser une fonction continue (Régression555Le problème de régression consiste à prédire une valeur réelle à partir d’un ensemble d’entrées.) : un problème de régression se pose lorsque la variable de sortie est une valeur réelle ou continue. Dans notre problème, le modèle de régression renvoie des valeurs réelles que nous devons arrondir en valeurs entières multiples de deux (les facteurs doivent être des entiers multiples de deux). Si la sortie est un nombre impair, il faut prendre soit son successeur ou son prédécesseur. En revanche, le nombre choisi risque de ne pas être la valeur la plus convenable pour le programme en entrée ce qui influence négativement la précision du modèle.
D’une autre part, la théorie des réseaux de neurones montre qu’ils sont aussi puissants dans la régression que dans la classification. Cependant, dans la pratique, il y a quelques différences en termes de précision. Dans la classification, il faut uniquement décider la classe convenable à l’entrée. Mais, les problèmes de régression sont plus difficiles car il s’agit de prévoir une certaine valeur pour chaque entrée.
Par conséquent, l’utilisation des réseaux de neurones pour les problèmes de régression peut être moins stable comparativement aux problèmes de classification. Il est généralement préférable de convertir les problèmes de régression en problèmes de classification lorsque cela est possible. Cependant, les réseaux de neurones restent toujours très puissants pour traiter des problèmes de régression, la difficulté serait dans l’optimisation des traitements du modèle. En effet, quelques techniques d’optimisation s’avèrent difficiles à appliquer dans les cas des modèles de régression (ex.dropout666Le décrochage, ou abandon est une technique de régularisation permet une suppression temporaire de neurones afin d’éviter le surapprentissage.).
Conclusion : La classification est la solution la plus convenable à notre problème, il faut juste s’assurer que l’ensemble de données d’apprentissage couvre toutes les valeurs de l’espace de recherche défini.
IV.2 Conception globale du système
Afin de donner une vue globale de la solution proposée, nous allons exposer l’architecture globale du système proposé, définir la classe des programmes visée et décrire les différents composants de la solution.
IV.2.1 Architechture globale du système
L’utilisateur définit la partie algorithme et éventuellement la partie Schedule contenant les différentes optimisations possibles hormis l’optimisation de déroulage de boucle (loop unrolling). L’utilisateur appelle le modèle de prédiction proposée, ce dernier analyse le code et décide le meilleur facteur de déroulage à appliquer. L’architecture globale du système est présentée dans la figure IV.3.
- •
**Module d’extraction des caractéristiques de programmes **: ce module permet d’extraire les caractéristiques (features) du programme en entrée . L’ensemble des caractéristiques est constitué essentiellement des informations sur les niveaux de boucles, les opérations effectuées ainsi que les caractéristiques des optimisations (Schedule ) appliquées. Ceci permet de représenter les programmes Tiramisu distinctement. Plus de détails sont données dans la section IV.3.1.
- •
Module de prédiction du facteur de déroulage: le module prend en entrée les caractéristiques extraites par le module précédent pour prédire le meilleur facteur de déroulage . Ce facteur est utilisé par le compilateur Tiramisu pour compléter le Schedule du programme.
IV.2.2 Classe de programmes visée
Tiramisu est dédié aux programmes dits de données parallèles qui utilisent des matrices denses et des nids de boucles. Notre équipe travaille sur la génération de programmes optimisés de calculs scientifiques comme l’algèbre linéaire dense. La classe des programmes visée est constituée de boucles à contrôle affine ACLs (Affine Contol loops), à savoir les bornes des nids de boucles et les adresses des accès en mémoire sont définies comme étant des fonctions affines des itérateurs de boucles et des paramètres constants.
Dans cette classe de programmes, nous avons visé des nids de boucles parfaitement imbriquées (voir section I.2.1) qui présentent des chargements de données en mémoire multiples et intenses. Nous focalisons notre travail sur les programmes composés d’une seule computation, à savoir un seul nids de boucles qui n’a pas été fusionné avec d’autres nids de boucles. La figure IV.4 donne un exemple de la classe des codes utilisés.
Le choix de cette classe de programmes est basé sur la nature des domaines visés par Tiramisu. En effet, cette classe présente des noyaux pour divers programmes dédiés aux calculs scientifiques. De plus, les chargements mémoire sont des opérations coûteuses en temps d’exécution, l’amélioration en temps d’exécution apportée grâce à l’application de déroulage de boucle avec un bon facteur est remarquable.
IV.2.3 Caractéristiques des programmes
Un programme en Tiramisu représente un ensemble de nids de boucles parfaitement imbriqués appelés computations (voir chapitre III). Le module d’extraction des caractéristiques représente chaque computation par un ensemble de caractéristiques synthétiques . Dans certains travaux précédents, un nombre considérable des caractéristiques est utilisé. Ils utilisent des caractéristiques décrivant l’effet de l’architecture d’exécution sur les nids de boucles ce qui augmente le nombre des caractéristiques extraites. Cependant, un grand nombre de caractéristiques risque de nuire la prédiction du modèle. En effet, sélectionner les caractéristiques les plus significatives permet d’optimiser la prédiction du meilleur facteur de déroulage.
Initialement, nous avons opté pour un grand nombre de caractéristiques. Ensuite, nous avons gardé les caractéristiques qui influencent le plus la prédiction. L’abstraction adoptée résume les critères influençant le temps d’exécution d’une computation indépendamment de l’architecture d’exécution ce qui offre plus de portabilité au modèle. Elle décrit principalement la structure, les opérations et les optimisations appliquées sur le nid de boucles.
Les caractéristiques des optimisations appliquées sur une computation peuvent être classées en deux principales classes : optimisations locales et optimisations globales. Les optimisations locales agissent uniquement sur la structure du nid de boucles sur lequel elles sont appliquées telles que l’optimisation de tuilage de boucles, déroulage de boucles, inversion de boucles, parallélisation, etc. Les optimisations globales agissent sur l’ensemble des computations, elles mettent en relation plusieurs computations telles que la fusion de nids de boucles, ordonnancement d’ordre de calcul des opérations dans les nids de boucles (after(), before(), compute_at(), etc.).
Le tableau V donne un sous-ensemble des caractéristiques des programmes considérées.
IV.3 Conception détaillée
Dans cette section, nous allons exposer les détails de la conception des modules composant notre solution. En effet, la solution proposée est basée sur deux principaux modules : le module d’extraction des caractéristiques des programmes et le module de prédiction du facteur de déroulage.
IV.3.1 Module d’extraction des caractéristiques des programmes
Ce module permet d’extraire les caractéristiques des unités composant les programmes Tiramisu, à savoir les nids de boucles (computations). L’extraction des caractéristiques d’une computation passe par deux phases, la première phase consiste à extraire les caractéristiques décrivant la structure du nid de boucles, l’ensemble des opérations effectuées ainsi que les types des données. La deuxième phase consiste à enregistrer les caractéristiques des optimisations appliquées (schedule) sur le nid de boucles pour ensuite, les utiliser dans la mise à jour des caractéristiques de la structure du nid de boucles.
IV.3.1.1 Extraction initiale des caractéristiques de la computation
Afin d’extraire initialement les caractéristiques définissant la structure d’une computation, le module commence par parcourir la liste des itérateurs puis les expressions (opérations) Tiramisu associées au nid de boucles. L’expression associée à une computation en Tiramisu est un arbre n-aire dont les nœuds sont des expressions aussi (voir figure IV.5). Dans notre cas, les expressions qui nous intéressent sont de type opération (les opérations arithmétiques et les opérations d’accès mémoire). L’exploration de l’arbre qui représente l’expression permet d’extraire les caractéristiques des opérations effectuées dans le nid de boucles.
L’optimisation de déroulage est fortement sensible à l’étendu de chaque niveau du nid de boucle, il influence également l’effet des opérations effectuées notamment les chargements mémoire101010Les chargements mémoire représentent la classe d’opérations la plus utilisée dans les programmes visés.. Pour accentuer cette relation nous avons défini des caractéristiques décrivant la quantité de donnée ("mots mémoire selon le type de données) chargée pour chaque variation d’un niveau de boucle. D’autre part, ces caractéristiques, décrivant les accès mémoire, dépendent de l’ordre des itérateurs lors des accès comparativement à l’ordre des niveaux de la computation(voir l’algorithme 2).
IV.3.1.2 Mise à jour et exportation des caractéristiques de la computation
Suite à l’application de chaque optimisation dans la partie Schedule du programme, le module d’extraction doit enregistrer cette optimisation en gardant trace de son type, ses facteurs et les niveaux de boucles sur lesquels elle a été appliquée. L’optimisation modifie la structure du nid de boucles d’où la nécessité de mettre à jour les caractéristiques de la computation. Par exemple l’application de l’optimisation de tuilage de boucle change d’une part, les étendus des niveaux des boucles ainsi que leurs ordres. D’une autre part, elle modifie les caractéristiques relatives à l’opération d’accès.
Après la mise à jour des caractéristiques, le module les prépare sous un format utilisable par le réseau de neurones. Il s’agit d’une représentation vectorielle où chaque caractéristique unitaire (nombre de niveaux, nombre des opérandes, type de donnée, etc.) représente une entrée (input) au réseau de neurones.
Le module d’extraction est composé de trois sous modules : un module d’extraction initiale des caractéristiques de chaque computation, un second module pour l’extraction des caractéristiques du schedule et la mise à jour des caractéristiques de la computation après l’application du schedule et un troisième module pour l’exportation des caractéristiques sous le format utilisable par le réseau de neurones (voir figure IV.6)
IV.3.2 Modèle de prédiction du meilleur facteur de déroulage
Nous avons conçu notre modèle de prédiction d’une manière itérative. Nous avons commencé par un modèle de réseau de neurones de base, puis nous avons ajouté des améliorations à chaque itération afin d’atteindre une bonne précision. Les améliorations apportées ont été à base des tests et des résultats de la précision du modèle à chaque fois. Le processus d’amélioration est arrêté une fois la précision désirée est atteinte.
Dans cette section, nous allons présenter les principales étapes suivies.
IV.3.2.1 Architecture du réseau de neurones
Nous avons utilisé le réseau de neurones pour construire un modèle de classification supervisée. Il permet la prédiction du meilleur facteur de déroulage. Dans un modèle de classification, les sorties (les classes) sont prédéfinies. Il reçoit un ensemble de données d’apprentissage (training) étiquetées pour apprendre à classer les nouveaux programmes en entrée. L’architecture du réseau de neurones est basée sur l’architecture typique des réseaux de neurones multicouche (voir figure IV.7). Nous définissons dans les sections suivantes le nombre des couches cachés et le nombre de neurones dans chaque couche (voir IV.7).
Le modèle doit prédire la sortie parmi les classes définies qui représentent la plage de valeurs possibles du facteur de déroulage (voir section IV.1.3.1).
Dans les sections qui suivent, nous présentons les notions décrivant le fonctionnent du réseau de neurones proposé (voir l’annexe C pour plus de détails sur les réseaux de neurones). Nous détaillons les différentes étapes pour la génération du modèle de prédiction du facteur de déroulage.
IV.3.2.2 Propagation de l’information
La couche d’entrée du réseau est alimentée par les caractéristiques du programme (voir section IV.2.3), ses sorties sont attribuées à la première couche cachée qui, à son tour, passe ses sorties à la couche suivante et ainsi de suite. Ce processus est appelé la propagation de l’information. La sortie de chaque couche cachée est le résultat de l’application d’une fonction à la somme pondérée des sorties des couches précédentes à laquelle un biais est ajouté. Cette fonction est appelée la fonction d’activation (voir la figure IV.8).
Essentiellement, les fonctions d’activation (ou fonctions de transfert) convertissent le signal d’entrée en un signal de sortie. Elles produisent une sortie non linéaire à partir d’une entrée linéaire. Ceci permet de représenter des fonctions plus complexes. Il existe différents types de fonctions d’activation, pour notre modèle nous considérons la fonction Relu comme fonction d’activation pour les couches cachées, elle retourne le max entre le [math] et la valeur d’entrée ( ).
La sortie de chaque couche est donnée par . La sortie du réseau est fournie directement par , tel que sont les poids, sont les entrées et est le vecteur de biais associé.
Comme il s’agit d’un problème de classification en classes multiples (nombre de classes > 2), nous utilisons la fonction pour la couche de sortie. La fonction permet de calculer la distribution de probabilités des différentes classes. Initialement, le modèle effectue le traitement avec des valeurs de pondération aléatoires qui sont mises à jour à chaque itération afin de minimiser l’erreur.
La fonction de perte (loss function) permet de calculer l’erreur de prédiction. Le choix de cette fonction dépend de type du problème traité. Dans le problème de classification, nous utilisons Cross-Entropy111111Connue aussi sous le nom log loss., elle permet de mesurer l’erreur de probabilité dans le cas où les classes sont mutuellement exclusives. Cela signifie que chaque entrée appartient à une et une seule classe. Dans notre cas, l’optimisation de déroulage peut avoir un seul facteur optimal. L’erreur commise est généralement mesurée pour un ensemble de données (dataset), donc nous calculons la moyenne de l’erreur commise pour l’ensemble d’apprentissage fourni.
IV.3.2.3 Rétropropagation de l’erreur
Une fois le processus de propagation de l’information est terminé, le réseau de neurones procède à la correction des poids afin de minimiser l’erreur autant que possible. Le gradient de l’erreur calculée est rétro-propagé pour mettre à jour les poids en fonction du degré de leur contribution à l’erreur. Nous appelons ce processus la rétropropagation du gradient Goodfellow \BOthers. (\APACyear2016).
La valeur des poids mis à jour est contrôlée par un paramètre appelé le taux d’apprentissage (learning rate). La modification apportée aux poids du réseau pour une erreur donnée est souvent de l’ordre ou ou encore moins. Il existe plusieurs algorithmes utilisés pour mettre à jour les poids et donc minimiser l’erreur (les algorithmes d’optimisation) tels que l’algorithme de la descente de gradient stochastique (SGD), l’algorithme ADAM et l’algorithme RMSprop Ruder (\APACyear2016). En fonction de l’algorithme choisi, certains paramètres doivent être ajustés tels que le taux d’apprentissage. Nous avons opté pour ADAM Optimiser Bock \BOthers. (\APACyear2018) comme algorithme d’optimisation, car il est connu pour sa robustesse et son efficacité. Le taux d’apprentissage pris pour cet algorithme est suite à plusieurs tests.
Les poids dans les réseaux de neurones peuvent être mis à jour à partir des erreurs calculées pour chaque ligne de données de traitements. Il s’agit de l’apprentissage en ligne. Cala permet de faire des mises à jour rapides mais parfois cause des effets chaotiques au réseau. Une autre alternative consiste à enregistrer l’erreur au niveau de tous les exemples du dataset d’entraînement. Ensuite, les mises à jour sont effectuées vers la fin. Il s’agit de l’apprentissage par lots (batch learning) qui est souvent plus stable. Étant donné que le nombre d’exemples dans le dataset est très grand, la taille du lot est réduite afin d’augmenter l’efficacité de calcul. Nous considérons des lots batch de taille de 100.
Le processus est répété pour toutes les données du dataset d’entraînement. Chaque passe sur l’intégralité de l’ensemble de données pour entraîner le réseau de neurones est appelée une itération (epoch). Le réseau de neurones peut être entraîné des dizaines, centaines, voire même des milliers d’itérations afin d’améliorer la précision. Le nombre d’itérations est un paramètre à choisir soigneusement. Nous considérons un nombre d’itérations pour lequel aucune amélioration n’est apportée au modèle.
IV.3.2.4 Génération et préparation de données
Le réseau de neurones est alimenté par un ensemble de données dont chaque élément représente les caractéristiques d’un programme donné et son facteur de déroulage optimal . Ce dernier est obtenu en exécutant le programme pour toutes les valeurs possibles dans l’espace d’exploration .
Cependant, le temps d’exécution d’un programme est influencé par plusieurs évènements externes relatifs au système et à l’architechture d’exécution (ordre d’instructions choisi par la machine, taille de cache alloué, les attentes entrées/sorties, etc.). Nous considérons alors une estimation de la moyenne du temps d’exécution pour chaque programme. Selon la loi des grands nombres121212La loi affirme que la moyenne empirique, calculée sur les valeurs d’un échantillon, converge vers l’espérance lorsque la taille de l’échantillon tend vers l’infini. Dans notre cas, ceci signifie que pour un grand nombre N d’exécutions, la moyenne des temps d’exécution enregistrés tend vers la moyenne réelle du temps d’exécution du programme, le nombre d’exécutions (soit ) doit être grand pour avoir une bonne estimation. Nous avons considéré la valeur minimale de qui permet d’avoir une stabilité du facteur de déroulage optimal . Après avoir effectué plusieurs tests, nous avons pris .
La génération des programmes se fait grâce à l’outil Tiramisu_Code_Generator (voir la section IV.6). Il s’agit de générer aléatoirement des programmes appartenant à la classe des programmes visée (voir section IV.2.2). Les schedules des programmes Tiramisu générés contiennent des combinaisons d’optimisations suivantes : tuilage de boucles (à deux et à trois niveaux), inversion de boucles et la parallélisation. Le module d’extraction des caractéristiques est ensuite utilisé pour extraire le vecteur de caractéristiques pour chaque programme généré.
Les données fournies aux réseau de neurones doivent être numériques. Elles peuvent être redimensionnées (normalisées) dans la plage comprise entre 0 et 1. Les données peuvent être aussi standardisées de tel sorte que chaque colonne soit centrée et réduite. Les données de notre dataset sont toutes numériques mais nécessitent une mise à l’échelle, nous allons donc les normaliser. Le Dataset est divisé en trois parties, chacune est utilisée pour une phase de création du modèle.
- –
Dataset d’entraînement ou Training set ( de l’ensemble de données d’origine). Nous utilisons ce dataset pour entraîner notre modèle (trouver les bons poids et biais pour le modèle).
- –
Dataset de validation ( de l’ensemble de données d’origine) : ce dataset est utilisé pour minimiser le surapprentissage. Il ne contribue pas directement dans la modification des poids, il permet uniquement de vérifier que toute augmentation de la précision par rapport à l’ensemble de données d’apprentissage entraîne une augmentation de la précision par rapport à un ensemble de données qui n’a pas encore été montré au réseau, ou au moins le réseau ne l’a pas encore utilisé pour s’entraîner. Si la précision sur l’ensemble de données d’entraînement augmente, mais la précision sur l’ensemble de données de validation reste la même ou diminue, cela implique que le modèle sur-apprend et donc nous devons arrêter l’entraînement.
- –
Dataset de test ou Test set ( de l’ensemble de données d’origine) : nous l’utilisons une fois le modèle finit l’entraînement. Ce dataset est utilisé uniquement pour tester la solution finale afin de confirmer la précision réelle du réseau.
Il est fortement déconseillé que la phase de test soit ignorée. En effet, l’algorithme qui prédit bien pendant la phase de validation ne signifie pas forcément que c’est le modèle le plus convenable au problème traité. Au cours de la phase de test, le modèle final est utilisé pour prédire de données non déjà vue. Donc, si la précision du modèle est très mauvaise pendant le test, tout le processus de conception du modèle doit être remis en cause (voir la figure IV.9).
IV.3.3 Itérations de construction du modèle de prédiction
Nous avons suivi un processus itératif afin d’arriver à la conception finale de notre modèle. La conception passe par trois principales itérations.
IV.3.3.1 1ère itération : modèle de réseau de neurones de base
Le but de cette étape est de bien cerner les entrées et les sorties du modèle. Nous avons expliqué dans la section IV.2.3 qu’initialement, le nombre de caractéristiques décrivant chaque nid de boucle est considérable. Il faut définir les caractéristiques les plus significatives pour la prédiction. D’autre part, le nombre de classes de sortie du modèle est grand. Nous avons fait une étude à priori sur un échantillon de programmes pour définir les classes qui ne seront pas utiles dans notre problème (voir section IV.1.3.1).
Le premier entraînement du modèle a été effectué sur un dataset de 1500 éléments générés toute au long de deux semaines suite à une exploration exhaustive de tous les Schedules possibles pour 20 programmes(partie algorithme). Évidement, La précision enregistrée a été médiocre car le dataset considéré est très petit et ne présente que 20 fonctions avec leurs schedules. En revanche, nous avons pu constater des points très importants.
- –
Quelques colonnes (features) du dataset ont la même valeur pour toutes les lignes comme la colonne qui représente le niveau d’application de l’optimisation de parallélisation, elle ne prend que la valeur ’1’131313La valeur ’1’ veut dire que la parallélisation est appliquée sur ce niveau de boucle sur toute la colonne ou que le ’0’141414La valeur ’0’ veut dire que la parallélisation n’est pas appliquée sur ce niveau car le niveau d’application de l’optimisation de parallélisation est le même (le niveau le plus profond). En effet, ce genre de colonnes ne porte aucune information utile au modèle, il faut donc les enlever.
- –
Les valeurs de certaines colonnes présentent une distance considérable par rapport à d’autres (d’ordre de ). Par exemple, la colonne du nombre d’opération d’accès mémoire (unitaire) par rapport à celle de l’étendue de boucles. Même en effectuant la normalisation, la distance entre les colonnes reste la même. Pour remédier à ce problème, il faut redimensionner ces colonnes en les divisant par par exemple pour avoir des valeurs plus petites.
- –
Pour chaque programme (partie algorithme), les lignes qui le représentent dans le dataset ont plusieurs colonnes communes. La différence entre ces lignes touche seulement les colonnes décrivant la partie schedule. Ce qui diminue la diversification dans le dataset et influence ainsi la précision du modèle.
- –
Le nombre de classes est très grand (64 classes) par rapport aux données que nous pouvons générer. Plus le nombre de classe est grand, plus la taille du dataset nécessaire et qui couvre toutes les classes est grand. Générer un dataset aussi immense nécessite des super calculateurs. Ceci impose des contraintes sur le nombre des classe à considérer, a fortiori, si le modèle enregistre une mauvaise précision, il serait difficile de changer la stratégie et lancer encore la génération pour avoir un nombre suffisant de données.
De ce fait, nous avons effectué une étude statistique afin de déterminer la marge de classes à éliminer. La figure IV.10 montre la distribution des données du dataset sur les classes.
IV.3.3.2 2ème itération : sélection de bons hyperparamètres du modèle
Les résultats de la première itération nous ont mené à remettre en question l’architecture du modèle ainsi que la nature de données à générer. Concernant le problème de diversification des données nous avons décidé de réduire le nombre de Schedules générés pour le même programme (partie algorithme) à 10 Schedules aléatoires. Le nombre de classes pose également un grand problème par rapport au nombre de données possible à générer compte tenu du temps disponible. Nous avons décidé de restreindre le nombre de classes à sept classes. Il s’agit des puissances de deux 0, 2, 4, 8, 16, 32, 64. Ce sont les facteurs de déroulage les plus utilisés par les experts. Les tests que nous avons effectués dans la phase précédente le montre également (voir figure IV.10).
D’autre part, nous avons effectué une analyse des caractéristiques (features) les plus significatives en utilisant l’outil features_selctor (voir la section IV.6). L’outil propose la suppression de 5 colonnes telles que le nombre de constantes et le nombre de niveaux de boucle151515Il s’agit d’une information déductible à partir de certaines autres colonnes (le nombre de colonnes qui présentent les étendus des niveaux du nid de boucle). Nous avons effectué des tests pour s’assurer que la précision augmente après la suppression des 5 colonnes.
Dans cette itération, nous allons relancer les tests sur un Dataset plus large pour sélectionner les bons hyperparamètres du modèle, à savoir le nombre de couches, le nombre de neurones dans chaque couche, l’algorithme d’optimisation à utiliser, etc. (pour davantage de détails, voir l’annexe D). Pour chaque hyperparamètre, nous effectuons un ensemble de tests, la valeur qui donne la meilleure précision du modèle est maintenue.
Nous avons commencé par choisir le nombre de couches et le nombre de neurones dans chaque couche. Nous avons constaté que quatre couches cachées avec 500, 400, 250 et 100 neurones dans chaque couche respectivement, donne la meilleure précision. En effet, nous avons testé pour 12 couches au maximal161616Les tests effectués pour un nombre de couches supérieur à 12 a donné une précision très basse, et ce, pour les différents cas de nombre de neurones., puis nous diminuons le nombre de couches si la précision dégrade. Pour chaque couche nous avons testé dichotomiquement les cas du nombre de neurones. D’abord, nous avons défini une borne minimale et maximale au nombre de neurones. Ensuite, nous définissons la valeur divisant l’ensemble de cas de tests en deux sous ensembles. Nous continuons l’exploration du sous ensemble qui donne une précision meilleure.
Concernant les autres hyperparamètres du modèle, les tests effectués (voir section IV.7) donnent les résultats résumés dans le tableau VI.
Le choix des hyperparamètres est suivi par une phase d’optimisation (voir l’annexe D). Pour remédier au problème du sousapprentissage, nous avons utilisé la technique d’optimisation dropout avec les facteurs (0.12, 0.1, 0.04 et 0.07) respectivement. Quant à la régularisation, nous l’avons pas appliquée car elle a influencé négativement sur la précision du modèle.
Par contre, nous avons appliqué la technique de batch-normalization pour améliorer la vitesse d’entraînement (d’ordre de 10 fois), améliorer la précision171717L’application de la technique de batch-normalization a amélioré la précision de 5% et la stabilité du modèle.
IV.3.3.3 3ème itération : l’entraînement du modèle sur les données finales
Tout au long de deux mois, nous avons pu générer un dataset de taille réduite181818La taille du dataset est de éléments relativement aux tailles nécessaires des datasets utilisés pour l’apprentissage profond. En effet, pour chaque programme (élément du dataset), nous effectuons 30 exécutions pour avoir une estimation de la moyenne réelle du temps d’exécution du programme (voir section IV.3.2.4). Ceci consomme énormément du temps. Il faut presque une année de génération de donnée pour collecter autant de données que nécessite notre problème.
Toutefois, la précision du modèle augmente tout au long du processus de génération de données. L’augmentation du nombre de données générées à chaque fois n’est pas aussi grande pour donner des changements considérables, mais la précision du modèle s’améliore graduellement et elle enregistre une augmentation relativement remarquable. (voir la figure IV.11).
Nous avons constaté également que la distribution des données du dataset sur les classes du modèle n’est pas équilibrée (voir la figure IV.12). La distribution doit être uniforme pour permettre à toutes les classes d’avoir le même degré de contribution dans l’apprentissage. De ce fait, nous avons appliqué la technique d’équilibrage de classes en définissant un seuil minimal d’éléments dans chaque classe. Certes, la taille du dataset a diminué (devient 26670 éléments), or, la précision du modèle s’est améliorée.
Nous lançons l’entraînement du modèle sur le dataset final. Le but est d’extraire et de sauvegarder les paramètres finaux du modèle (derniers poids et biais avec l’architecture du modèle) et par la suite, l’intégrer dans Tiramisu.
IV.4 Synthèse de conception
Dans cette première partie de ce chapitre , nous avons détaillé les différentes étapes de conception de notre solution. D’abord, nous avons discuté les choix conceptuels concernant les entrées, les sorties et l’approche d’optimisation automatique à considérer pour concevoir la solution. L’architecture conceptuelle de notre solution est basée sur deux principaux modules : le module d’extraction des caractéristiques et le modèle de prédiction du meilleur facteur de déroulage. Ce dernier est basé sur les réseaux de neurones, nous avons expliqué et justifié tous les choix considérés pour le concevoir.
Dans cette deuxième partie, nous détaillons l’implémentation des choix conceptuels considérés dans les sections précédentes.
D’abord, nous exposons les phases de réalisation du système ainsi que son architecture technique. Ensuite, nous définissons l’environnement du développement en citant les technologies, les outils et les plateformes utilisés pour développer notre solution. Tous les choix techniques considérés seront également justifiés.
IV.5 Architecture technique du système
Notre système de prédiction du meilleur facteur de déroulage représente un composant de tout un système d’optimisation automatique dans le compilateur Tiramisu191919Le système d’optimisation automatique dans Tiramisu est un projet en cours de développement, notre contribution touche principalement l’optimisation de déroulage de boucle.. Ce dernier utilise une interface fournie permettant de gérer les appels à notre système prédicteur.
Lorsque l’utilisateur de Tiramisu lance la commande automatic_unrolling() pour automatiser le choix du facteur de déroulage, le compilateur fait appel à la première couche du système, à savoir l’extraction des caractéristiques du programme. Ces derniers sont ensuite passées au modèle de prédiction pour prédire le bon facteur de déroulage. Enfin, Tiramisu exécute le programme en considérant le facteur prédit (voir la figure IV.13).
IV.6 Environnement de développement
Nous avons choisi les technologies suivantes pour implémenter notre solution.
- –
Tiramisu202020https://github.com/Tiramisu-Compiler/tiramisu : est le système hôte de notre solution : les modules implémentés sont intégrés comme étant des sous systèmes en Tiramisu. Il est utilisé pour compiler et exécuter les programmes.
- –
Tiramisu_Code_Generator212121https://github.com/Tiramisu-Compiler/tiramisu/tree/master/utils/code_generator : afin de construire notre Dataset, nous avons généré aléatoirement des codes appartenant à la classe de programmes visée (voir section IV.2.2). Le générateur permet d’introduire des caractéristiques définissant certaines classes de programmes, il permet également de personnaliser la génération aléatoire de codes Tiramisu (parties algorithme) et les schedules associés.
- –
TensorFlow222222https://www.tensorflow.org/ : est un Framework utilisé pour développer le modèle de prédiction du bon facteur de déroulage. Il est open source, bien documenté et soutenu par une très grande communauté active et par le géant du développement Google. C’est l’outil de développement de solutions d’apprentissage automatique le plus utilisé. D’autre part, TensorFlow facilite l’exportation et le déploiement des modèles générés vers d’autres plateformes cibles, le C++ en l’occurrence (Tiramisu est embarqué sur le C++).
- –
Keras232323https://keras.io/ : ce Framework haut niveau écrit en Python, facilite l’implémentation des algorithmes du machine learning. Nous l’avons utilisé pour comparer notre modèle implémenté sous TensorFlow notamment pour la sélection des bons hyperparamètres.
- –
Feature_Selctor242424https://github.com/WillKoehrsen/feature-selector. : nous avons utilisé cet outil open source pour identifier les features qui ne contribuent pas significativement à l’apprentissage du modèle. Il se base sur plusieurs principes pour identifier les features à supprimer tels que la colinéarité des caractéristiques. cet outil offre la possibilité de visualiser les résultats sous forme de tableaux ou de graphes. Il donne également la liste directe des features à supprimer.
- –
Pandas : une bibliothèque open source de Python qui permet de manipuler et d’analyser les données. En particulier, elle offre plusieurs structures de données (DataFrame) qui facilitent la manipulation des données numériques. Elle dispose également de nombreuses méthodes utilisées pour l’analyse de données, ce qui s’avère très utile lorsque nous travaillons sur des problèmes d’apprentissage automatique sur Python. Nous avons utilisé la structure DataFrame pour lire et manipuler notre dataset sauvgardé dans les fichiers CSV252525CSV signifie ”valeurs séparées par des virgules”. Nous l’avons choisi comme format de fichiers pour sauvegarder les données d’entraînement (caractéristiques des programmes)..
- –
TensorBoard : est un outil de visualisation de TensorFlow. Il facilite la compréhension et l’optimisation des modèles implémentés sous TensorFlow. Il permet de visualiser le graphe d’exécution et les différents paramètres du modèle (les poids, les biais, fonction de perte, etc.).
- –
Matplotlib : est une bibliothèque de visualisation rapide et facile à manipuler. Nous l’avons utilisée pour visualiser les différents résultats du modèle. Ceci nous a permis de comparer l’évolution de la fonction de perte pour différents types d’hyperparamètres (différents algorithmes d’optimisation, taux d’apprentissage, etc.).
- –
scikit-learn : est une bibliothèque Python open source qui offre une solide implémentation de plusieurs algorithmes d’apprentissage automatique (SVMs, K-means, etc.). Nous l’avons utilisée principalement pour diviser notre Dataset en trois parties (entraînement, validation et test).
- –
Lanka: le cluster est géré exclusivement par MIT. Il est composé de 24 machines. Il comporte au total 48 nœuds chacun dispose de 12 cœurs de 128GB de RAM. Nous avons utilisé ce cluster pour lancer les scripts de génération de codes et d’extraction des caractéristiques des programmes.
- –
GoogleColab : ou Colaboratory est un service du Cloud totalement gratuit. C’est un environnement portable (qui ne nécessite aucune configuration) qui permet de développer des applications d’apprentissage automatique en utilisant les bibliothèques populaires telles que Keras, TensorFlow, PyTorch et OpenCV. Il facilite l’entraînement des modèles sans se soucier des problèmes d’installation et de puissance de calcul. Nous l’avons utilisé comme un IDE pour développer notre modèle afin de remédier au problème en puissance de calcul de nos machines.
IV.7 Phases de réalisation du système
Après avoir cerné les objectifs du système et défini une conception de base, nous avons commencé la réalisation pour valider la conception. Initialement nous avons préparé l’environnement de développement, à savoir l’installation des technologies et des outils ainsi que la préparation de la plateforme Lanka. Les différentes phases de réalisation ont été lancées parallèlement suivant un processus itératif pour tester à chaque itération les décisions prises.
IV.7.1 Implémentation du module d’extraction des caractéristiques
Le module d’extraction est implémenté directement sur le compilateur Tiramisu, Il a un accès direct aux différents composants et classes de Tiramisu. Le langage utilisé est le C qui est le plus approprié car Tiramisu est embarqué sur C. Le module permet d’exporter les caractéristiques sous forme d’un fichier CSV pour pouvoir construire l’ensemble de données.
IV.7.2 Préparation de données
Nous avons configuré le générateur Tiramisu_code_Générator pour donner des programmes appartenant à la classe des programmes visée : le générateur prend un fichier de configuration en entrée dans lequel nous avons introduit les caractéristiques de la classe des programmes visée. Nous avons introduit d’autres paramètres définissant des bornes supérieures pour certains features telles que le nombre maximal des niveaux dans les nids de boucles, le nombre maximal des Inputs262626Inputs en Tiramisu est une matrice multidimensionnelle pour charger les données., etc.
Nous avons défini également les différentes optimisations de boucles ainsi que l’ensemble de facteurs à explorer lors de la génération des programmes.
Nous avons lancé les scripts d’extraction des caractéristiques en parallèle sur 10 nœuds du cluster Lanka. Chaque nœud traite un ensemble de programmes, pour chaque programme, le générateur donne tous les schedules possibles (en se basant sur les paramètres définis dans le fichier de configuration de générateur) et pour chaque Schedule possible, le générateur donne le code pour chaque facteur de déroulage possible.
Dans la première itération de réalisation, les facteurs de déroulage sont les multiples de 2 (0, 2, 4, … min(étendu de la boucle/2, 128)). Ensuite dans la seconde itération, les facteurs explorés sont les puissances de deux (0, 2, 4, 8 … 64).
Afin d’avoir une bonne précision des temps d’exécution, le programme généré pour chaque facteur de déroulage doit être exécuté 30 fois et prendre la moyenne des temps d’exécution enregistrés. Cette phase a coûté énormément de temps. En effet, si par hypothèse le temps d’exécution d’un programme en moyenne prend un temps , avec le nombre des Schedules en moyenne pour chaque programme et est le nombre de facteurs de déroulage de boucles. La génération au niveau de chaque nœud prend pour chaque programme (partie algorithme commune)
IV.7.3 Implémentation du modèle de réseau de neurones
Le modèle est implémenté en utilisant le FrameWork TensorFlow sur l’environnement de développement GoogleColab. Nous avons implémenté toutes les méthodes qui permettent de construire le modèle et d’effectuer les traitements nécessaires. Nous nous sommes appuyés sur certaines bibliothèques de Python(Pandas, Sklearn) pour implémenter d’autres opérations de pré-traitement essentiellement(la manipulation du dataset sous le format "CSV" et sa division en trois parties).
Pour définir les paramètres du modèle, nous effectuons des tests pour chaque paramètre, la valeur qui donne la meilleure précision du modèle est maintenue.
Pour l’algorithme d’optimisation, nous avons testé cinq algorithmes les plus utilisés : le SGD, ADAM, NADAM et RMSProp Ruder (\APACyear2016).
Quant au taux d’apprentissage, il existe plusieurs méthodes de test permettant de choisir sa meilleure valeur. Nous avons adopté une approche efficace et simple qui consiste à essayer une valeur relativement élevée ( nous avons choisi 0.1272727Haut-delà de cette valeur la fonction de perte ne converge pas.), puis la réduire avec un facteur de 10 à chaque étape du test empirique. Pendant ces tests, nous varions le taux d’apprentissage tout en gardant les autres paramètres fixes afin de voir le vrai impact de ce paramètre sur le modèle. Nous choisissons vers la fin, l’algorithme d’optimisation avec le temps d’apprentissage permettant d’avoir plus de précision. La figure IV.14.(a) montre la variation de la fonction perte (loss) pour les données de validation en fonction de nombre d’itérations pour les différents algorithmes d’optimisation. Le test est effectué en gardant l’initialisation par défaut de chaque algorithme282828Le taux d’apprentissage par défaut de chaque algorithme.
Nous avons choisi ADAM comme algorithme d’optimisation. En effet, l’algorithme ADAM et ses deux versions optimisées (Nadam et Adamx) surmonte significativement l’algorithme SGD et Adagrad. Quant aux autres algorithmes, ils ont pu enregistrer des résultats relativement compétitifs, mais l’algorithme ADAM converge plus rapidement et minimise mieux le taux d’erreur de prédiction.
Après avoir choisi l’algorithme d’optimisation, nous procédons aux tests de différents taux d’apprentissages. Les résultats obtenus sont présentés dans le graphe de la figure IV.14.(b). Le graphe permet de constater que pour des taux élevés (à partir de ) le modèle diverge au fil des itérations. Nous constatons également que les taux bas (à partir de ), la fonction perte commence à diminuer plus lentement ce qui montre que le taux d’apprentissage du modèle devient trop faible. En se basant sur ces résultats, nous choisissons la valeur comme taux d’apprentissage pour le modèle.
Pour trouver les bonnes valeurs initiales à attribuer au poids , nous avons testé plusieurs algorithmes d’initialisation de poids (voir la figure IV.15). Les résultats des tests affirment que l’algorithme d’initialisation uniforme ((Random unifrom initializer) minimise le plus la fonction perte du modèle. Il permet d’initialiser les poids avec une distribution uniforme292929La distribution uniforme est le type de distribution de probabilité dans lequel tous les valeurs ont la même probabilité d’apparaître..
Quant au nombre d’itérations, nous avons effectué la méthode d’arrêt précoce (Early stopping) qui permet d’arrêter l’entraînement du modèle une fois l’erreur est stable et aucune amélioration n’est apportée au réseau. La patience303030Le nombre d’itérations effectuées après avoir atteint le stade où aucune amélioration n’est apportée au modèle est un paramètre important à définir pour cet algorithme. Selon les tests effectués nous posons ce paramètre à 10.
IV.7.4 Entraînement du modèle de prédiction
L’entraînement du modèle est fait sur l’environnement de développement Colaboratory en deux étapes : la première consiste à entraîner le réseau de neurones pour ajuster le modèle sur les bons hyperparamètres. La deuxième étape consiste à relancer le processus d’entraînement mais en considérant les meilleurs hyperparamètres choisis dans la première étape, et ce, en utilisant l’ensemble de données final. Les paramètres finaux obtenus à la fin du traitement(les dernières valeurs du poids, biais) sont sauvegardés afin de les utiliser directement pour exporter le modèle.
IV.7.5 Génération du modèle et intégration à Tiramisu
Après avoir sauvegarder la dernière architecture et paramètres du réseau, le modèle est prêt pour l’exporter sous forme de script pour l’intégrer à Tiramisu. Le modèle de prédiction peut être appelé directement par la méthode "automatic_unrolling()" afin de prédire le meilleur facteur de déroulage pour de nouveaux programmes écrits en Tiramisu.
Conclusion
Dans ce chapitre , nous avons détaillé les différentes étapes de conception de notre solution. Nous avons discuté les différents choix considérés. Dans une deuxième partie, nous avons présenté l’environnement de développement ainsi que les phases de réalisations suivies. Dans le chapitre suivant, nous allons tester le modèle avec différents scénarios pour confirmer que le modèle est en mesure de bien prédire sur des cas réels.
Chapitre V Tests et évaluation
Introduction
Après avoir détaillé notre conception et les étapes de la réalisation, nous exposons le processus d’évaluation du modèle prédicteur du meilleur facteur de déroulage. D’abord, nous avons comparé la précision de notre modèle avec d’autres modèles du machine learning. Ensuite, il est évalué sur un ensemble de benchmarks. Nous comparons entre les résultats obtenus par notre modèle et ceux obtenus par la recherche exhaustive.
Dans ce chapitre, nous commençons par décrire la plateforme matérielle de tests, les critères d’évaluation considérés et les benchmarks utilisés. Ensuite nous exposons les résultats obtenus pour enfin donner une synthèse sur les tests effectués.
V.1 Plateforme de tests
Afin d’évaluer le prédicteur du meilleur facteur de déroulage, nous avons utilisé deux plateformes de tests suivant les deux phases de tests considérées.
D’abord, pour l’évaluation initiale, nous avons évalué les perfomramnces du modèle en comparant sa précision avec d’autres algorithmes du machine learning. Cette évaluation est effectué sur la même plateforme GoogleColabe (voir section IV.6). Cette plateforme offre un environnement de développement qui ne nécessite pas des configurations complexes ni des installations de bibliothèques. De plus, GoogleColabe offre actuellement l’accès à un processeur graphique GPU de type T4 avec une RAM de 16 GiB111Voir les caractéristiques du GPU sur https://www.nvidia.com/fr-fr/data-center/tesla-t4/. (voir la figure V.1). Nous avons lancé l’exécution du modèle sur le processeur GPU pour bénéficier de la puissance en calcul offerte et diminuer le temps d’entraînement du modèle.
Durant la deuxième phase de tests basée sur les benchmarks, nous avons utilisé les machines du cluster Lanka (voir section IV.6). Ce cluster dispose de plusieurs nœuds ayant les mêmes caractéristiques (voir tableau VII). Nous avons déployé notre système intégré au compilateur Tiramisu. Pour chaque benchmark, nous avons exécuté exhaustivement sur Lanka les instances de codes avec les différents facteurs de déroulage possibles (utilisés lors d’entraînement du modèle) pour définir le meilleur facteur. Utilisant la même plateforme, nous avons également exécuté les benchmarks pour la prédiction automatique par le modèle implémenté.
V.2 Évaluation initiale du modèle
L’évaluation initiale consiste à comparer les résultats que donne notre modèle avec d’autres modèles d’apprentissage automatique supervisé présentes dans des travaux précédents. Il s’agit de l’algorithme des plus proches voisins et les arbres de décision.
- –
L’algorithme de K plus proches voisins KNN : l’application de cet algorithme pour classer les programmes (individus) selon leurs facteurs de déroulage consiste à chercher des individus qui leurs ressemblent dans le dataset. Deux programmes sont proches (ont le même facteur de déroulage) si leurs caractéristiques se ressemblent.
- –
Les arbres de décision : Ils sont plus rapides et plus efficaces comparés à l’algorithme de K plus proches voisins KNN. La section (II.3.1.3.– ‣ B) donne plus de détail sur l’utilisation de cet algorithme dans le problème de sélection des optimisations.
La métrique d’évaluation considérée est la précision du modèle sur les données de tests. La précision du modèle représente le rapport entre le nombre des prédictions correctes et le nombre total des éléments dans le dataset du test. Il s’agit de la même métrique utilisée pour évaluer les différents modèles générés lors de la phase d’entraînement afin de sélectionner le meilleur.
Le but est d’évaluer l’amélioration apportée par l’utilisation des réseaux de neurones pour résoudre le problème de sélection du facteur de déroulage. La précision de notre modèle est comparée aux deux algorithmes du machine learning choisis (voir le tableau VIII). Les résultats exposées présentent la moyenne des précisions suite à plusieurs exécutions effectuées. Après un certain nombre d’exécutions, la précision converge vers la même valeur.
V.2.1 Analyse des résultats
Notre modèle est compétitif aux modèles de machine learning utilisés dans des travaux précédents. Nous remarquons que les réseaux de neurones apportent une légère amélioration. Ceci prouve que les réseaux de neurones peuvent être utilisés pour résoudre le problème de prédiction du meilleur facteur de déroulage.
Nous rappelons que le but principal de notre projet est d’explorer la technique du deep learning pour le problème du choix du meilleur facteur de déroulage. En effet, cette technique n’a pas été utilisée auparavant pour résoudre ce problème. Nous rappelons aussi que la précision atteinte est jugée très acceptable malgré le manque significatif de données dû aux contraintes techniques.
Dans cette partie nous avons évalué l’effet apporté par l’utilisation de réseaux de neurones pour résoudre le problème de sélection de meilleur facteur de déroulage. Nous avons pu conclure que les réseaux de neurones peuvent être utilisés pour prédire le meilleur facteur de déroulage. Avec un nombre suffisant de données, le modèle peut atteindre un seuil de précision important. Dans la prochaine section, nous évaluons notre modèle sur un ensemble de benchmarks réels.
V.3 Évaluation par Benchmarks
Dans une deuxième phase, le système est évalué grâce à un ensemble de benchmarks (appartiennent à la classe de programmes choisie) que nous avons implémentés nous même.
Nous avons implémenté d’abord la méthode d’exploration exhaustive des facteurs de déroulage. Elle représente une référence pour comparer les résultats de prédiction que donne le modèle.
Pour chaque benchmark, nous avons lancé l’exploration exhaustive des facteurs de déroulage afin de définir le meilleur facteur et le comparer avec le facteur prédit par le modèle. Nous avons répété ce processus pour différentes tailles de données d’entrées (voir tableau IX) car la taille de données est un facteur déterminant qui influence fort la perdition.
Nous avons également répété les tests de chaque benchmarks pour différents schedules possibles.
Pour évaluer les résultats, nous avons considéré les deux métriques d’évaluation suivantes :
- –
Le coût de prédiction (prediction cost) qui représente le rapport entre le temps d’exécution dont le facteur est obtenu par l’exploration exhaustive (), avec le temps d’exécution dont le facteur de déroulage est prédit automatiquement().
PC =
- –
Accélération (speedup) qui est le rapport entre le temps d’exécution sans application d’optimisation de déroulage () et le temps d’exécution obtenu en appliquant le facteur prédit ().
SP =
V.3.1 Benchmarks de tests
Dans cette section, nous exposons les résultats obtenus de test sur les benchmarks. Nous commençons d’abord par présenter chaque benchmark et donner ses caractéristiques. Ensuite, nous exposons l’évaluation des performances suivie par une analyse des résultats obtenus. Le tableau X représente la liste des benchmarks utilisés.
V.3.2 Benchmark MMM
La multiplication de deux matrices constitue un noyau pour divers programmes scientifiques (voir l’algorithme 3). MMM est considéré comme un benchmark gourmand en temps d’exécution. Ceci est dû notamment à la contrainte de localité de données qu’il impose. En effet, les accès en mémoires ne sont pas contigus. Ce fait influence l’efficacité du cache considérablement, et ce, pour les deux modes d’accès aux éléments de matrices que peut adopter l’architecture d’exécution222Ligne par ligne (row-major access) ou colonne par colonne column-major access..
L’effet qu’apporte l’optimisation du benchmark est remarquable. Le tableau XI présente un sous ensemble des caractéristiques (features) du benchmark MMM données par le module d’extraction des features. Le facteur Msize peut prendre trois valeurs (voir le tableau IX) selon le cas de test.
Msize représente la taille de matrices (soupons que les deux matrices sont carrées de taille égale).
V.3.2.1 Résultats de tests
Les tests sont lancés pour les trois modalités de tailles de données. Pour chaque cas, nous avons également proposé un schedule (voir tableau XII).
Nous avons opté pour l’optimisation de tuilage de boucles (tile) appliquée sur les deux niveaux ( i0 et i1). Ensuite, la parallélisation est toujours appliquée sur le niveau le plus externe. Les cas de tests sont les suivants:
- –
**schedule0 (pour le cas de taille des entrées): ** le facteur de tuilage est 16.
- –
**schedule1 (pour le cas de taille des entrées): ** seule l’optimisation de parallélisation est appliquée.
- –
schedule2 (pour le cas de taille des entrées): le facteur de tuilage est 32.
V.3.3 Benchmark SMM
Il représente la formule de somme matricielle générale (voir l’algorithme 4). Nous avons sélectionné, dans le tableau XIII, un sous ensemble de caractéristiques du benchmark SMM que donne le module d’extraction de features.
Msize représente la taille de matrices (nous soupons également que les deux matrices sont carrées de taille égale).
V.3.3.1 Résultats de tests
Nous appliquons dans chaque cas de test (voir tableau XIV) l’optimisation de tuilage de boucles (tile) sur les deux niveaux ( i0 et i1). Ensuite, l’optimisation de l’inversion de boucles (interchange) entre le deuxième et le troisième niveau. L’optimisation de parallélisation est toujours appliquée sur le niveau le plus externe. Les cas de tests sont les suivants:
- –
schedule0 (pour le cas de taille des entrées): seule l’optimisation de déroulage est appliquée.
- –
schedule1 (pour le cas de taille des entrées): le facteur de tuilage est 16 avec l’application de l’inversion de boucles.
- –
schedule2 (pour le cas de taille des entrées): le facteur de tuilage est 32 avec application de l’inversion de boucles.
V.3.4 Benchmark RGB_gray
Le benchmark effectue la transformation d’un image constituée de trois couches : une couche rouge (R), une couche verte (G), une couche bleue (B) vers une image en niveaux de gris. (voir l’algorithme 5). Dans le tableau XV, nous avons sélectionné un sous ensemble de caractéristiques définissant le benchmark RGB_gray.
L’image est représentée sous forme de trois matrices carrées chacune ayant la taille Isize.
V.3.4.1 Résultats de tests
Nous appliquons dans chaque cas de test un schedule donnée (voir tableau XVI). L’optimisation de tuilage de boucles (tile) est appliquée sur les deux niveaux (x et y). L’optimisation de parallélisation est toujours appliquée sur le niveau le plus externe.
Les cas de tests sont les suivants:
- –
schedule0 (pour le cas de taille des entrées): seule la parallélisation est appliquée.
- –
schedule1 (pour le cas de taille des entrées): le facteur de tuilage est 32.
- –
schedule2 (pour le cas de taille des entrées): le facteur de tuilage est 64.
V.3.5 Benchmark Blur
Ce benchmark permet de flouter une image (en niveaux de gris). Il s’agit de calculer la moyenne des valeurs du voisinage pour chaque pixel (voir l’algorithme 6). Dans le tableau XVII, nous exposons un sous ensemble de caractéristiques définissant le benchmark Blur.
L’image est représentée sous forme d’une matrice carrée dont la taille est Isize.
V.3.5.1 Résultats de tests
Le tableau XVIII représente les cas de tests effectués sur le benchmark Blur. Nous avons appliqué seulement l’optimisation de parallélisation (sur le niveau le plus externe). Les cas de tests dépendent donc de la taille de données en entrée.
V.3.6 Benchmark Conv_layer
Ce benchmark888L’implementation est inspiré du benchmark implémenté par l’équipe Tiramisu https://github.com/Tiramisu-Compiler/tiramisu/tree/master/benchmarks/DNN/layers/convolution/. représente l’opération de convolution effectuée aux niveaux des couches de réseaux de neurones convolutifs CNN. L’algorithme reçoit en entrée une matrice de quatre dimensions et un filtre de trois dimensions. Le benchmark Conv_layer agit sur un lot de données (batch_size) dont la taille représente la dimension la plus externe de la matrice d’entrée.
La convolution est similaire à une opération de filtrage. Pour chaque position du filtre, les valeurs des deux matrices en superposition (filtre et image à traiter) sont multipliées. Chaque valeur ainsi inférée est projetée dans une nouvelle matrice (voir l’algorithme 7).
V.3.6.1 Résultats de tests
Les cas de tests (voir le tableau XIX) sont définis selon les tailles de la matrice d’entrée et le filtre. En effet, la taille du lot de données (batch_size) influence sur les performances. Nous considérons trois valeurs possibles de la taille du lot (64, 32, 8), et ce, selon la taille du Data_set (petit, moyen, grand respectivement).
Nous avons appliqué seulement l’optimisation de parallélisation (sur le niveau le plus externe).
V.4 Analyse et synthèse des tests
Notre solution est testée sur un ensemble de 5 benchmarks que nous avons implémentés manuellement. Pour chaque benchmark nous avons proposé trois cas de tests qui dépendent de schedules affectés ou de la taille de données en entrée.
Le modèle proposé arrive à prédire correctement le meilleur facteur de déroulage dans 4/15 des cas de tests effectués. Nous avons enregistré une amélioration (accélération) du temps d’exécution dans 5/15 des cas. La figure V.2 synthétise les résultats enregistrés.
Nous rappelons que (coût de prédiction) représente le rapport entre le temps d’exécution dont le facteur est obtenu par l’exploration exhaustive (facteur optimal), avec le temps d’exécution dont le facteur de déroulage est prédit automatiquement. Quant à (speedup), c’est le rapport entre le temps d’exécution sans application d’optimisation de déroulage et le temps d’exécution obtenu en appliquant le facteur prédit.
Nous remarquons dans la figure V.2 que les taux et varient d’un benchmark à un autre. En effet, pour le benchmark MMM et Blur, nous avons enregistré des taux assez positifs. Ceci signifie que le modèle arrive à apprendre de bonnes prédictions pour gérer le problème de localité de données qui s’impose dans les deux benchmarks.
Pour certains cas de tests, le modèle permet d’avoir une accélération () suite à l’application de l’optimisation de déroulage. Dans d’autres cas, nous avons enregistré un comme dans les trois cas de tests du benchmark Conv_layer. Ceci prouve que l’optimisation de déroulage peut ne pas apporter d’amélioration.
Pour synthétiser, le modèle arrive à apprendre des caractéristiques de haut niveau (high level features999Caractéristiques plus complexes liées au comportement dynamique du programme tel que la gestion des chargements/écritures de données en mémoire et en cache, la gestion des registres, etc.) à partir de caractéristiques bas niveau (low level features101010Des caractéristiques statiques basiques qui décrivent le programme.). Il donne des prédictions du meilleur facteur de déroulage pour les nouveaux programmes111111les programmes appartiennent à la classe visée avec une précision qui atteint 20%. Ceci montre que le modèle apprend et il dépasse la prédiction aléatoire (dont la précision est de 14%).
La précision de notre modèle est compétitive par rapport à d’autres modèles basés sur des algorithmes déjà explorés dans des travaux précédents. Ceci nous permet de déduire que les réseaux de neurones peuvent être utilisés pour le problème de prédiction du facteur de déroulage.
D’autre part, nous avons démontré que la précision de la prédiction augmente avec l’augmentation de la taille du dataset utilisé pour entraîner le modèle.
Conclusion
Nous avons montré à travers ce chapitre que le modèle proposé est compétitif aux algorithmes du machine learning utilisés pour résoudre le problème de sélection du facteur de déroulage. De ce fait, la solution basée sur les réseaux de neurones peut être explorée davantage pour améliorer la précision.
Nous avons également évalué notre modèle sur un ensemble de banchmarks. Les résultats obtenus affirment que le modèle arrive bien à apprendre la prédiction du facteur de déroulage. La précision des prédictions s’améliore avec l’augmentation de la taille du dataset utilisé pour l’entraînement. Nous avons pu générer un dataset relativement petit, ceci empêche la précision de dépasser le seuil obtenu.
Conclusion générale et perspectives
Notre projet de fin d’étude s’inscrit dans le cadre du projet lancé par l’équipe COMMIT de MIT qui vise à améliorer le compilateur Tiramisu. Notre contribution consiste à concevoir et réaliser un modèle basé sur les réseaux de neurones pour automatiser le choix du meilleur facteur associé à l’optimisation de déroulage de boucles. Le modèle est conçu pour prédire sur des programmes écrits en Tiramisu (programmes déjà optimisés ou non optimisés) appartenant à une classe de programmes souvent utilisés dans Tiramisu (voir section IV.2.2). Les architectures d’exécution considérées sont à base de CPU.
Le projet vise également à explorer l’utilisation des réseaux de neurones profonds pour la résolution du problème de sélection du facteur de déroulage.
L’optimisation de code est une étape cruciale pour assurer une meilleure exploitation des ressources matérielles. Dans certains domaines, les systèmes conçus sont critiques ou nécessitent un calcul immense, l’optimisation devient indispensable. L’optimisation de code est l’ensemble des techniques utilisées afin d’améliorer les performances d’un programme. En effet, la réduction du temps d’exécution constitue l’objectif principal de l’optimisation de codes. Maintes optimisations appliquées sur les boucles sont proposées car les boucles consomment plus de du temps d’exécution d’un programme. Chaque optimisation de boucles vise à améliorer certaines métriques influençant le temps d’exécution, à savoir l’utilisation du cache, le pré-chargement de données, l’utilisation des registres, etc.
Nous avons exposé les différentes optimisations de boucles. Nous nous sommes restreints aux optimisations de boucles utilisées dans le compilateur Tiramisu. L’optimisation de codes nécessite une expertise approfondie, du temps et beaucoup d’effort. C’est une tâche fastidieuse qui nécessite plusieurs tests pour trouver les meilleures combinaisons d’optimisations. Elle est aussi une tâche critique, car elle risque de dégrader les performances du programme au lieu de les améliorer. En effet, l’optimisation dépend de plusieurs paramètres comme les interactions entre les transformations et l’architecture matérielle d’exécution. L’automatisation de cette tâche permet de décharger le programmeur du travail fastidieux.
Plusieurs compilateurs intègrent un module d’optimisation automatique. Cependant, l’optimisation automatique du code présente plusieurs défis et reste contraignante. Les principaux problèmes d’optimisation automatique se résument dans le problème de sélection de bonnes combinaisons d’optimisations, le problème de sélection des meilleurs facteurs affectés aux optimisations sélectionnées et le problème d’estimation du temps d’exécution. En effet, il s’agit d’un problème NP-complet : la sélection de bonnes combinaisons d’optimisations de boucles à appliquer ainsi que leurs facteurs se fait sur un ensemble dont le cardinal dépend exponentiellement du temps de résolution.
Pour concevoir des techniques d’optimisation automatique, plusieurs approches sont adoptées. D’abord, l’approche exploratrice qui consiste à parcourir un ensemble d’optimisations puis tester et mesurer chacune des combinaisons afin de renvoyer celle qui minimise le temps d’exécution du programme. Cette approche est la plus précise, mais elle impose des restrictions sur la taille du problème, car elle nécessite un temps d’exécution considérable. Ensuite, l’approche analytique pour la prédiction du temps d’exécution. Elle se base sur un modèle défini en fonction de plusieurs paramètres souvent relatifs à l’architecture d’exécution. Or, ces modèles doivent être assez complexes pour donner de bons résultats. L’approche basée sur l’apprentissage automatique, permet de générer des modèles capables de prédire les bonnes optimisations à appliquer, leurs facteurs ou encore le temps d’exécution des programmes. La précision de cette approche dépend de l’algorithme d’apprentissage automatique choisi, la définition des hyperparamètres du modèle et de la taille du dataset d’apprentissage dans le cas d’apprentissage supervisé.
Tiramisu représente le système hôte de notre modèle. C’est un nouveau langage et compilateur qui permet de générer des codes très rapides et de cibler différentes architectures matérielles (multicore, GPU, FPGA et systèmes distribués).
La conception de notre solution traite deux principaux modules : l’extraction automatique des caractéristiques (features) de programmes et le modèle de prédiction du meilleur facteur de déroulage. Nous avons suivi un processus de développement itératif. Dans chaque phase, nous apportons des améliorations sur la conception du système en se basant sur les tests des métriques de performances.
Le module d’extraction automatique des caractéristiques de programmes que nous avons proposé agit en mode online, ce qui veut dire que l’extraction de features se fait au moment de l’exécution. Ce mode permet d’avoir des informations nécessaires accessibles en cours d’execution. Le module est complètement intégré dans Tiramisu, ceci accélère les traitements d’extraction des caractéristiques.
L’entraînement du modèle de réseau de neurones proposé nécessite un dataset de taille immense (d’ordre de ) afin d’atteindre une précision élevée. Après deux mois de génération de données, nous avons pu construire un dataset relativement petit (). Ceci est dû aux contraintes de temps et de puissance de calcul des machines. En effet, la génération de datasets immenses pour les utiliser dans les réseaux de neurones profonds consomme énormément de temps et nécessite des machines puissantes. Par exemple, le projet lancé par Facebook qui traite également le problème de sélection du facteur d’une optimisation de boucles (le tuilage) pour le langage Halide121212Halide partage une logique très similaire à Tiramisu. La collecte de données (dataset de taille éléments) et l’ingénierie des caractéristiques (feature engineering) ont pris plus d’une année, et ce, en utilisant les supercalculateurs de Facebook131313Ces informations sont données par notre promoteur qui a travaillé sur ce projet.
Pour évaluer notre modèle, nous avons comparé la précision de notre modèle avec deux autres algorithmes de machine learning (K plus proches voisins et les arbres de décision) utilisés dans des travaux précédents pour le problème de sélection du bon facteur de déroulage. Les résultats permettent de vérifier que l’utilisation d’un modèle basé sur les réseaux de neurones profonds dans notre problème donne des résultats compétitifs. Ceci encourage à continuer la recherche afin d’améliorer davantage le modèle.
Nous avons également testé les performances du modèle sur un ensemble de benchmarks implémentés manuellement en Tiramisu. Dans des cas de tests, le modèle a prédit correctement le meilleur facteur de déroulage. Dans des cas, il a amélioré le temps d’exécution des programmes sur lesquels l’optimisation de déroulage n’a pas été appliquée.
Les résultats exposés dans la partie évaluation confirment que le modèle apprend à prédire, car la précision atteinte dépasse la prédiction aléatoire (14%). Cependant, la précision de notre modèle est influencée par le manque de données. En effet, nous avons démontré que la précision de notre modèle augmente avec l’augmentation de la taille du dataset.
Les résultats obtenus nous encouragent à améliorer davantage notre modèle. Nous avons défini les perspectives suivantes:
- –
Générer plus de données pour améliorer la précision du modèle.
- –
Diversifier la génération de programmes en introduisant des nouvelles opérations (comparaison, max, etc.). D’une autre part, le générateur de codes se restreint sur les optimisations locales suivantes tuilage, interversion, déroulage de boucles et la parallélisation. Il faut améliorer le générateur de codes pour qu’il puisse générer toutes les optimisations locales de Tiramisu telles que la torsion de boucles (loop skewing). Cette diversification des éléments du dataset permet d’améliorer la précision du modèle.
- –
Étendre la solution pour traiter une classe de programmes plus large. En effet, notre solution résout le problème de sélection du facteur de déroulage pour une seule computation (voir IV.2.2). Des optimisations globales telles que la fusion de boucles permettent de regrouper un ensemble de nids de boucles (qui ne sont pas parfaitement imbriqués).
- –
Tester la solution sur le problème de sélection des paramètres pour d’autres optimisations de boucles tels que l’optimisation de tuilage et la vectorisation.
- –
Nous visons à long terme, l’extension de notre solution pour cibler d’autres plateformes notamment le GPU. Ceci permet d’explorer d’autres optimisations de boucles qu’offre Tiramisu.
Pour conclure, ce projet de fin d’études nous a permis d’explorer une nouvelle approche de résolution du problème de sélection du meilleur facteur de déroulage. Le modèle de prédiction basé sur les réseaux de neurones profonds donne des résultats encourageants pour continuer ce chemin et réaliser des versions plus améliorées.
Annexes
Annexe A Détails sur l’optimisation de déroulage et de tuilage
A.1 Optimisation de déroulage
L’idée derrière l’optimisation de déroulage est de répliquer les instructions à l’intérieur de la boucle plusieurs fois. Le nombre de réplication est appelé le facteur de déroulage. Le nombre d’itération est divisé par ce facteur. Par exemple, si on réplique une fois le corps d’une boucle dont l’étendue est 100, le nombre d’itérations peut être réduit de 100 à 50.
Cette transformation est clairement utile pour diminuer le nombre de branchements exécutés ainsi que les instructions de maintenances (avancement des adresses et comptage d’itérations). Pour montrer ça, considérant le code "C" ci-dessous qui permet de calculer la somme de 100 entrées du vecteur A.
Le code assembleur équivalent est donné dans la figure A.2 (Initialiser le compteur de la boucle (f10) à 0, initialiser le pointeur de A[i] ($5) sur l’adresse de base de A). Les instructions “addi” dans ce code sont les instructions de maintenances de la boucle.
L’application de déroulage sur le code précédent avec un facteur de 4 donne le code résultat de la figure A.3. Notez que les déplacements dans les chargements (loads) sont incrémentés par le compilateur dans les instructions répliquées (la deuxième, la troisième et la quatrième copies des instructions du corps de la boucle d’origine). La boucle avance avec un pas de 4, les valeurs immédiates dans les instructions "addi" ont été multipliées par 4, de telle sorte que l’effet d’une itération dans la boucle dont nous appliquons la transformation de déroulage est identique à celui de 4 itérations de la boucle d’origine. Les branchements et les instructions de maintenance de boucles ont été réduites d’un facteur de 4 UMD (\APACyear2019).
La transformation de déroulage s’applique facilement aux boucles de traitement de tableaux séquentiels où le nombre d’itérations est connu avant l’exécution de la boucle. Évidemment, il faut bien choisir le facteur de déroulage pour ne pas dégrader les performances au lieu de les améliorer.
A.1.1 Avantages de l’optimisation de déroulage
L’optimisation de déroulage a des effets directs et indirects sur le code que le compilateur peut produire sur une boucle donnée. Les performances finales dépendent de ces deux effets. Les avantages directs peuvent être la réduction des instructions de test et de branchement et la réduction du trafic mémoire en créant des réutilisations dans le corps de la boucle. Parmi les effets indirects ScienceDirect (\APACyear2019) :
- •
L’augmentation du nombre d’opérations indépendantes dans le corps de la boucle peut améliorer le schedule des instructions. Avec plus d’opérations, le planificateur (scheduler) a plus de chances de garder plusieurs unités fonctionnelles occupées et de masquer la latence des opérations de longues durées telles que les instructions de branchement et les accès à la mémoire.
- •
Le déroulage peut déplacer des accès mémoire consécutifs dans la même itération de boucle, où le compilateur peut les scheduler ensemble, ce qui permet d’améliorer la localité.
- •
Cette optimisation peut améliorer la réutilisation des registres ce qui affecte radicalement la vitesse du code.
A.1.2 Inconvénients du déroulage
L’optimisation du déroulage est très utile comme elle améliore indirectement nombreux aspects de performance de système (l’architecture du registre, système de mémoire, etc). Puisque cette transformation peut avoir des effets secondaires, il est difficile de décider quand l’application de cette transformation est appropriée. Superficiellement, cette optimisation apparaît comme une optimisation qui est toujours bénéfique. Cependant, elle peut nuire aux performances dans certains cas. Les inconvénients possibles de cette optimisation sont Stephenson \BBA Amarasinghe (\APACyear2005) :
- •
L’inconvénient le plus avéré de cette optimisation est qu’elle peut provoquer l’expansion du code ce qui peut dégrader les performances du cache d’instructions.
- •
Il est possible que la boucle déroulée demande plus de registres que l’originale. Dans le cas ou cette demande entraîne des spills111S’il n’y a pas assez de registres pour contenir toutes les variables, certaines variables peuvent être déplacées vers et depuis la RAM. Ce processus s’appelle ”renverser” (spills) les registres. (stockage et rechargement) supplémentaire, le trafic mémoire résultant peut annuler les avantages potentiels de l’optimisation.
- •
Si le compilateur ne peut pas déterminer qu’une boucle peut prendre une sortie précoce, il faudra qu’il ajoute un flux de contrôle222 Flux d’instructions ou Control flow : sont en particulier des instructions de contrôles : if, for, while, break, appels de fonctions, etc. (control flow) pour la boucle déroulée, ce qui risque de nier ou neutraliser les avantages de l’optimisation.
A.2 Optimisation de tuilage
Le tuilage est une optimisation qui transforme une boucle en une autre boucle, qui effectue le même calcul, mais dans un ordre différent. Elle divise les données de la boucle en sous-blocs de petites tailles déterminées par le facteur de tuilage. Ce partitionnement en blocs permet aux éléments de données requises par un bloc de tenir dans la mémoire locale. Ceci permet d’améliorer la localité de données et d’exploiter le parallélisme. Chaque boucle est transformée en deux boucles : une itère à l’intérieur de chaque bloc (intra-tuile) et l’autre itère à travers les blocs (inter-tuile). Cependant, le choix du facteur de tuilage est un problème critique comme il affecte considérablement les performances du programme. Si le facteur de tuilage est trop petit, il conduit à une mauvaise exploitation de ressources (ex : cache de données). Tandis qu’un facteur de tuilage trop grand augmente les défauts de cache et affaiblit ou dégrade les performances.
Le déroulage consiste en deux étapes : le découpage en bandes de la boucle (strip mining) et la permutation de boucles. Durant le découpage en bandes, la boucle est divisée en intra-tuile et inter-tuile (intra-tile et inter-tile) boucles333La première (intra-tile) itère à l’intérieur des blocs et la dernière (inter-tile) itère à travers les boucles.. Pendant la phase de permutation, les boucles inter-tuiles sont déplacées vers l’extérieur.
Dans la figure A.4, nous avons découpé la boucle originale "i" en intra-tuile boucle "i" et inter-tuile boucle "it". La boucle originale "j" est aussi découpée en intra et inter-tuile boucles "j" et "jt" respectivement.
L’étape suivante est de permuter les boucles résultantes de telle sorte que les boucles inter-tuile soient déplacées à l’extérieur et les boucles intra-tuile soient déplacées à l’intérieur (voir la figure A.5).
Dans le code ci-dessus, "T" est le facteur de tuilage. Le choix du facteur "T" détermine si le cache sera bien ou mal utilisé. Le tuilage de la boucle originale peut être présentée comme dans la figure A.6.
A.2.1 Aspects à considérer dans la conception du modèle de tuilage
Plusieurs modèles sont conçus pour prédire le facteur de tuilage le plus optimal. Toutefois, la conception de tels modèles est très complexe. La difficulté de sélection de la taille des blocs (Tile Size Selection) est dû à la hiérarchie de mémoire, complexité du cache, préfecheur matériel (Hardware prefetcher), optimisations complexes du compilateur et l’évolution de l’architecture matérielle. Pour construire un modèle plus précis différents aspects doivent être pris en considération Mohammed \BOthers. (\APACyear2010) :
- •
la réutilisation de données dans l’exécution d’un bloc.
- •
La réutilisation de données entre les blocs.
- •
La disposition de données utilisées par un bloc dans la mémoire.
- •
La pénalité relative au défaut de cache dans chaque niveau de la hiérarchie, qui dépend de la machine.
- •
la politique de remplacement du cache.
- •
l’interaction avec d’autres unités, tel que le préfecheur.
- •
L’interaction avec d’autres optimisations (la vectorisation) pour en profiter de cette dernière.
A.2.2 Approches adoptées de choix du facteur de tuilage optimal
Différentes approches proposées pour trouver le bon facteur de tuilage. Elle sont classées en plusieurs catégoriesGhosh \BBA Balasubramanian (\APACyear2017) :
- •
Approches basées sur les modèles statiques : ce sont des modèles analytiques développés en se basant sur l’observation de l’architecture matérielle, certains détails du logiciel et du comportement d’un ensemble de programmes. Ils donnent directement en sortie le facteur de tuilage optimal pour le programme donné sur une architecture spécifique. L’inconvénient majeur de ce type de modèles, c’est qu’ils ne sont pas portables entre les différents types d’architectures. L’évolution rapide dans les architectures matérielles rend l’utilisation de ces modèles plus difficile.
- •
Approches basées sur des modèles conduits par la recherche empirique : ces approches exécuteent le code pour chaque facteur de tuilage dans l’espace de recherche, le facteur de tuilage qui donne le meilleur temps d’exécution est sélectionné. L’avantage de ces modèles est qu’ils sont portables entre les différentes architectures. Cependant, ces approche ne peuvent pas être adoptées lorsque l’espace de recherche est très grand, comme elles doivent explorer toutes les combinaisons possibles. Par exemple, il est impossible d’utiliser ces approches pour trouver les tailles de tuilages rectangulaires optimales (des blocs de tailles rectangulaires), car l’espace de recherche de ces tailles est exponentiellement plus grand que celui des tuilages carrés.
- •
Approche basés sur l’apprentissage automatique : les modèles basées sur ces approches deviennent plus populaires grâce à leurs portabilités, ainsi qu’ils peuvent être utilisés pour les deux types de tuilage (rectangulaire et carré). Ces modèles sont entraînés avec un petit sous-ensemble de l’espace de recherche global, et donc ils peuvent être utilisés pour le problème de sélection de facteur de tuilage rectangulaire.
Annexe B Commandes de scheduling en Tiramisu
Les commandes de scheduling sont classées en quatre principaux types : les commandes de transformation des nids de boucles, les commandes pour mapper les niveaux des boucles sur l’architecture matérielle, les commandes de manipulation des données et les commandes de synchronisation Baghdadi \BOthers. (\APACyear2019).
B.1 Commandes de transformation des nids de boucles
Il s’agit des commandes d’optimisation qui engendrent des transformations sur la structure du nid de boucles, à savoir éliminer des niveaux de la boucle, les diviser ou les dérouler. Par exemple, soit la computation ("C", , , ), le fait de lui appliquer la commande de découpage en bandes .split (, , , ) change sa structure en éliminant le niveau de boucle et en produisant deux nouveaux niveaux de boucle , , avec l’étendue de est et celui de est égal à l’étendu de divisé par . La figure B.1 montre la transformation de la computation .
B.2 Commandes du mappage des niveaux des boucles sur l’architecture matérielle
Ces commandes permettent de définir comment les niveaux de boucles seront lancées (parallélisés ou vectorisés) selon l’architecture cible, à savoir les niveaux à exécuter sur des blocs GPU ou sur des nœuds donnés dans le cas distribué.
Par exemple, la commande C.gpu (, , ) permet de spécifier que les niveaux , , seront exécutés sur le GPU.
B.3 Commandes de manipulation des données
Utilisées pour la gestion et la structuration de données. Il permet principalement les manipulations suivantes:
- –
allocation des tableaux (tampons).
- –
définition des propriétés des tampons à savoir, s’ils sont stockés dans la machine hôte, dans un périphérique, dans la mémoire partagée ou encore dans la mémoire locale (GPU).
- –
copier les données (entre les différents niveaux hiérarchiques la de mémoire ou entre les nœuds dans le cas de distribué).
- –
configuration des accès aux tampons.
Le programmeur utilise principalement un ensemble de commandes de haut niveau. Cependant, si ces dernières ne sont pas assez précises pour exprimer le niveau de détail souhaité, le programmeur fait recours aux commandes de bas niveau.
B.4 Commandes de synchronisation
Il s’agit des directives assurant la communication et la synchronisation. Par exemple, la commande barrier_at (p, i) permet de définir une barrière de synchronisation111Selon Michel Riveill (dans le cours Communication, Coopération et Concurrence entre processus, 2007) les barrières permettent de synchroniser les processus d’un groupe à un endroit donné du programme. Lorsque c’est le cas, chacun des processus peut reprendre son travail. de la boucle au niveau . En revanche, pour gérer automatiquement les domaines d’itération pour les opérations de synchronisation, le programmeur utilise barrier_at () sans préciser les niveaux de synchronisation ce qui le dispense de calculer les domaines d’itération, notamment quand il explore plusieurs Schedules pour en tirer le meilleurBaghdadi \BOthers. (\APACyear2019).
Il est important de signaler que l’appel de certaines commandes telles que cache_shared_at(), cache_local_at(), allocate_at(), copy_at(), barrier_at(), etc. retourne une opération Tiramisu222Operation en Tiramisu est un type héritant de computation (il peut être donc planifié dans la patrie Schedule) mais il retourne aucune valeur. sur laquelle il est possible d’appliquer les commandes de scheduling.
Nous exposons quelques commandes de scheduling en Tiramisu. Supposons que et sont deux computations, est un tampon et et sont des itérateurs de boucle.
Annexe C Principaux concepts du Réseaux de neurones
Les réseaux de neurones artificiels sont des systèmes inspirés du fonctionnement des neurones biologiques (voir la figure C.1). L’un des types de réseaux de neurones les plus fameux est le perceptron multicouche dit également réseaux de neurones multicouches (MLPs en anglais).
Dans cette partie, nous allons voir les notions de base du perceptron multicouche. Les MLPs sont un cas particulier de réseaux de neurones, mais très souvent lorsque nous disant réseaux de neurones nous pensons aux MLPs comme il est le cas le plus simple et le plus répondu. Bien qu’il présente un autre type de réseau de neurones comme les réseaux de neurones convolutifs, récurrents, etc.
C.1 Principe général
L’idée générale derrière les réseaux de neurones est la séparation de l’espace de données pour fournir une sortie. Grâce à cette séparation, qu’ils peuvent pour une nouvelle donnée de décider à quel ensemble doit-elle appartenir. Si nous avons deux classes par exemple, le modèle à partir des entrées essaye d’apprendre la différence entre eux. Ensuite, il tracera une droite de séparation entre ces deux types de données. Chaque nouvelle entrée sera positionner soit à droite ou à gauche de la droite.
Le réseau de neurones multicouches est un ensemble de neurones organisés en couche. Chaque couche se propage le signal d’entrée à la couche suivante jusqu’à la sortie, en activant ou non au fur et à mesure les neurones.
Une fois la sortie est fournie par le modèle, elle est comparée avec la sortie attendue, puis les liaisons entre les neurones sont mises à jour pour améliorer les résultats à chaque fois.
C.2 Neurone
Les neurones sont l’unité principale qui construit les réseaux de neurones. Ce sont des simples unités de calcul qui ont des signaux d’entrée pondérés. Il produisent un signal de sortie à l’aide d’une fonction d’activation (voir la figure C.2).
C.2.1 Poids (weights)
Des signaux (, , …, ) arrivent au neurone par chaque neurones des couches précédentes. Chaque lien qui amène le signal est pondéré, respectivement , , … , . C’est ces poids qui vont être adaptés tout au long de l’apprentissage pour permettre au réseau de prédire efficacement (en général il prend des valeurs entre [math] et ou et ). La somme de tous ces signaux pondérés est calculé après () et un certain biais () est ajouté.
C.2.2 Biais (bias)
Le biais peut être vue comme un neurone externe supplémentaire. La valeur du biais permet de décaler la fonction d’activation vers la gauche (désactiver le neurone) ou vers la droite (activer le neurone). Ceci peut être essentiel pour l’apprentissage.
C.2.3 Activation (activation)
Les entrées pondérées sont additionnées et transmises via une fonction d’activation (activation function), parfois appelée fonction de transfert, pour obtenir le signal de sortie. Elle est appelée une fonction d’activation, car elle régit le seuil d’activation du neurone.
- •
La formule de sortie des neurones cachés est de la forme
- •
Aucun calcule n’est effectuée aux neurones d’entrée.
- •
La formule des neurones de sortie est : .
Dans la pratique, les poids et les biais sont initialisés au hasard lors de la création du réseau de neurones. Au niveau de la fonction d’activation, aucun calcule n’est effectuée pour les neurones d’entrée. Pour les couches cachées plusieurs fonctions d’activation peuvent être considérées (sigmoïde, relu, …). La sortie est fournie directement, mais quelques fonctions peuvent être appliquées comme “softmax” au cas de classification.
C.3 Couches du MLP
Les réseaux de neurones multicouches sont composés de trois parties principales (voir la figure C.3) :
- •
Couche d’entrée (input layer) : elle forme la première couche du modèle. Elle est constituée d’un ensemble de neurones qui portent les entrées du modèle. Tous les neurones de cette couche d’entrée sont reliés aux neurones de la couche suivante.
- •
Couches cachées (hidden layers) : les couches postérieures à la couche d’entrée sont appelées couches cachées, car elles ne sont pas directement exposées à l’entrée. Ce sont un ensemble de couches où chacune est formée d’un ensemble de neurones. Le réseau de neurones peut avoir plusieurs couches cachées. Lorsque le nombre de couches cachées est supérieur à deux le réseau est appelé un réseau de neurones profond. Le choix du bon nombre de neurones par couche et le bon nombre de couches est très difficile. Généralement, deux couches suffisent pour la plupart des problèmes. Aller au-delà de 6 à 10 couches cause très souvent des problèmes de surapprentissage111Le problème de surapprentissage apparaît lorsque le réseau a tellement appris qu’il ne peut plus généraliser. (overfitting). En pratique, on a souvent au moins autant de neurones par couche que ce qu’on avait d’entrées.
- •
Couche de sortie (output layer) : la couche finale est appelée la couche de sortie. Elle représente le résultat final (la prédiction) de notre réseau.
C.4 Entraînement du Réseau de neurones
Une fois configuré, le réseau de neurones doit être entrainé sur un ensemble de données appelé dataset d’entainement. Ce processus est effectué sur plusieurs étapes:
C.4.1 Préparation de données
D’abord, il faut préparer les données pour former le réseau de neurones. Les données fournies aux réseaux de neurones doivent être numériques. Si le dataset contient des valeurs qui ne sont pas numériques il faut les transformer en valeurs numériques. L’une des techniques de transformation qui peut être utilisé pour ça est appelée le codage one-hot222Le hot encoding est l’une des techniques utilisée pour transformer les données non numériques en données numériques. Si nous disposons de données catégorielles, telles qu’un attribut de sexe avec les valeurs «masculin» et «féminin», nous pouvons utiliser le codage one-hot pour les représenter en valeur réelle. C’est ici qu’une nouvelle colonne est ajoutée pour chaque valeur de classe (deux colonnes dans le cas du sexe masculin et féminin) et un 0 ou 1 est ajouté pour chaque ligne en fonction de la valeur de classe pour cette ligne.. Ce principe est aussi appliqué sur la variable de sortie dans les problèmes de classification multiple. Ce qui permet de facilité la classification. Les réseaux de neurones nécessitent que l’entrée soit mise à l’échelle d’une manière cohérente. Elles peuvent être redimensionnées dans la plage comprise entre 0 et 1. Nous appelons ça la normalisation. Une autre technique répandue consiste à la standardiser de telle sorte que chaque colonne soit centrée réduite.
C.4.2 Propagation de l’information et rétropropagation du gradient
Lorsque la donnée est fournie au modèle, les sorties de tous les neurones la première couche peuvent être calculées en appliquant la formule précédente (somme pondérée plus biais puis fonction d’activation). Avec la sortie de la première couche nous pouvons calculer la sortie de la deuxième couche et ainsi de suite jusqu’à la sortie. Donc l’information est propagée dans l’ensemble du réseau. Nous appelons ça la propagation de l’information. C’est le même processus qui est utilisé pour prédire une nouvelle entrée après que le réseau est entrainé.
La sortie prédite par le modèle est comparée avec la sortie attendue puis l’erreur commise est calculé. Cette erreur est propagée en arrière (propagated back) couche par couche, et les poids sont mis à jour en fonction du degré de leurs contributions à l’erreur. Nous appelons ça la rétropropagation du gradient.
Le processus est répété pour tous les exemples (samples) du dataset d’entraînement. L’une des passes pour entraîner le réseau de neurones avec tous l’ensemble de données est appelé nombre d’itération (epochs). Le réseau de neurones pout être entrainé pour dizaines, centaines, ou même des milliers d’itérations afin d’améliorer la précision du modèle à chaque fois. Le nombre d’itération est l’un des paramètres qui doit être bien choisie pour avoir un bon modèle.
C.4.3 Mise à jour du poids (updating weights)
Les poids dans les réseaux de neurones peuvent être mis à jour à partir des erreurs calculées pour chaque exemple des données de traitements. Nous appelons ça l’apprentissage en ligne. Cala permet de faire des mises à jour rapides mais également chaotiques au réseau.
Une autre alternative consiste à enregistrer l’erreur au niveau de tous les exemples du dataset d’entraînement, et puis les mises à jour sont effectuées vers la fin. Cela s’appelle l’apprentissage par lots (batch learning) qui est souvent plus stable. Vu que le nombre d’exemples dans le dataset est très grand, la taille du lot est réduite au petit nombre d’exemple tel que cent exemples afin d’augmenter l’efficacité de calcul.
La valeur dont les poids sont mis à jour est contrôlée par un paramètre appelé le taux d’apprentissage (learning rate). La modification apportée au poids du réseau pour une erreur donnée est souvent de petite taille telle que ou ou plus petit.
C.4.4 Prédiction
Une fois le réseau de neurones est formé, il peut être utilisé pour prédire de nouvelles données. Nous pouvons effectuer des prédictions sur les données de test ou de validation afin d’évaluer la précision du modèle pour des nouvelles données non déjà vues. Le modèle peut être également déployé pour être utilisé pour la prédiction d’une manière continue. La topologie du modèle et les dernières valeurs du poids et de biais est tout ce qu’il doit être sauvegardé du modèle. La prédiction est établie en fournissant une entrée au modèle, puis ce dernier effectue la propagation ce qui lui permettra de générer la sortie qui peut être utilisée après comme une prédiction.
C.5 Classes principales de réseau de neurones
De nouveaux modèles sont publiés chaque jours. Comme il n’est pas facile de distinguer de ce qui fonctionne bien les praticiens recommande d’attendre qu’un modèle sera stable avant de l’adopter.
Il existe trois classes de réseaux de neurones artificiels les plus recommandées :
- •
Les réseaux de neurones multicouches (MLPs)
- •
Les réseaux de neurones convolutifs (CNNs)
- •
Les réseaux de neurones récursifs (RNNs)
Ces trois types sont plus flexibles et prouvent leurs utilités dans la résolution de grands nombres de problèmes. Ils ont également de nombreux sous-types spécialiser dans la résolution de certains problèmes spécifiques.
Les MLPs sont le type classique de réseaux de neurones. Ils sont composés d’une ou plusieurs couches. L’entrée est connectée avec la sortie à travers les couches intermédiaires ou appeler couches cachées. Ils conviennent au problème de prédiction de classification333Problème de classification est un type de problèmes dans lequel une classe ou une étiquette est attribuée aux entrées. et aussi de régression444Problème de régression est un type de problèmes dans lequel une valeur réelle est prédite à partir d’un ensemble d’entrées. là où les données sont souvent fournisses sous forme de tableau, comme dans un fichier CSV555Format de fichier utilisé pour stocker les données. ou une feuille de calcul. Ils sont très flexibles et peuvent généralement être utilisés pour apprendre à mapper des entrées en sorties.
Les CNNs sont un type de réseaux de neurones désigné pour mapper les données sous formes d’images à une variable de sortie. Ils se sont avérés si efficaces qu’ils sont le premier choix pour tout type de problème de prédiction impliquant les images en entrée. Les CNNs fonctionnent bien avec des données ayant une relation spatiale666La relation spatiale est une propriété des données et pour laquelle nous utilisons les CNNs. Cette propriété définit que les points d’une unité de données sont liés les unes aux autres de telle sorte qu’elles ne peuvent pas être séparées. Et par conséquent, si nous faisant ça, ou si nous les modifiant indépendamment l’unité de donnée sera corrompue. Les images et les données audio sont de type de données qui représente une relation spatiale..
Les RNNs sont créés pour traiter les problèmes de prédiction de séquence de données comme les textes. Les RNNs ont eu le plus de succès lorsqu’ils sont utilisés avec des séquences de mots et des paragraphes, généralement appelées traitement de langage naturel (NLP). Ils sont également utilisés comme modèles génératifs nécessitant une sortie séquentielle non seulement avec du texte, mais également dans des applications telles que la génération d’écriture manuscrite. Les réseaux de neurones récurrents ne sont pas appropriés dans le cas de données tabulaires, comme dans un fichier CSV ou un tableur. Ils ne sont également pas appropriés pour les entrées de types images.
Annexe D Amélioration des performances du réseau de neurones
Il existe plusieurs techniques qui visent à améliorer les performances des réseaux de neurones, car initialement, ces performances peuvent souvent ne pas être satisfaisantes. Les principaux facteurs conceptuels causant la dégradation de performances sont le problème du sur apprentissage et le mauvais choix des hyperparamètres. Nous avons sélectionné certaines techniques d’amélioration de performance pour les tester sur notre modèle.
D.1 Diagnostiquer le modèle pour vérifier surapprentissage
Le surapprentissage111Appelé aussi sur-ajustement. survient lorsque le modèle commence à mémoriser les valeurs de données d’apprentissage au lieu d’apprendre avec. Par conséquent lorsque le modèle rencontre de nouvelles données qu’il n’a jamais vues auparavant, il n’est pas en mesure de prédire correctement. Nous pouvons vérifier ce phénomène en comparant la précision du modèle pour le dataset du test par celle du dataset d’entraînement. Si la précision de l’entraînement est bien supérieure à celle du test, il est fort possible que le modèle a été sur-ajusté. Nous pouvons également vérifier le surapprentissage en visualisant les points prédits par le modèle sur un graphe et étudier leur dispersion (voir figure D.1).
Les techniques suivantes peuvent être utilisées pour remédier au problème de surapprentissage :
D.2 Régularisation de données
La régularisation est une technique qui apporte de légères modifications à l’algorithme d’apprentissage de telle sorte que le modèle généralise mieux en pénalisant les poids des neurones. Cela améliore également les performances du modèle sur les données non déjà vue. Les régularisations et représentent les types de régularisation les plus répondus. Elles permettent de mettent à jour la fonction de coût générale en ajoutant un autre terme appelé terme de régularisation : fonction du coût devient :
D.2.1 Technique du Dropouts
La technique du Dropouts permet d’ignorer certains neurones choisis aléatoirement pendant la phase d’entraînement. Les neurones développent une co-dépendance pendant l’entraînement, ce qui limite la puissance individuelle de chaque neurone menant ainsi le modèle au surapprentissage. L’utilisation de cette technique permet d’abandonner aléatoirement des connexions entre les neurones et oblige le réseau à trouver de nouveaux chemins.
D.2.2 Technique de early stopping
Appelée aussi l’arrêt précoce, cette technique permet d’interrompre l’entraînement avant que les poids ne convergent. Souvent, nous nous arrêtons lorsque la précision cesse d’améliorer pour l’ensemble de validation.
D.3 Choix de bons hyperparamètres
Les hyperparamètres sont des valeurs d’initialisation données au réseau. Elles ne peuvent pas être apprises par le réseau pendant l’entraînement. Certains hyperparamètres sont de type architecturals tels que le nombre de couches de réseau et le nombre de neurones dans chaque couche cachée, d’autres sont comportementals, à savoir la fonction d’activation, la fonction de perte (loss function), l’optimiseur, la taille du lots (batch size) et le nombre d’itérations (epochs). Il n’existe pas une méthode directe qui permet d’identifier les meilleurs hyperparamètres, ils sont principalement obtenus par tests. Or, il existe certaines bonnes pratiques que nous avons prises en considération pour choisir certains hyperparamètres.
D.3.1 Fonction d’activation
Le choix de la bonne fonction d’activation permet au modèle de mieux apprendre. Certain fonctions d’activations comme le Sigmoid et le Tanh sont incapables de résoudre le problème de la disparition des gradients222Les valeurs des gradients diminuent lorsqu’elles atteignent les couches initiales. (vanishing gradients) contrairement à la fonction ReLU333ReLu est fonction d’activation qui retourne le max entre le [math] et la valeur d’entrée (). qui est devenue la fonction la plus utilisée. Cette dernière résout ce problème, elle a permis aux architectures de réseaux de neurones d’être plus profondes et d’voir des tailles plus grandes.
D.3.2 Taux d’apprentissage (learning rate)
Le choix du bon taux d’apprentissage est important car il détermine si le modèle converge ou non vers les minima globaux. Un taux élevé ne mène presque jamais au minimum global. Un très petit taux peut prend énormément de temps pour converger vers un minima global. Il peut également être piéger dans un minimum local et donc, le réseau risque de converger vers un minimum local et ne pourra pas en sortir à cause du faible taux d’apprentissage. Généralement le taux d’apprentissage est une puissance de et varie entre et ).
D.3.3 Taille de lot et nombre d’itérations
Il n’y a pas de valeurs standards pour la taille du lot et le nombre d’itérations qui fonctionnent pour tous les types de problèmes. Il faut tester les différentes valeurs. Les puissances de deux sont en général les tailles de lot les plus utilisées. Le nombre d’itérations nécessaire est généralement défini par les tests ou par la technique de Early stopping motionné ci-dessus.
D.3.4 Initialisation du poids
Lors de l’entraînement des réseaux de neurones, les poids initiaux sont attribués de manière aléatoire. Pour des architectures multicouches, les poids initiaux aléatoires ne fonctionnent pas bien. Il est possible de fournir des poids initiaux optimaux. Plusieurs méthodes peuvent être utilisées pour initialiser les poids, tel que l’initialisation aléatoire et l’initialisation des poids Xavier444Une heuristique qui crée une distribution uniforme par couche du gradient.. La méthode qui convient le mieux au problème est celle qui doit être choisie. Ceci est déterminé en effectuant des tests de comparaisons entre les méthodes.
Références
- Agakov \BOthers. (\APACyear2006)
\APACinsertmetastarAgakov2006{APACrefauthors}Agakov, F., Bonilla, E., Cavazos, J., Franke, B., Fursin, G., O’Boyle, M\BPBIF\BPBIP.\BCBL \BBA Thomson, J.
\APACrefYearMonthDay2006.
\BBOQ\APACrefatitleUsing Machine Learning to Focus Iterative Optimization Using machine learning to focus iterative optimization.\BBCQ
\PrintBackRefs\CurrentBib
- Allen \BBA Kennedy (\APACyear2002)
\APACinsertmetastarAllen2002Optim{APACrefauthors}Allen, R.\BCBT \BBA Kennedy, K.
\APACrefYear2002.
\APACrefbtitleOptimizing compilers for modern architectures : a dependence-based approach Optimizing compilers for modern architectures : a dependence-based approach (\BVOL 1).
\APACaddressPublisherMorgan Kaufmann San Francisco. \PrintBackRefs\CurrentBib
- Ashouri \BOthers. (\APACyear2018)
\APACinsertmetastarAshouri2018Survey{APACrefauthors}Ashouri, A\BPBIH., Killian, W., Cavazos, J., Palermo, G.\BCBL \BBA Silvano, C.
\APACrefYearMonthDay2018.
\BBOQ\APACrefatitleA Survey on Compiler Autotuning Using Machine Learning A survey on compiler autotuning using machine learning.\BBCQ
\APACjournalVolNumPagesACM Comput. Surv.. \PrintBackRefs\CurrentBib
- Bacon \BOthers. (\APACyear1994)
\APACinsertmetastarDavid1994Compilertransformations{APACrefauthors}Bacon, D\BPBIF., Graham, S\BPBIL.\BCBL \BBA Sharp, O\BPBIJ.
\APACrefYearMonthDay1994.
\BBOQ\APACrefatitleCompiler transformations for high-performance computing Compiler transformations for high-performance computing.\BBCQ
\PrintBackRefs\CurrentBib
- Baghdadi \BOthers. (\APACyear2015)
\APACinsertmetastarBaghdadi2015Pencil{APACrefauthors}Baghdadi, R., Cohen, A., Grosser, T., Verdoolaege, S., Lokhmotov, A., Absar, J.\BCBL \BBA van Haastregt, S.
\APACrefYearMonthDay2015.
\APACrefbtitlePENCIL Language Specification Pencil language specification \APACbVolEdTR\BTR. \PrintBackRefs\CurrentBib
- Baghdadi \BOthers. (\APACyear2019)
\APACinsertmetastarBaghdadi2018TiramisuV3{APACrefauthors}Baghdadi, R., Ray, J., Ben Romdhane, M., Del Sozzo, E., Akkas, A.\BCBL \BBA Zhang, Y.
\APACrefYearMonthDay2019\APACmonth02.
\BBOQ\APACrefatitleTiramisu: A Polyhedral Compiler for Expressing Fast and Portable Code Tiramisu: A polyhedral compiler for expressing fast and portable code.\BBCQ
\APACjournalVolNumPagesProceedings of the 2019 International Symposium on Code Generation and Optimization (CGO 2019).
{APACrefURL} http://groups.csail.mit.edu/commit/papers/18/tiramisu_paper.pdf
\PrintBackRefs\CurrentBib
- BenRomdhane (\APACyear2017)
\APACinsertmetastarMalek2017ExtendingTiramiu{APACrefauthors}BenRomdhane, M.
\APACrefYear2017.
\APACrefbtitleExtending the Capabilities of Tiramisu Extending the capabilities of tiramisu \APACtypeAddressSchool\BUMTh.
\APACaddressSchoolMassachusetts Institute of Technology (MIT). \PrintBackRefs\CurrentBib
- Bock \BOthers. (\APACyear2018)
\APACinsertmetastarAdamOptimizer{APACrefauthors}Bock, S., Goppold, J.\BCBL \BBA Weiss, M.
\APACrefYearMonthDay2018.
\BBOQ\APACrefatitleAn improvement of the convergence proof of the ADAM Optimizer An improvement of the convergence proof of the adam optimizer.\BBCQ
\APACjournalVolNumPagesCoRR. \PrintBackRefs\CurrentBib
- Bondhugula \BBA Hartono (\APACyear2008)
\APACinsertmetastarpolyhedral{APACrefauthors}Bondhugula, U.\BCBT \BBA Hartono, A.
\APACrefYearMonthDay2008.
\BBOQ\APACrefatitleA Practical Automatic Polyhedral Parallelizer and Locality Optimizer A practical automatic polyhedral parallelizer and locality optimizer.\BBCQ
\APACjournalVolNumPagesSIGPLAN Not.. \PrintBackRefs\CurrentBib
- Cascaval \BOthers. (\APACyear2000)
\APACinsertmetastarCascaval2000AnalyticTimeEstimation{APACrefauthors}Cascaval, C., Rose, L\BPBID., Padua, D\BPBIA.\BCBL \BBA Reed, D\BPBIA.
\APACrefYearMonthDay2000.
\BBOQ\APACrefatitleCompile-Time Based Performance Prediction Compile-time based performance prediction.\BBCQ
\BIn \APACrefbtitleProceedings of the 12th International Workshop on Languages and Compilers for Parallel Computing. Proceedings of the 12th international workshop on languages and compilers for parallel computing.
\APACaddressPublisherBerlin, HeidelbergSpringer-Verlag.
{APACrefURL} http://dl.acm.org/citation.cfm?id=645677.663790
\PrintBackRefs\CurrentBib
- Cavazos \BOthers. (\APACyear2007)
\APACinsertmetastarCavazos2007PerformanceCounters{APACrefauthors}Cavazos, J., Fursin, G., Agakov, F., Bonilla, E., O’Boyle, M\BPBIF\BPBIP.\BCBL \BBA Temam, O.
\APACrefYearMonthDay2007.
\BBOQ\APACrefatitleRapidly Selecting Good Compiler Optimizations Using Performance Counter Rapidly selecting good compiler optimizations using performance counter.\BBCQ
\PrintBackRefs\CurrentBib
- Ciorba (\APACyear2008)
\APACinsertmetastarCiorba2008ParallelLops{APACrefauthors}Ciorba, F\BHBIM.
\APACrefYear2008.
\APACrefbtitleAlgorithms Design for the Parallelization of Nested Loops Algorithms design for the parallelization of nested loops \APACtypeAddressSchool\BPhDNational Technical University Of Athens School of Electrical And Computer Engineering Department Of Informatics And Computer Technology Computing Systems Laboratory.
{APACrefURL} http://www.cavs.msstate.edu/publications/docs/2008/04/7116PD2008-0008.pdf
\PrintBackRefs\CurrentBib
- Cooper \BOthers. (\APACyear1999)
\APACinsertmetastarCooper1999AG{APACrefauthors}Cooper, K\BPBID., Schielke, P\BPBIJ.\BCBL \BBA Subramanian, D.
\APACrefYearMonthDay1999.
\BBOQ\APACrefatitleOptimizing for Reduced Code Space Using Genetic Algorithms Optimizing for reduced code space using genetic algorithms.\BBCQ
\APACjournalVolNumPagesSIGPLAN Not.. \PrintBackRefs\CurrentBib
- Elloumi (\APACyear2013)
\APACinsertmetastarYaroub2013BclnestOptm{APACrefauthors}Elloumi, Y.
\APACrefYear2013.
\APACrefbtitleParallélisme des nids de boucles pour l’optimisation du temps d’exécution et de la taille du code Parallélisme des nids de boucles pour l’optimisation du temps d’exécution et de la taille du code \APACtypeAddressSchool\BPhDUniversité Paris-Est École Doctorale Mathématiques et STIC, Université de Sfax École Doctorale Sciences et Technologies.
{APACrefURL} https://hal.archives-ouvertes.fr/tel-01338975
\PrintBackRefs\CurrentBib
- Georgios Zacharopoulos (\APACyear2018)
\APACinsertmetastarMLAforLoopUnrollingFactorPredictionInHighLevelSynthesis{APACrefauthors}Georgios Zacharopoulos, G\BPBIA\BPBIL\BPBIP., Andrea Barbon.
\APACrefYearMonthDay2018.
\BBOQ\APACrefatitleMachine Learning Approach for Loop Unrolling Factor Prediction in High Level Synthesis Machine learning approach for loop unrolling factor prediction in high level synthesis.\BBCQ
\APACjournalVolNumPages2018 International Conference on High Performance Computing and Simulation. \PrintBackRefs\CurrentBib
- Ghosh \BBA Balasubramanian (\APACyear2017)
\APACinsertmetastarhTSS2017{APACrefauthors}Ghosh, A.\BCBT \BBA Balasubramanian, S.
\APACrefYearMonthDay2017August.
\BBOQ\APACrefatitleA Hybrid Method for Selection of Tile Sizes A hybrid method for selection of tile sizes.\BBCQ
{APACrefURL} http://academicscience.co.in/admin/resources/project/paper/f201708221503402231.pdf
\PrintBackRefs\CurrentBib
- Goodfellow \BOthers. (\APACyear2016)
\APACinsertmetastarGoodfellow{APACrefauthors}Goodfellow, I., Bengio, Y.\BCBL \BBA Courville, A.
\APACrefYear2016.
\APACrefbtitleDeep Learning Deep learning.
\APACaddressPublisherMIT Press.
\APACrefnotehttp://www.deeplearningbook.org \PrintBackRefs\CurrentBib
- Hochreiter \BBA Schmidhuber (\APACyear1997)
\APACinsertmetastarLSTM{APACrefauthors}Hochreiter, S.\BCBT \BBA Schmidhuber, J.
\APACrefYearMonthDay1997.
\BBOQ\APACrefatitleLong Short-Term Memory Long short-term memory.\BBCQ
\APACjournalVolNumPagesNeural Comput.. \PrintBackRefs\CurrentBib
- Jang \BOthers. (\APACyear2010)
\APACinsertmetastarByunghyun2010DataTransformations{APACrefauthors}Jang, B., Mistry, P., Schaa, D., Dominguez, R.\BCBL \BBA Kaeli, D.
\APACrefYearMonthDay2010.
\BBOQ\APACrefatitleData Transformations Enabling Loop Vectorization on Multithreaded Data Parallel Architectures Data transformations enabling loop vectorization on multithreaded data parallel architectures.\BBCQ
\PrintBackRefs\CurrentBib
- Kisuki \BOthers. (\APACyear2000)
\APACinsertmetastariterativeCompilationFactors{APACrefauthors}Kisuki, T., Knijnenburg, P\BPBIM\BPBIW.\BCBL \BBA O’Boyle, M\BPBIF\BPBIP.
\APACrefYearMonthDay2000.
\BBOQ\APACrefatitleCombined Selection of Tile Sizes and Unroll Factors Using Iterative Compilation Combined selection of tile sizes and unroll factors using iterative compilation.\BBCQ
\BIn \APACrefbtitleProceedings of the 2000 International Conference on Parallel Architectures and Compilation Techniques. Proceedings of the 2000 international conference on parallel architectures and compilation techniques.
\APACaddressPublisherWashington, DC, USAIEEE Computer Society.
{APACrefURL} http://dl.acm.org/citation.cfm?id=517554.825767
\PrintBackRefs\CurrentBib
- Knijnenburg \BOthers. (\APACyear2002)
\APACinsertmetastarKnijnenburg2002{APACrefauthors}Knijnenburg, P\BPBIM\BPBIW., Kisuki, T.\BCBL \BBA O’Boyle, M\BPBIF\BPBIP.
\APACrefYearMonthDay2002.
\BBOQ\APACrefatitleIterative Compilation Iterative compilation.\BBCQ
\BIn E\BPBIF. Deprettere, J. Teich\BCBL \BBA S. Vassiliadis (\BEDS), (\BPGS 171–187).
\APACaddressPublisherNew York, NY, USASpringer-Verlag New York, Inc.
{APACrefURL} http://dl.acm.org/citation.cfm?id=765198.765209
\PrintBackRefs\CurrentBib
- Koseki \BOthers. (\APACyear1997)
\APACinsertmetastarKosekiGraphe{APACrefauthors}Koseki, A., Komastu, H.\BCBL \BBA Fukazawa, Y.
\APACrefYearMonthDay1997.
\BBOQ\APACrefatitleA method for estimating optimal unrolling times for nested loops A method for estimating optimal unrolling times for nested loops.\BBCQ
\PrintBackRefs\CurrentBib
- Kulkarni \BBA Cavazos (\APACyear2012)
\APACinsertmetastarKulkarni2012phaseOrderingANN{APACrefauthors}Kulkarni, S.\BCBT \BBA Cavazos, J.
\APACrefYearMonthDay2012.
\BBOQ\APACrefatitleMitigating the Compiler Optimization Phase-ordering Problem Using Machine Learning Mitigating the compiler optimization phase-ordering problem using machine learning.\BBCQ
\APACjournalVolNumPagesSIGPLAN Not.. \PrintBackRefs\CurrentBib
- Li \BBA Garzaran (\APACyear2008)
\APACinsertmetastarXiaoming2008Matrix{APACrefauthors}Li, X.\BCBT \BBA Garzaran, M\BPBIJ.
\APACrefYearMonthDay2008.
\BBOQ\APACrefatitleOptimizing Matrix Multiplication with a Classifier Learning System Optimizing matrix multiplication with a classifier learning system.\BBCQ
\APACjournalVolNumPagesSpringer-Verlag. \PrintBackRefs\CurrentBib
- Manseri (\APACyear2018)
\APACinsertmetastarMenaceri2008HalideAuto{APACrefauthors}Manseri, I.
\APACrefYear2018.
\APACrefbtitleOptimisation automatique d’une classe de programmes écrits en Halide Optimisation automatique d’une classe de programmes écrits en halide \APACtypeAddressSchool\BUMTh.
\APACaddressSchoolEcole National Supérieur de l’Informatique. \PrintBackRefs\CurrentBib
- Martins \BOthers. (\APACyear2016)
\APACinsertmetastarMartins2016Clustering{APACrefauthors}Martins, L\BPBIG\BPBIA., Nobre, R., Cardoso, J\BPBIa\BPBIM\BPBIP., Delbem, A\BPBIC\BPBIB.\BCBL \BBA Marques, E.
\APACrefYearMonthDay2016.
\BBOQ\APACrefatitleClustering Based Selection for the Exploration of Compiler Optimization Sequences Clustering based selection for the exploration of compiler optimization sequences.\BBCQ
\APACjournalVolNumPagesACM Trans. Archit. Code Optim.. \PrintBackRefs\CurrentBib
- Mendis \BOthers. (\APACyear2018)
\APACinsertmetastarmendis2018ithemal{APACrefauthors}Mendis, C., Amarasinghe, S.\BCBL \BBA Carbin, M.
\APACrefYearMonthDay2018.
\APACrefbtitleIthemal: Accurate, Portable and Fast Basic Block Throughput Estimation using Deep Neural Networks. Ithemal: Accurate, portable and fast basic block throughput estimation using deep neural networks. \PrintBackRefs\CurrentBib
- MilePost (\APACyear2018)
\APACinsertmetastarmilepost{APACrefauthors}MilePost.
\APACrefYearMonthDay2018Juin.
\APACrefbtitleMilepostGCC. Milepostgcc.
{APACrefURL} http://ctuning.org/wiki/index.php/CTools:MilepostGCC:StaticFeatures:MILEPOST_V2.1 \PrintBackRefs\CurrentBib
- Mohammed \BOthers. (\APACyear2010)
\APACinsertmetastarRahmani2010TSS{APACrefauthors}Mohammed, R., Louis-Noel, P.\BCBL \BBA Sadayappan, P.
\APACrefYearMonthDay2010.
\BBOQ\APACrefatitleNeural Network Assisted Tile Size Selection Neural network assisted tile size selection.\BBCQ
{APACrefURL} http://web.cse.ohio-state.edu/~pouchet.2/doc/iwapt-article.10.pdf
\PrintBackRefs\CurrentBib
- Monsifrot \BOthers. (\APACyear2002)
\APACinsertmetastarMonsifrot2002{APACrefauthors}Monsifrot, A., Bodin, F.\BCBL \BBA Quiniou, R.
\APACrefYearMonthDay2002.
\BBOQ\APACrefatitleA Machine Learning Approach to Automatic Production of Compiler Heuristics A machine learning approach to automatic production of compiler heuristics.\BBCQ
{APACrefURL} http://dl.acm.org/citation.cfm?id=646053.677574
\PrintBackRefs\CurrentBib
- Mullapudi \BOthers. (\APACyear2016)
\APACinsertmetastarMullapudi2016HalidAutoScheduler{APACrefauthors}Mullapudi, R\BPBIT., Adams, A., Sharlet, D., Ragan-Kelley, J.\BCBL \BBA Fatahalian, K.
\APACrefYearMonthDay2016\APACmonth07.
\BBOQ\APACrefatitleAutomatically Scheduling Halide Image Processing Pipelines Automatically scheduling halide image processing pipelines.\BBCQ
\PrintBackRefs\CurrentBib
- Mullapudi \BOthers. (\APACyear2015)
\APACinsertmetastarPolyMage{APACrefauthors}Mullapudi, R\BPBIT., Vasista, V.\BCBL \BBA Bondhugula, U.
\APACrefYearMonthDay2015.
\BBOQ\APACrefatitlePolyMage: Automatic Optimization for Image Processing Pipelines Polymage: Automatic optimization for image processing pipelines.\BBCQ
\APACjournalVolNumPagesSIGARCH Comput. Archit. News. \PrintBackRefs\CurrentBib
- Park \BOthers. (\APACyear2012)
\APACinsertmetastarGrapheModeling{APACrefauthors}Park, E., Cavazos, J.\BCBL \BBA Alvarez, M\BPBIA.
\APACrefYearMonthDay2012.
\BBOQ\APACrefatitleUsing Graph-based Program Characterization for Predictive Modeling Using graph-based program characterization for predictive modeling.\BBCQ
\APACaddressPublisherNew York, NY, USAACM. \PrintBackRefs\CurrentBib
- Park \BOthers. (\APACyear2011)
\APACinsertmetastarPark2011IterativeCompilation{APACrefauthors}Park, E., Kulkarni, S.\BCBL \BBA Cavazos, J.
\APACrefYearMonthDay2011.
\BBOQ\APACrefatitleAn Evaluation of Different Modeling Techniques for Iterative Compilation An evaluation of different modeling techniques for iterative compilation.\BBCQ
\PrintBackRefs\CurrentBib
- PRADELLE (\APACyear2011)
\APACinsertmetastarPRADELLE2011polyedrique{APACrefauthors}PRADELLE, B.
\APACrefYear2011.
\APACrefbtitleMéthodes statiques et dynamiques de compilation polyédrique pour l’exécution en environnement multicoeurs Méthodes statiques et dynamiques de compilation polyédrique pour l’exécution en environnement multicoeurs \APACtypeAddressSchool\BPhDUniversité de Strasbourg.
{APACrefURL} http://theses.unistra.fr/ori-oai-search/notice.html?id=oai:EPrintsUneraTest01:2401&printable=true
\PrintBackRefs\CurrentBib
- Ragan-Kelley \BOthers. (\APACyear2013)
\APACinsertmetastarKelly2013Halide{APACrefauthors}Ragan-Kelley, J., Barnes, C., Adams, A., Paris, S., Durand, F.\BCBL \BBA Amarasinghe, S.
\APACrefYearMonthDay2013.
\BBOQ\APACrefatitleHalide : a language and compiler for optimizing parallelism, locality, and recomputation in image processing pipelines Halide : a language and compiler for optimizing parallelism, locality, and recomputation in image processing pipelines.\BBCQ
\PrintBackRefs\CurrentBib
- Ragan-Kelley \BOthers. (\APACyear2012)
\APACinsertmetastarJonathan2012Decouplingalgorithms{APACrefauthors}Ragan-Kelley, J., Paris, S., Adams, A., Levoy, M., Amarasinghe, S.\BCBL \BBA Durand, F.
\APACrefYearMonthDay2012.
\BBOQ\APACrefatitleDecoupling algorithms from schedules for easy optimization of image processing pipe-lines Decoupling algorithms from schedules for easy optimization of image processing pipe-lines.\BBCQ
\PrintBackRefs\CurrentBib
- Ray (\APACyear2018)
\APACinsertmetastarRay2018DistributedTiramiu{APACrefauthors}Ray, J\BPBIM.
\APACrefYear2018.
\APACrefbtitleA Unified Compiler Backend for Distributed Cooperative Heterogeneous Execution A unified compiler backend for distributed cooperative heterogeneous execution \APACtypeAddressSchool\BUMTh.
\APACaddressSchoolMassachusetts Institute of Technology (MIT). \PrintBackRefs\CurrentBib
- Ruder (\APACyear2016)
\APACinsertmetastarNNOptimizers{APACrefauthors}Ruder, S.
\APACrefYearMonthDay2016.
\BBOQ\APACrefatitleAn overview of gradient descent optimization algorithms An overview of gradient descent optimization algorithms.\BBCQ
\APACjournalVolNumPagesCoRR.
{APACrefURL} http://arxiv.org/abs/1609.04747 \PrintBackRefs\CurrentBib
- Sarkar \BBA Megiddo (\APACyear2000)
\APACinsertmetastarSarkarTileAnalytic{APACrefauthors}Sarkar, V.\BCBT \BBA Megiddo, N.
\APACrefYearMonthDay2000.
\BBOQ\APACrefatitleAn analytical model for loop tiling and its solution An analytical model for loop tiling and its solution.\BBCQ
\BIn \APACrefbtitle2000 IEEE International Symposium on Performance Analysis of Systems and Software. ISPASS (Cat. No.00EX422). 2000 IEEE international symposium on performance analysis of systems and software. ISPASS (cat. no.00ex422).
\APACaddressPublisherIEEE. \PrintBackRefs\CurrentBib
- ScienceDirect (\APACyear2019)
\APACinsertmetastarscience_direct_unrolling{APACrefauthors}ScienceDirect.
\APACrefYearMonthDay2019.
\APACrefbtitleLoop Unrolling : Introduction to Optimization,8.5.2 Loop Unrolling. Loop unrolling : Introduction to optimization,8.5.2 loop unrolling.
{APACrefURL} [2019-03-28]https://www.sciencedirect.com/topics/computer-science/loop-unrolling
\PrintBackRefs\CurrentBib
- Smith \BBA Saavedra (\APACyear1995)
\APACinsertmetastarSmith1995MemoryTimeMesuring{APACrefauthors}Smith, A\BPBIJ.\BCBT \BBA Saavedra, R\BPBIH.
\APACrefYearMonthDay1995\APACmonth10.
\BBOQ\APACrefatitleMeasuring Cache and TLB Performance and Their Effect on Benchmark Runtimes Measuring cache and tlb performance and their effect on benchmark runtimes.\BBCQ
\PrintBackRefs\CurrentBib
- Stephenson \BBA Amarasinghe (\APACyear2005)
\APACinsertmetastarsupervised2005{APACrefauthors}Stephenson, M.\BCBT \BBA Amarasinghe, S.
\APACrefYearMonthDay2005.
\BBOQ\APACrefatitlePredicting unroll factors using supervised classification Predicting unroll factors using supervised classification.\BBCQ
\PrintBackRefs\CurrentBib
- Touati \BBA de Dinechin (\APACyear2014)
\APACinsertmetastarTouatiAdvancedBackendOptim{APACrefauthors}Touati, S.\BCBT \BBA de Dinechin, B.
\APACrefYear2014.
\APACrefbtitleAdvanced Backend Optimization - Iste Advanced backend optimization - iste (\PrintOrdinal1st \BEd).
\APACaddressPublisherWiley-IEEE Press. \PrintBackRefs\CurrentBib
- Triantafyllis \BOthers. (\APACyear2005)
\APACinsertmetastarOptimSpace{APACrefauthors}Triantafyllis, S., Vachharajani, M.\BCBL \BBA August, D\BPBII.
\APACrefYearMonthDay2005.
\APACrefbtitleCompiler Optimization-Space Exploration. Compiler optimization-space exploration.
\APACaddressPublisherJournal of Instruction-Level Parallelism.
{APACrefURL} https://liberty.princeton.edu/Publications/jilp_ose.pdf
\PrintBackRefs\CurrentBib
- UMD (\APACyear2019)
\APACinsertmetastarumn_unrolling{APACrefauthors}UMD.
\APACrefYearMonthDay2019.
\APACrefbtitleLoop Unrolling : Array Sum. Loop unrolling : Array sum.
{APACrefURL} [2019-03-28]https://www.d.umn.edu/~gshute/arch/loop-unrolling.xhtml
\PrintBackRefs\CurrentBib
- Whaley \BBA Dongarra (\APACyear1998)
\APACinsertmetastarAtlasref{APACrefauthors}Whaley, R\BPBIC.\BCBT \BBA Dongarra, J\BPBIJ.
\APACrefYearMonthDay1998.
\BBOQ\APACrefatitleAutomatically Tuned Linear Algebra Software Automatically tuned linear algebra software.\BBCQ
{APACrefURL} http://dl.acm.org/citation.cfm?id=509058.509096
\PrintBackRefs\CurrentBib
- Yuki \BOthers. (\APACyear2010)
\APACinsertmetastarYuki{APACrefauthors}Yuki, T., Renganarayanan, L., Rajopadhye, S., Anderson, C., Eichenberger, A\BPBIE.\BCBL \BBA O’Brien, K.
\APACrefYearMonthDay2010.
\APACrefbtitleAutomatic Creation of Tile Size Selection Models. Automatic creation of tile size selection models. \PrintBackRefs\CurrentBib
The reference list from the paper itself. Each links out to its DOI / PubMed record.
- 1Agakov \B Others . ( \APA Cyear 2006) \APA Cinsertmetastar Agakov 2006 {APA Crefauthors} Agakov, F., Bonilla, E., Cavazos, J., Franke, B., Fursin, G., O’Boyle, M \BPBI F \BPBI P. \BCBL \BBA Thomson, J. \APA Cref Year Month Day 2006. \BBOQ \APA Crefatitle Using Machine Learning to Focus Iterative Optimization Using machine learning to focus iterative optimization. \BBCQ \Print Back Refs \Current Bib
- 2Allen \BBA Kennedy ( \APA Cyear 2002) \APA Cinsertmetastar Allen 2002 Optim {APA Crefauthors} Allen, R. \BCBT \BBA Kennedy, K. \APA Cref Year 2002. \APA Crefbtitle Optimizing compilers for modern architectures : a dependence-based approach Optimizing compilers for modern architectures : a dependence-based approach ( \BVOL 1). \APA Caddress Publisher Morgan Kaufmann San Francisco. \Print Back Refs \Current Bib
- 3Ashouri \B Others . ( \APA Cyear 2018) \APA Cinsertmetastar Ashouri 2018 Survey {APA Crefauthors} Ashouri, A \BPBI H., Killian, W., Cavazos, J., Palermo, G. \BCBL \BBA Silvano, C. \APA Cref Year Month Day 2018. \BBOQ \APA Crefatitle A Survey on Compiler Autotuning Using Machine Learning A survey on compiler autotuning using machine learning. \BBCQ \APA Cjournal Vol Num Pages ACM Comput. Surv.. \Print Back Refs \Current Bib
- 4Bacon \B Others . ( \APA Cyear 1994) \APA Cinsertmetastar David 1994 Compilertransformations {APA Crefauthors} Bacon, D \BPBI F., Graham, S \BPBI L. \BCBL \BBA Sharp, O \BPBI J. \APA Cref Year Month Day 1994. \BBOQ \APA Crefatitle Compiler transformations for high-performance computing Compiler transformations for high-performance computing. \BBCQ \Print Back Refs \Current Bib
- 5Baghdadi \B Others . ( \APA Cyear 2015) \APA Cinsertmetastar Baghdadi 2015 Pencil {APA Crefauthors} Baghdadi, R., Cohen, A., Grosser, T., Verdoolaege, S., Lokhmotov, A., Absar, J. \BCBL \BBA van Haastregt, S. \APA Cref Year Month Day 2015. \APA Crefbtitle PENCIL Language Specification Pencil language specification \APA Cb Vol Ed TR \BTR . \Print Back Refs \Current Bib
- 6Baghdadi \B Others . ( \APA Cyear 2019) \APA Cinsertmetastar Baghdadi 2018 Tiramisu V 3 {APA Crefauthors} Baghdadi, R., Ray, J., Ben Romdhane, M., Del Sozzo, E., Akkas, A. \BCBL \BBA Zhang, Y. \APA Cref Year Month Day 2019 \APA Cmonth 02. \BBOQ \APA Crefatitle Tiramisu: A Polyhedral Compiler for Expressing Fast and Portable Code Tiramisu: A polyhedral compiler for expressing fast and portable code. \BBCQ \APA Cjournal Vol Num Pages Proceedings of the 2019 International Symposium on Code Gener
- 7Ben Romdhane ( \APA Cyear 2017) \APA Cinsertmetastar Malek 2017 Extending Tiramiu {APA Crefauthors} Ben Romdhane, M. \APA Cref Year 2017. \APA Crefbtitle Extending the Capabilities of Tiramisu Extending the capabilities of tiramisu \APA Ctype Address School \BUM Th . \APA Caddress School Massachusetts Institute of Technology (MIT). \Print Back Refs \Current Bib
- 8Bock \B Others . ( \APA Cyear 2018) \APA Cinsertmetastar Adam Optimizer {APA Crefauthors} Bock, S., Goppold, J. \BCBL \BBA Weiss, M. \APA Cref Year Month Day 2018. \BBOQ \APA Crefatitle An improvement of the convergence proof of the ADAM Optimizer An improvement of the convergence proof of the adam optimizer. \BBCQ \APA Cjournal Vol Num Pages Co RR. \Print Back Refs \Current Bib
