Le paysage des DSL pour noyaux GPU

Depuis l'apparition de Triton, une vague de langages dédiés (DSL) Pythoniques a émergé pour écrire des noyaux GPU sans recourir directement à CUDA ou aux templates C++ lourds. Parmi eux figurent CuTe-DSL, cuTile, Pallas, Gluon, Warp et le récent TileLang utilisé dans DeepGEMM de DeepSeek. Tous visent à abaisser un programme orienté tuiles vers du PTX ou du LLVM-IR, mais la manière d'intégrer le DSL dans Python diffère fondamentalement.

Deux familles : parsing et tracing

La plupart des DSL, à commencer par Triton et CuTe-DSL, analysent l'arbre syntaxique abstrait (AST) du code Python source. À l'inverse, Pallas exécute la fonction sous des valeurs abstraites et trace les opérations résultantes. PyTorch, avec torch.compile, intercepte le bytecode CPython plutôt que le source, ce qui reste une forme d'analyse, mais sur une grammaire post-désucrée. L'article soutient que l'approche par traçage (tracing) est souvent préférable.

Les limites du modèle CUDA et des templates C++

Un noyau CUDA classique spécifie directement le code d'exécution pour chaque thread. Par exemple, un noyau softmax fusionné nécessite que le type d'élément et la taille de bloc soient connus à la compilation, car la mémoire partagée (__shared__) est dimensionnée statiquement. Cela conduit à une multiplication des instanciations : trois types d'élément et quatre tailles de bloc impliquent déjà douze versions distinctes, que l'appelant doit dispatcher.

CUTLASS pousse cette logique à l'extrême avec le méta-programmation par templates C++. Sa classe principale Gemm compte environ vingt paramètres template, dont plusieurs avec des valeurs par défaut qui recherchent récursivement des configurations. L'exemple canonique pour l'architecture Hopper utilise des CollectiveBuilder imbriqués, chacun générant des dizaines d'instanciations supplémentaires. Le code assemble des types comme Shape<_128,_128,_32> – un type et non une valeur – et le compilateur doit instancier chaque template dépendant pour chaque forme distincte, ce qui alourdit considérablement les temps de compilation.

Des temps de compilation préoccupants

Des mesures rapportées dans l'article illustrent le coût. La compilation d'un seul fichier CuTe de 500 lignes, sans code de benchmark, avec nvcc -std=c++17 -arch=sm_90a, a nécessité environ 20,5 secondes en régime stationnaire. Une compilation complète de CUTLASS ciblant plusieurs architectures multiplie ce temps. Le bug tracker de NVIDIA mentionne 17 minutes et 22 secondes pour deux noyaux i16832gemm_s8 sur Ampere (issue #1042) et environ deux minutes pour un noyau CuTe-DSL de 30 lignes (issue #2677). L'article de blog de NVIDIA présentant le CuTe Python DSL en novembre 2025 soulignait que sa principale contribution était une réduction des temps de compilation « jusqu'à deux ordres de grandeur » par rapport au C++ CUTLASS, avec un « ~100x d'accélération » pour le Blackwell GEMM et « 30-50x » pour l'attention flash.

Pourquoi le traçage pourrait être préférable

L'analyse par traçage, telle que mise en œuvre dans Pallas, exécute le code Python avec des tenseurs symboliques et enregistre le graphe d'opérations. Cela évite la dépendance à la structure lexicale du code source et permet une spécialisation plus souple sans multiplication d'instanciations de templates. L'auteur argue que cette approche réduit la complexité pour l'utilisateur, qui n'a pas à gérer des paramètres template, et facilite l'optimisation dynamique.

Des choix architecturaux aux conséquences concrètes

Le débat ne se limite pas à une préférence esthétique. Il conditionne la facilité d'écriture des noyaux, la vitesse de compilation, et la capacité à générer du code optimisé pour différentes architectures GPU. Alors que Triton domine le paysage des DSL Python, l'article invite à reconsidérer le traçage comme une alternative viable, notamment pour les noyaux où les configurations de tuiles varient fréquemment.