Avec la naissance des ordinateurs modernes, le problème de savoir comment compiler du code plus rapide et plus petit est apparu.
L'optimisation de la compilation est la méthode d'optimisation présentant le rapport coût-bénéfice le plus élevé. Une meilleure optimisation du code peut réduire considérablement les coûts d'exploitation des grandes applications de centres de données. La taille du code compilé est essentielle pour les systèmes ou logiciels mobiles et embarqués déployés sur une partition de démarrage sécurisée, car les binaires compilés doivent respecter des budgets stricts en matière de taille de code. À mesure que le domaine progresse, des heuristiques de plus en plus complexes réduisent considérablement l'espace limité du système, entravant la maintenance et les améliorations ultérieures.
Des recherches récentes montrent que l'apprentissage automatique peut ouvrir davantage d'opportunités en matière d'optimisation du compilateur en remplaçant les heuristiques complexes par des stratégies d'apprentissage automatique. Cependant, l’adoption de stratégies d’apprentissage automatique dans des compilateurs industriels à usage général reste un défi.
Afin de résoudre ce problème, deux ingénieurs senior de Google, Yundi Qian et Mircea Trofin, ont proposé "MLGO, un framework d'optimisation de compilateur guidé par l'apprentissage automatique". Il s'agit du premier framework général de conversion d'apprentissage automatique de qualité industrielle. Ces techniques sont systématiquement intégrées dans LLVM, une infrastructure de compilateur industriel open source omniprésente dans la création de logiciels critiques et hautes performances.
Adresse papier : https://arxiv.org/pdf/2101.04808.pdf
MLGO utilise l'apprentissage par renforcement pour entraîner les réseaux de neurones à prendre des décisions visant à remplacer les algorithmes heuristiques dans LLVM. Selon la description de l'auteur, il existe deux optimisations MLGO sur LLVM :
1) Réduisez la taille du code grâce à l'inline ;
2) Améliorez les performances du code grâce à l'allocation de registres ;
Les deux optimisations sont disponibles dans le référentiel LLVM et ont été déployées en production.
Inlining aide à réduire la taille du code en prenant des décisions qui suppriment le code redondant. Dans l'exemple ci-dessous, la fonction appelante <code style="font-family: monospace; font-size: 12px; background-color: rgba(0, 0, 0, 0.06); padding: 0px 2px; border-radius: 6px; line-height: inherit; overflow-wrap: break-word; text-indent: 0px;"><span style="font-size: 15px;">foo()</span>
调用被调用者函数 <span style="font-size: 15px;">bar()</span>
,而 <span style="font-size: 15px;">bar()</span>
本身又调用了 <span style="font-size: 15px;">baz()</span>
。内联这两个调用站点将返回一个简单的 <code style="font-family: monospace; font-size: 12px; background-color: rgba(0, 0, 0, 0.06); padding: 0px 2px; border-radius: 6px; line-height: inherit; overflow-wrap: break-word; text-indent: 0px;"><span style="font-size: 15px;">foo()</span>foo()Appelez la fonction appelée
<p style="text-align: center;">bar()<img src="https://img.php.cn/upload/article/000/000/164/168291834816871.png" alt="内存减少3%-7%!谷歌提出用于编译器优化的机器学习框架 MLGO"></p>
, tandis que <span style="font-size: 12px;">bar()<span style="color: #888888;"> code></span> appelle lui-même </span><code style="font-family: monospace; font-size: 12px; background-color: rgba(0, 0, 0, 0.06); padding: 0px 2px; border -radius: 6px ; hauteur de ligne : hériter ; overflow-wrap : break-word ; text-indent : 0px;">
🎜foo()🎜
🎜, qui réduira la taille du code. 🎜🎜🎜🎜🎜🎜🎜🎜Légende : L'inlining réduit la taille du code en supprimant le code redondant🎜🎜🎜Dans le code réel, il existe des milliers de fonctions qui s'appellent les unes les autres, formant ainsi un graphe d'appel. Pendant la phase d'intégration, le compilateur parcourt le graphe d'appel de toutes les paires appelant-appelé et décide s'il doit intégrer une paire appelant-appelé. Il s'agit d'un processus décisionnel continu, car les décisions d'intégration précédentes modifieront le graphique d'appel, affectant les décisions ultérieures et le résultat final. Dans l'exemple ci-dessus, le graphe d'appel <code style="font-family: monospace; font-size: 12px; background-color: rgba(0, 0, 0, 0.06); padding: 0px 2px; border-radius: 6px; line-height: inherit; overflow-wrap: break-word; text-indent: 0px;"><span style="font-size: 15px;">foo()</span>
→ <span style="font-size: 15px;">bar()</span>
→ <span style="font-size: 15px;">baz()</span>
foo() →
<p style="text-align: justify;">bar()<span style="font-size: 15px;"></span></p>
→ <p style="text-align: center;">baz()<img src="https://img.php.cn/upload/article/000/000/164/168291834817462.gif" alt="内存减少3%-7%!谷歌提出用于编译器优化的机器学习框架 MLGO"></p>
Une décision « oui » doit être prise des deux côtés pour réduire la taille du code. Avant MLGO, les décisions en ligne/non en ligne étaient prises par des heuristiques qui devenaient de plus en plus difficiles à améliorer au fil du temps. MLGO remplace l'heuristique par un modèle d'apprentissage automatique. Pendant le parcours du graphe d'appel, le compilateur recherche les recommandations du réseau neuronal sur l'opportunité d'intégrer une paire appelant-appelé spécifique via des fonctionnalités pertinentes (c'est-à-dire des entrées) dans le graphe d'entrée, et exécute les décisions séquentiellement jusqu'à ce que l'intégralité du graphe d'appel soit terminée. est atteint.
Illustration : Illustration de MLGO pendant le processus d'inline, "#bbs", "#users" et "callsite height" sont des instances de propriétés de paire appelant-appelé
MLGO utilise le gradient de politique et l'évolution algorithmes de politique pour la formation RL des réseaux de décision. Bien qu'il n'existe pas de vérité fondamentale sur les décisions optimales, RL en ligne utilise une politique entraînée itérant entre la formation et l'exécution de l'assemblage pour collecter des données et améliorer la politique. En particulier, étant donné le modèle actuellement en formation, le compilateur consulte le modèle pendant la phase d'inline pour prendre une décision en ligne/non en ligne. Après compilation, il produit un journal du processus de décision séquentiel (statut, action, récompense). Ce journal est ensuite transmis au formateur pour mettre à jour le modèle. Ce processus est répété jusqu'à l'obtention d'un modèle satisfaisant.
Légende : Comportement du compilateur pendant la formation
- Le compilateur compile le code source foo.cpp dans le fichier objet foo.o et effectue une série d'optimisations, dont l'une est la route de communication en ligne. La politique formée est intégrée au compilateur, fournissant des décisions en ligne/non en ligne pendant le processus de compilation. Contrairement au scénario de formation, cette stratégie ne génère pas de journaux. Les modèles TensorFlow sont intégrés dans XLA AOT, qui convertit le modèle en code exécutable. Cela évite les dépendances et les frais d'exécution de TensorFlow, minimisant ainsi le temps supplémentaire et les coûts de mémoire introduits par l'inférence du modèle ML au moment de la compilation.
Légende : Comportement du compilateur en production 🎜🎜🎜🎜🎜 Nous avons formé la stratégie d'inline grande et petite sur un grand package interne contenant 30 000 modules. La stratégie formée peut être généralisée lors de la compilation d'autres logiciels et réduit la surcharge de temps et de mémoire de 3 % à 7 %. 🎜En plus de la généralité entre les logiciels, la généralité dans le temps est également importante, les logiciels et les compilateurs sont en cours de développement actif, une stratégie bien entraînée est donc nécessaire pour maintenir de bonnes performances dans un laps de temps raisonnable. Nous avons évalué les performances du modèle sur le même ensemble de logiciels après trois mois et n'avons constaté qu'une légère dégradation. 🎜🎜
Graphique : Pourcentage de réduction de la taille de la stratégie de taille en ligne, l'axe des x représente différents logiciels et l'axe des y représente le pourcentage de réduction. « Training » est le logiciel qui entraîne le modèle, et « InfraX » est un progiciel interne différent.
La formation de redimensionnement en ligne de MLGO a été déployée sur Fuchsia, un système d'exploitation open source à usage général conçu pour alimenter divers écosystèmes matériels et logiciels où la taille binaire est essentielle. Ici, MLGO montre une réduction de 6,3 % de la taille de l’unité de traduction C++.
En tant que cadre général, nous utilisons MLGO pour améliorer le canal d'allocation de registre (allocation de registre) afin d'améliorer les performances du code dans LLVM. L'allocation de registres résout le problème de l'allocation de registres physiques aux étendues actives (c'est-à-dire les variables).
Au fur et à mesure que le code est exécuté, différentes plages en direct sont complétées à différents moments et les registres sont libérés pour être utilisés dans les étapes de traitement ultérieures. Dans l'exemple suivant, chaque instruction « ajouter » et « multiplier » nécessite que tous les opérandes et résultats se trouvent dans des registres physiques. La plage en temps réel x est affectée au registre vert et se termine avant la plage en temps réel du registre bleu ou jaune. Une fois x terminé, le registre vert devient disponible et attribué à la plage en direct t.
Pendant l'exécution du code, différentes plages en direct sont complétées à différents moments et les registres publiés sont utilisés dans les étapes de traitement ultérieures. Dans l'exemple ci-dessous, chaque instruction « ajouter » et « multiplier » nécessite que tous les opérandes et résultats se trouvent dans des registres physiques. La plage active x est affectée au registre vert et se termine avant la plage active du registre bleu ou jaune. Une fois x terminé, le registre vert devient disponible et est affecté à la plage en direct t .
Légende : Exemple d'allocation de registre
Lorsque la plage active q est allouée, aucun registre n'est disponible, le canal d'allocation de registre doit donc décider quelle plage active peut être "expulsée" de ses registres afin que Faites de la place pour q. C'est ce qu'on appelle le problème de « l'expulsion du terrain », et c'est là que nous entraînons le modèle pour remplacer la décision de l'heuristique d'origine. Dans cet exemple, il expulse z du registre jaune et l'attribue à q et à la première moitié de z.
Nous considérons maintenant la moitié inférieure non allouée de la plage z réelle. Nous avons un autre conflit, cette fois la plage active t est expulsée et divisée, la première moitié de t et la dernière partie de z finissent par utiliser le registre vert. La partie médiane de Z correspond à l'instruction q = t * y, où z n'est pas utilisé, il n'est donc alloué à aucun registre, et sa valeur est stockée dans la pile du registre jaune et est ensuite rechargée dans le registre vert . La même chose se produit avec t. Cela ajoute des instructions de chargement/stockage supplémentaires au code, réduisant ainsi les performances. Le but de l’algorithme d’allocation de registre est de minimiser cette inefficacité. Ceci est utilisé comme récompense pour guider la formation politique RL.
Semblable à la politique de dimensionnement en ligne, la politique d'allocation de registre (regalloc-for-Performance) a été formée sur un grand progiciel au sein de Google et peut être généralisée à différents logiciels et appliquée dans un ensemble de grands centres de données internes. des requêtes par seconde (QPS) sur le programme ont augmenté de 0,3 % à 1,5 %. Les améliorations du QPS ont persisté pendant plusieurs mois après le déploiement, démontrant la généralisabilité du modèle.
MLGO utilise l'apprentissage par renforcement pour entraîner les réseaux de neurones à prendre des décisions. Il s'agit d'une stratégie d'apprentissage automatique qui remplace les méthodes heuristiques complexes. En tant que cadre général de qualité industrielle, il sera plus approfondi et plus largement utilisé dans davantage d'environnements que la simple intégration et l'allocation de registres.
MLGO peut être développé pour être : 1) plus profond, comme l'ajout de plus de fonctionnalités et l'application de meilleurs algorithmes RL ; 2) plus large, étant capable d'appliquer davantage d'heuristiques d'optimisation au-delà des méthodes d'intégration et de redistribution.
Les auteurs sont enthousiasmés par les possibilités que MLGO peut apporter au domaine de l'optimisation des compilateurs et attendent avec impatience son adoption ultérieure et ses futures contributions de la communauté des chercheurs.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!