HyperAIHyperAI

Command Palette

Search for a command to run...

Le Noyau Du Compilateur De Bi Sheng, AscendNPU IR, Est Désormais Open source. Hai Lijuan, Architecte Chez Huawei, Explique Les Avancées Techniques Et Souligne Sa Compatibilité Avec Les Écosystèmes d'opérateurs Tels Que Triton.

Featured Image

1er aoûtLe 9e salon technique Meet AI Compiler, organisé par HyperAI, s'est déroulé comme prévu.Des intervenants de renom issus d'entreprises et d'instituts de recherche de premier plan, tels que l'Académie d'intelligence artificielle de Pékin, l'équipe TileRT, Tencent, Huawei et Zhiyuan Innovation, ont mené des discussions approfondies sur l'évolution collaborative des compilateurs d'IA sous de multiples angles : expression du langage, calcul des opérateurs, exécution des inférences et implémentation de scénarios. Ils se sont concentrés sur les difficultés techniques, les solutions pratiques et les tendances du secteur, apportant ainsi des idées variées et une expérience concrète à l'évolution de la technologie des compilateurs d'IA et à l'exploration approfondie de sa valeur industrielle.

sur place,Hai Lijuan, architecte de Huawei AscendNPU IR, a partagé ses réflexions sur le sujet « AscendNPU IR : Base de compilation open source, prenant en charge l'accès multilingue à Ascend ».Elle a présenté de manière systématique l'architecture technique globale et la philosophie de conception du compilateur AscendNPU IR, et a expliqué en détail les nouvelles fonctionnalités d'AscendNPU IR pour les extensions Ascend 950, ses avantages en matière d'itération technologique et le développement de son écosystème communautaire. Elle a analysé les capacités fondamentales et les avantages techniques de la plateforme open source, démontré son adaptabilité à de nombreux langages front-end et présenté aux développeurs une expérience de développement collaborative, conviviale et ouverte avec AscendNPU IR.

L'enseignante Hai Lijuan a participé à la 9e session de Meet AI Compiler.

HyperAI a compilé et résumé les présentations sans en altérer le sens original.

Suivez le compte officiel WeChat « HyperAI » et répondez avec le mot-clé « 0801 AI Compiler » pour obtenir le PPT de la présentation du conférencier autorisé.

Pourquoi la programmation Triton est-elle efficace et facile à utiliser ?

En tant que langage de programmation, Triton est à la fois performant et convivial. D'où provient donc cet avantage en matière de développement ?

En termes simples, Triton utilise une syntaxe similaire à celle de Python et propose une programmation par blocs, ce qui protège les développeurs des détails de bas niveau concernant la mémoire matérielle, les instructions, le pipeline et autres opérations du noyau abstrait, réduisant ainsi la barrière au développement des opérateurs et améliorant l'efficacité du développement.

Le modèle de programmation par tuiles de Triton, comparé aux langages de programmation hétérogènes tels que C++, offre des avantages par rapport aux langages de programmation traditionnels.Cela permet aux développeurs de se concentrer davantage sur le partitionnement des données pour les opérateurs, en privilégiant l'abstraction des opérations tensorielles et l'expression logique.En se basant sur l'abstraction de programmation au niveau des tuiles, à l'exception de la planification des données inter-cœurs qui est gérée par l'utilisateur, la fusion des accès mémoire intra-cœur et la gestion de la mémoire partagée peuvent être effectuées automatiquement par le compilateur.

Plus important encore, le dialecte Triton et le compilateur GPU Triton officiel sont tous deux basés sur une abstraction IR multiniveau construite sur MLIR, permettant une optimisation approfondie pour différents matériels.

AscendNPU IR continue de faire l'objet d'une optimisation architecturale.

AscendNPU IR est une abstraction au niveau des tuiles matérielles Ascend, basée sur MLIR et servant à optimiser la compilation sous-jacente. Son architecture est illustrée dans le diagramme ci-dessous.L'interface AscendNPU IR s'établit en aval avec LLVM IR et est finalement compilée dans le binaire matériel Ascend.Les langages et compilateurs tiers de couche supérieure tels que Triton peuvent être intégrés dans AscendNPU IR, assurant ainsi la compatibilité avec Ascend.

Principales caractéristiques techniques de l'AscendNPU IRPremièrement, il offre une abstraction ascendante pour le matériel Ascend, masquant les différentes ressources au sein du cœur, telles que la mémoire et les unités de transport de données. Deuxièmement, il fournit des opérateurs abstraits au niveau des tuiles, permettant une prise en charge unifiée et multi-architecture du matériel sous-jacent, couvrant les architectures matérielles les plus récentes, d'A2/A3 à Ascend 950 SIMD et SIMT. Grâce à l'ouverture et à l'évolutivité de MLIR, AscendNPU IR prend en charge l'accès depuis un plus grand nombre de langages de couche supérieure.

Le schéma ci-dessous illustre l'architecture de compilation IR d'AscendNPU, depuis les plateformes Ascend A2/A3 jusqu'à la dernière version matérielle Ascend 950 sortie cette année. La partie gauche présente la première version de cette architecture, publiée l'année dernière et composée principalement de deux couches. La couche supérieure, HFusion, indépendante du matériel, implémente principalement l'optimisation de la fusion multidimensionnelle. Elle s'appuie sur Linalg (langage d'organisation linéaire) et des opérateurs opérationnels étendus, et assure la connexion avec différents dialectes standardisés, le prétraitement de la normalisation des types de données et des opérateurs opérationnels, ainsi que les optimisations de fusion telles que la planification automatique.

La couche matérielle HIVM effectue principalement une abstraction de haut niveau du matériel Ascend, en réalisant successivement la compilation du mappage des cœurs, le mappage de la mémoire sur puce et le mappage de l'unité de traitement.

La première étape consiste à compiler le mappage du noyau.Cela inclut les noyaux Cube et les noyaux Vector. Les représentations de tuiles de haut niveau sont mappées via les noyaux : les noyaux Cube sont associés à d’autres noyaux Cube et les noyaux Vector à d’autres noyaux Vector. La communication, la synchronisation et la gestion de la mémoire de l’espace de travail intermédiaire sont entièrement automatisées par le compilateur, qui effectue également des optimisations de haut niveau pour le parallélisme pipeliné CV.

L'étape suivante est le mappage de la mémoire sur puce. Les cœurs Cube et Vector possèdent chacun leur propre mémoire embarquée. Pour qu'un tenseur fonctionne sur le matériel Ascend, les données logiques du tenseur doivent être déduites dans la mémoire physique correspondante, ainsi que la dérivation du format de matrice fractale spécifique au Cube. L'allocation de mémoire embarquée est entièrement gérée automatiquement par le compilateur.

Le niveau le plus bas est le mappage des unités de traitement.La synchronisation du pipeline garantit la synchronisation et l'efficacité du parallélisme. La vectorisation et la quantification des tenseurs permettent aux instructions matérielles hautes performances de finaliser l'optimisation de la compilation.

La partie droite du diagramme illustre les principales évolutions de l'architecture Ascend 950 par rapport aux modèles A2/A3 de l'année dernière. La couche supérieure HFusion, indépendante du matériel, demeure globalement stable. L'extension majeure concerne la couche HIVM, dépendante du matériel, qui passe du SIMD basé sur la mémoire au SIMD basé sur les registres et au SIMT. Auparavant, la compilation par fusion CV impliquait une interaction entre les cœurs Cube et les cœurs Vector via la mémoire globale ; l'Ascend 950 met en œuvre une approche plus intégrée pour une interaction des données plus efficace.

Globalement, la nouvelle architecture conserve une structure stable à deux couches, mais grâce aux changements d'architecture, la compilation des tuiles a subi une mise à niveau significative.

Examinons d'abord l'évolution de l'architecture SIMD à registres vectoriels. En SIMD basé sur la mémoire, les calculs vectoriels sont effectués sur la puce à l'aide d'un tampon unifié, et les instructions sont exécutées directement depuis la mémoire interne. Sur l'Ascend 950, les calculs sont effectués à l'aide de registres haute vitesse, ce qui ajoute une couche de registres supplémentaire. Pour l'optimisation du backend, notamment pour les calculs vectoriels, le processus de compilation évolue comme suit :

Premièrement, la vectorisation pour les systèmes à base de registres. Les opérations sur les tenseurs sont mappées sur des instructions de registre de largeur fixe via un partitionnement vectorisé. Les données sont chargées depuis la mémoire embarquée vers les registres, les calculs sont effectués à partir de ces registres, puis les données sont à nouveau stockées dans la mémoire embarquée, formant ainsi une boucle de chargement-calcul-stockage.

Deuxièmement, fusion de la granularité des registres. Les opérations de chargement et de déchargement répétées depuis la mémoire embarquée sont inefficaces. La fusion profonde basée sur les registres permet de stocker les données dans ces registres. La fusion de plusieurs opérations Triton en une seule boucle réduit l'espace de stockage nécessaire au chargement. Cela implique l'utilisation de différents algorithmes de recherche et modèles de coûts pour développer de meilleures stratégies de fusion.

Troisièmement, l'abstraction du dialecte AVE.  L'extension Vector d'Ascend, conviviale, masque les différences de masques matériels sous-jacentes, permettant ainsi l'analyse et la dérivation des masques. Le chargement vectoriel matériel offre de nombreuses fonctionnalités d'optimisation au sein du chemin d'exécution. L'activation des opérations au sein du chemin d'exécution permet de combiner plusieurs instructions en une seule. Ces optimisations sont spécifiquement conçues pour le dialecte SIMD Regbase.

Le matériel Ascend 950 prend désormais en charge les unités SIMT, comme illustré en vert ci-dessous. Le cœur Vector peut ainsi fonctionner selon trois modes de calcul : SIMD pur, SIMT pur et un mode hybride combinant SIMD et SIMD. SIMT accélère les accès mémoire discrets, tandis que le calcul dense exploite SIMD pour une optimisation vectorielle plus poussée.

Avant de procéder à la compilation vectorielle SIMD ou SIMT, le processus de compilation effectue une analyse de fusion et une optimisation. Il identifie les composants adaptés à SIMT, les extrait pour des compilations SIMT et SIMD distinctes, puis les combine pour le calcul vectoriel global. Au cours de ce processus, SIMT et SIMD sont modélisés et analysés.

La mise à niveau et la modification des noyaux de vision par ordinateur sont étroitement liées aux opérateurs de type attention. Sur les processeurs A2/A3, le Cube et le Vector ne disposent pas de connectivité interne et ne peuvent interagir que via la mémoire globale externe. En revanche, sur l'Ascend 950, comme illustré en rouge sur le schéma, le résultat du Cube réside dans le L0C et peut être directement copié dans la mémoire interne du Vector. Réciproquement, le résultat du calcul du Vector peut également être directement copié dans la mémoire interne du Cube pour un calcul ultérieur. En résumé, un couplage étroit et un échange de données rapide en vision par ordinateur optimisent considérablement les performances de l'attention.

L'une des technologies clés pour optimiser les opérateurs de vision par ordinateur est le parallélisme du pipeline de vision par ordinateur. Le pipeline de vision par ordinateur d'Ascend Automation permet de mieux masquer la latence et d'exploiter pleinement la puissance de calcul des cœurs Cube et Vector.

Une autre optimisation clé est le sous-titrage automatique.Les cœurs Cube et Vector d'Ascend sont configurés selon un ratio de 1:2, deux cœurs Vector correspondant à un cœur Cube. Le compilateur effectue les opérations Vector par blocs de 1:2, permettant à chaque cœur Vector de calculer simultanément la moitié des données et d'accélérer ainsi le calcul.

La dernière version du travail de généralisation des fonctionnalités du compilateur

Cet article présentera le travail de généralisation des fonctions importantes dans la nouvelle version publiée d'AscendNPU IR.

Premièrement : interaction des données CV. L'interaction des données entre les cœurs, nécessaire aux opérations Cube et Vector, est automatiquement gérée par le compilateur. Les calculs Cube et Vector de l'opérateur Triton peuvent être répartis sur différentes branches du flux de contrôle, ce qui complexifie la compilation et l'analyse. L'insertion d'instructions par simple correspondance de motifs n'est pas envisageable. Dans l'exemple ci-dessus, les deux branches effectuent respectivement les calculs Cube et Vector. L'interaction des données CV nécessite une dérivation complexe du flux de contrôle pour garantir la complétude fonctionnelle.

La nouvelle version améliore l'analyse des passes clés dans InsertCVLoadStore, complétant l'interaction des données CV par une dérivation globale à travers des flux de contrôle complexes. Tout d'abord, des points d'ancrage déterministes sont insérés : les opérations de multiplication matricielle entrent au niveau L1 et sortent au niveau L0C, tandis que les opérations vectorielles entrent et sortent toutes deux au niveau UB. Ensuite, à partir de ces points d'ancrage initiaux, des conversions de type sont insérées pour effectuer une dérivation ascendante et descendante ainsi qu'une propagation à travers les flux de contrôle. Après propagation, le niveau mémoire de tous les tenseurs d'opérandes est clairement défini.

Comme illustré dans l'exemple ci-dessus, un tenseur ne contient pas initialement de sémantique de hiérarchie mémoire. Après l'insertion d'une opération de conversion de type, il est confiné à la mémoire UB du cœur Vector ; en dessous se trouve la mémoire L1 du cœur Cube. Un conflit survient lors du processus de dérivation : la hiérarchie mémoire est incohérente, ce qui nécessite l'insertion d'une instruction Load-Store ou de copie pour établir la connectivité des données. Les méthodes de gestion diffèrent légèrement selon les générations. Les processeurs Cube et Vector A2/A3 nécessitent l'insertion d'une instruction Global Memory Load-Store pour établir la connectivité des données, tandis que l'Ascend 950 établit cette connectivité par copie de données sur puce.

Deuxièmement : MultiBuffer améliore le flux de contrôle complexe. MultiBuffer est une condition essentielle au pipeline et au parallélisme. Comment transformer un tenseur en MultiBuffer ?

La solution précédente ne prenait en charge que les boucles « for ». Lors de l'itération, MultiBuffer réutilisait la variable d'itération de la boucle « for », faisant pivoter l'emplacement du tampon en fonction de cette variable. Cette solution était limitée par son incapacité à gérer d'autres boucles « while » et les flux de contrôle imbriqués. C'est pourquoi un compteur de tampon indépendant a été introduit. Ce compteur suit le flux de contrôle complexe à travers les boucles de branchement, assurant ainsi un suivi précis des changements d'emplacement.

De plus, l'opération d'allocation doit être visible dans la couche de boucle commune de Cube et Vector, afin que Cube et Vector puissent effectuer simultanément des opérations d'allocation de mémoire, telles que l'allocation de 2 ou 3 Buffers, pour assurer la cohérence de la transformation MultiBuffer.

Troisièmement : AutoBlockify, une transformation multicœur étroitement liée à l’optimisation de l’opérateur Ascend.Lors de la programmation d'opérateurs CUDA ou Triton, le code est généralement divisé en de nombreux blocs logiques, exécutés ensuite par l'ordonnancement matériel. Ascend s'appuie fortement sur un ordonnancement répétitif de type round-robin. Il fusionne différents cœurs logiques dans une seule boucle `for` et laisse l'ordonnancement logiciel optimiser les performances. De plus, des techniques d'optimisation avancées telles que MultiBuffer et le pipeline peuvent être appliquées au sein de la boucle `for` pour améliorer encore les performances initiales.

Quatrièmement : Le pipeline CV.Par exemple, l'opérateur FlashAttention comporte quatre tâches réparties sur deux cœurs, Cube et Vector, exécutées séparément. Ces tâches s'exécutent séquentiellement, et les calculs effectués dans Cube et Vector doivent s'attendre mutuellement. Lors de la compilation, l'utilisation de MultiBuffer permet de garantir que Cube et Vector soient exécutés le plus tôt possible, ce qui permet d'obtenir une accélération parallèle par pipeline.

Tout d'abord, identifiez les points de frontière C/V, puis divisez précisément le segment de code en portée cube et portée vectorielle. Ensuite, appliquez MultiBuffer pour implémenter des transformations parallèles pipelinées selon l'algorithme d'ordonnancement, incluant différents modes stratégiques : déroulement, décalage et dynamique. En fonction de la charge de calcul, le mode parallèle pipeliné optimal peut être sélectionné.

Cinquième : CV fractionné 1:2.Les deux noyaux vectoriels et le noyau cubique collaborent pour traiter les données. Les données tensorielles originales doivent être divisées par deux aussi précisément que possible afin d'optimiser le calcul vectoriel. Idéalement, des axes parallèles sont identifiés dans le volume pour effectuer une division globale. Cependant, les axes candidats peuvent subir des opérations de calcul telles que la transposition, la diffusion et la réduction, ce qui accroît la complexité de la division 1:2 du volume vectoriel.

Commencez par effectuer une analyse dimensionnelle globale afin d'identifier l'axe de division le plus approprié. Une fois cet axe déterminé, insérez un marqueur de début de division à la fin de l'opération de stockage, puis remontez la chaîne de division jusqu'au nœud racine jusqu'à obtenir une division parfaite. En cas de scénario complexe empêchant toute division, revenez à l'état initial (division 1:1).

De plus, dans certains scénarios complexes, la simple division de l'axe parallèle s'avère insuffisante pour optimiser les performances. Par exemple, l'algorithme d'attention FB8 à faible précision subit une charge vectorielle élevée et nécessite une division et une réduction de l'axe de réduction. Les deux noyaux vectoriels ne pouvant échanger directement de données, les résultats de réduction intermédiaires doivent transiter par la mémoire globale pour une réduction secondaire, achevant ainsi la réduction et la division globales. Par ailleurs, les scénarios tels que les formes dynamiques requièrent également des méthodes de division CV 1:2 améliorées.

Triton-Ascend et AscendNPU IR sont des logiciels libres développés en collaboration.

AscendNPU IR est désormais disponible en open source sur la communauté Ascend. Scannez le QR code sur l'image pour suivre le projet. Les réunions bimensuelles du groupe d'intérêt spécialisé (SIG) de la communauté AscendNPU IR sont publiées dans le calendrier des activités. Les développeurs peuvent ainsi consulter à l'avance les sujets de discussion hebdomadaires et les comptes rendus des réunions.

La communauté Ascend a lancé des projets de stage open source et des tâches communautaires, offrant ainsi aux développeurs la possibilité de contribuer au développement de l'écosystème. Vous pouvez consulter l'état d'avancement des tâches ici et vous inscrire. Ces tâches communautaires sont de difficulté variable ; choisissez et participez en fonction de vos compétences.

Des stages et des tâches communautaires open source sont disponibles via des liens rapides sur les pages d'accueil des dépôts AscendNPU IR et Triton-Ascend, où vous pouvez également vous inscrire. Chaque personne ne peut s'inscrire qu'à une seule tâche à la fois. Les tâches communautaires seront régulièrement mises à jour et le processus de développement restera interactif et ouvert. Chaque tâche offre une rémunération attractive ; chacun est libre de choisir.

Pour les participants ne disposant pas d'environnement de développement Ascend, la communauté met à leur disposition une plateforme de calcul gratuite dédiée à Ascend : HiDevLab (https://hidevlab.huawei.com/home). Les développeurs peuvent s'inscrire et solliciter de la puissance de calcul. Une fois leur demande acceptée, ils se verront attribuer par défaut 100 heures de temps de calcul gratuit. Les développeurs ayant accepté des tâches communautaires peuvent également solliciter de la puissance de calcul pour mener à bien le développement et la vérification de ces tâches.

Merci de votre attention !