10 points par GN⁺ 2025-07-27 | 1 commentaires | Partager sur WhatsApp
  • Un projet de démonstration a été dévoilé montrant qu’une base de code unique écrite en Rust fonctionne sur toutes les principales plateformes GPU et CPU, notamment CUDA, Vulkan (SPIR-V), Metal, DirectX 12, WebGPU et CPU
  • La programmation GPU classique est complexe et entraîne des duplications, car elle nécessite l’usage de langages distincts comme GLSL, HLSL, etc. ; cette démonstration prend au contraire en charge toutes les cibles GPU uniquement avec du code Rust pur
  • Elle combine des implémentations majeures comme Rust GPU (SPIR-V), Rust CUDA (NVVM IR) et Naga (couche de traduction entre langages GPU) ; le même algorithme de tri bitonique fonctionne ainsi sur le CPU et sur tous les GPU, tandis que les fonctionnalités du langage Rust comme no_std, la compilation conditionnelle, les newtypes, les enums et les traits s’appliquent telles quelles au code GPU
  • Même s’il reste des points à améliorer, comme l’intégration officielle dans rustc, le débogage et la cohérence des API, il s’agit d’une tentative qui pourrait marquer un tournant pour le calcul cross-platform sur GPU

Concrétisation du calcul GPU cross-platform basé sur Rust

Présentation du projet et intérêt

  • Une seule base de code Rust permet d’exécuter le même code de kernel sur CUDA (NVIDIA), Vulkan (SPIR-V), Metal (Apple), DirectX 12 (Windows), WebGPU (navigateur) et CPU
  • Sans utiliser de langage dédié aux shaders ou aux kernels (GLSL, HLSL, etc.), il devient possible d’effectuer les mêmes opérations sur GPU et CPU uniquement avec du code Rust pur
  • Cela réduit fortement la duplication de code et la complexité entre GPU et CPU, tout en apportant à la programmation GPU les avantages de l’écosystème Rust et du langage lui-même (sûreté des types, tests, documentation, gestion de build, etc.)

Contexte

  • La programmation GPU traditionnelle impose l’usage de langages de shader spécialisés selon chaque plateforme (par ex. GLSL, HLSL, MSL, WGSL, etc.)
  • Cela sépare le code CPU et GPU, entraîne de la duplication logique et accroît la complexité du développement
  • Pour y répondre, la communauté Rust poursuit une approche consistant à compiler du Rust standard vers des cibles GPU
    • Rust GPU : compile du code Rust en SPIR-V et l’exécute sur Vulkan et les GPU compatibles SPIR-V
    • Rust CUDA : compile du code Rust vers l’IR CUDA de NVIDIA (NVVM IR, PTX) pour l’exécuter sur CUDA
    • Naga : couche intermédiaire permettant les conversions entre divers langages GPU (WGSL, SPIR-V, GLSL, MSL, HLSL), principalement utilisée dans le projet wgpu. Elle assure la portabilité des backends
  • Chaque projet ayant démarré de manière indépendante avec des API et des structures différentes, c’est grâce à une collaboration récente que l’exécution GPU d’une base de code commune a pu être réalisée

Mode d’implémentation

  • Dans la démonstration, l’algorithme de tri bitonique est implémenté sous la forme d’un unique code Rust, exécuté à l’identique sur tous les GPU et sur le CPU
  • Terminologie des principaux composants
    • Host : code Rust exécuté sur le CPU (démarrage du travail)
    • Device : GPU/CPU sur lequel le kernel s’exécute réellement
    • Driver API : API bas niveau de communication avec le périphérique, comme CUDA, Vulkan, Metal, DX12, etc.
    • Backend : abstraction en Rust au-dessus des Driver API (cust, ash, wgpu, etc.)
  • Les feature flags de Rust et les combinaisons de cibles permettent de choisir le backend et de contrôler la build
    • Exemple : des options de build indépendantes sont fournies pour chaque backend, comme wgpu, ash ou cuda
    • Il est possible de construire plusieurs backends dans un même binaire et de les sélectionner dynamiquement à l’exécution

Flux de compilation des kernels

  • Selon la cible et les features, des binaires d’exécution GPU (SPIR-V, PTX, etc.) sont générés à la compilation puis embarqués dans les fichiers objet
  • À l’exécution, les kernels embarqués sont chargés puis convertis, notamment via Naga, vers le format requis par chaque plateforme avant d’être lancés
  • Exemples :
    • macOS : conversion de SPIR-V vers MSL → exécution sur Metal
    • Windows : conversion de SPIR-V vers HLSL → exécution sur DX12
    • Linux/Android : SPIR-V → exécution sur Vulkan
    • CUDA : compilation de PTX en SASS, envoi au GPU puis exécution

Techniques de codage GPU adaptées à Rust

  • Prise en charge de no_std

    • Les GPU ne disposant pas de support OS, l’usage de no_std en Rust est indispensable
    • Comme l’écosystème Rust de base vise déjà nativement des environnements sans OS, comme l’embarqué, le firmware ou le kernel, les GPU peuvent eux aussi être pris en charge sans « mode spécial » distinct, selon l’approche standard de Rust
  • Compilation conditionnelle

    • La combinaison des attributs cfg et des feature flags permet d’écrire de façon claire et concise le code différenciant les plateformes ainsi que les chemins GPU/CPU
    • L’IDE et le compilateur comprennent l’ensemble des chemins de code, ce qui améliore la fiabilité et la productivité
  • Utilisation des newtypes

    • Les erreurs possibles sur GPU liées aux index implicites ou au mapping de structures peuvent être évitées au niveau du type grâce aux newtypes
    • L’attribut #[repr(transparent)] permet cette abstraction de type sans surcoût réel
  • Enum et représentations sûres

    • Au lieu d’utiliser des magic numbers, Rust permet d’employer des enums ; avec #[repr(u32)], on garantit un mapping correct vers les entiers natifs
    • Le pattern matching et l’exhaustivité du traitement des cas permettent d’écrire un code plus sûr
    • Il faut toutefois être prudent lors des échanges de valeurs d’enum via des buffers partagés entre CPU et GPU, car toutes les valeurs doivent correspondre à des enums valides
  • Algorithmes génériques basés sur les traits

    • Les traits permettent d’abstraire les opérations GPU communes — comparaison, conversion, calcul, etc. — pour divers types de valeurs
    • En définissant clairement les trait bounds dans les algorithmes génériques, on concilie sûreté des types et performance
  • #[inline] et optimisation des performances

    • L’usage de #[inline] aide à faire disparaître les couches d’abstraction dans le résultat compilé
    • La conception vise à éviter tout coût lié à l’abstraction, ce qui est important compte tenu des contraintes propres aux GPU (performance, pile limitée, etc.)
  • Composition de structs et regroupement sémantique

    • Des paramètres GPU complexes sont regroupés dans des struct par unité de sens, afin d’assurer la sûreté des types et d’éviter l’explosion du nombre de paramètres
    • Le pattern du smart constructor permet d’écarter les états invalides dès la compilation
  • Contrôle de la disposition mémoire et #[repr(C)]

    • Pour assurer la compatibilité des données avec le GPU, la disposition des struct est explicitement définie avec #[repr(C)]
    • Le besoin d’un support futur du langage pour automatiser, par exemple, le padding selon les GPU est également mentionné
  • Pattern matching

    • Comme extension du concept de switch/case, il permet de traiter clairement tous les branchements et états dans le code GPU
    • Le compilateur peut alors vérifier les chemins de code et optimiser les performances
  • Fonctions génériques

    • Elles permettent d’implémenter une même logique pour plusieurs types, indépendamment du type de donnée
    • Les trait bounds, la monomorphisation, etc. améliorent la maintenabilité et la facilité de test
  • Macros derive

    • Elles automatisent l’implémentation de traits adaptés au GPU comme Copy, Clone, Debug, PartialEq, Pod, etc.
    • Cela renforce la sûreté et réduit le boilerplate
  • Système de modules et gestion des workspaces

    • Le système de packages/modules de Rust permet de structurer séparément le code hôte, les kernels, les types et les sources propres à chaque backend
    • Les Cargo workspaces et les workspace dependencies assurent la cohérence des dépendances entre crates et facilitent la maintenance
  • Formatage, linting et documentation

    • Le code GPU peut être géré exactement comme le reste du code Rust avec les outils standard tels que rustfmt et clippy, ce qui permet de conserver une qualité homogène
    • Les doc comments et cargo doc permettent aussi de documenter le code GPU
  • Scripts de build

    • Via build.rs de Cargo, il est possible d’automatiser la build des kernels selon les feature flags et leur embarquement dans le binaire
  • Tests unitaires et productivité de développement

    • Le code de kernel GPU peut également être testé sur CPU, ce qui facilite le développement et la détection de bugs
    • Il est possible d’utiliser des outils traditionnels comme le debug par println, gdb ou lldb
    • Les kernels Vulkan peuvent aussi être testés en CI via des pilotes logiciels comme SwiftShader ou lavapipe
    • Les tests, la mesure de couverture de code et les property-based tests s’intègrent facilement avec des outils tiers

Expérience développeur encore incomplète et défis restants

  • Les backends GPU n’étant pas intégrés officiellement au compilateur Rust, il faut utiliser des backends de génération de code séparés (spirv, nvvm) et figer une version nightly
  • La cible CUDA dépend de LLVM 7.1 de NVIDIA, ce qui nécessite une build distincte sur les distributions Linux récentes
  • L’expérience de build et de débogage des kernels reste limitée, avec des problèmes de traçage des erreurs et d’informations de debug insuffisantes
  • Les API de Rust GPU et Rust CUDA ainsi que les noms de bibliothèques standard diffèrent, ce qui peut prêter à confusion
  • À long terme, il sera nécessaire de renforcer la cohérence et l’intégration orientées GPU dans l’ensemble du langage Rust et de son écosystème

Participation de la communauté et avenir

  • L’exécution d’un même code Rust sur toutes les grandes plateformes GPU est désormais devenue réalité
  • Les prochaines étapes concernent l’amélioration de la build et du débogage, l’élargissement de la prise en charge du langage Rust et des API, ainsi que le tuning des performances
  • Les développeurs souhaitant participer ou contribuer peuvent se référer aux dépôts GitHub de rust-gpu et rust-cuda

1 commentaires

 
GN⁺ 2025-07-27
Commentaires sur Hacker News
  • Le simple fait que cette technique soit possible est vraiment impressionnant
    Mais mon cas d’usage consiste à exécuter sur du matériel client arbitraire, donc j’ai tendance à ne pas faire confiance à toutes les couches d’abstraction construites au-dessus des API GPU
    Le but est d’exploiter au maximum les détails bas niveau du GPU, et les approches qui considèrent ces détails comme une nuisance finissent par entraîner des bugs et des pertes de performances
    Parce que chaque cible est significativement différente
    Pour surmonter cela, je pense qu’un système similaire devrait être fourni directement par les constructeurs
    Mais comme les positions entre fournisseurs ne font toujours pas consensus, les différences entre plateformes semblent rester importantes
    Il y a bien des exceptions comme Angle dans certains cas, mais même là, la stabilité n’est obtenue qu’au prix de limitations fonctionnelles, avec au final une perte de performances
    Cela dit, le fait que des approches comme la compilation conditionnelle soient possibles aide clairement

    • Rust étant un langage système, on peut avoir autant de contrôle qu’on le souhaite
      Nous prévoyons de refléter les détails et les API des GPU dans le langage et dans les bibliothèques core/std, et d’exposer les fonctionnalités du GPU et des pilotes via le système cfg()
      (auteur)

    • Je pense exactement pareil
      Je suis toujours prudent à l’idée de construire quelque chose de commercial sur des couches d’abstraction, des adaptateurs ou des couches de traduction dont on ne sait pas si elles auront assez de support à l’avenir
      On est presque en 2025, et j’ai toujours l’impression qu’on a désespérément besoin d’un standard ouvert pris en charge par tous les fournisseurs et donnant accès à toutes les capacités du matériel GPU moderne
      Vu la situation actuelle, le fait que Nvidia, qui a construit la barrière logicielle à l’entrée la plus puissante, siège à la tête de Khronos me paraît assez révélateur

    • Comme vous semblez très intéressé par les performances, j’aimerais poser la question par curiosité
      De mon point de vue, l’état actuel du monde GPU ressemble beaucoup à l’ancien monde CPU
      Pour les CPU, on avait une architecture de compilateur en trois niveaux, avec optimisation dans une couche intermédiaire puis génération de code adapté à chaque matériel dans la couche finale
      L’avantage de cette structure, c’est que la couche d’abstraction dure longtemps, et que le compilateur devient plus intelligent avec le temps
      Je me demande si une structure comparable est possible côté GPU
      Ou bien si la diversité des GPU est trop grande pour que ce soit possible ou économiquement viable, ou encore si c’est évidemment la direction à prendre mais qu’on n’y est pas encore techniquement

    • C’est tout à fait juste
      Je ne vois pas très bien en quoi faire tourner Rust sur des GPU Nvidia serait vraiment préférable au code CUDA existant
      J’entends l’intérêt d’ajouter de l’abstraction, mais au final cela donne une impression de « faire un peu tout et n’importe quoi »

    • En réalité, tout est abstraction
      CUDA aussi n’est au fond qu’une abstraction qui unifie conceptuellement des matériels pourtant complètement différents

  • Je développe des applications audio natives, et ici chaque cycle de calcul compte
    Et j’ai besoin d’une API de calcul complète, pas seulement de shaders graphiques
    Je me demande si le pipeline "Rust -> WebGPU -> SPIR-V -> MSL -> Metal" est solide du point de vue des performances
    Il y a tellement d’étapes de transformation que cela paraît fragile et imprévisible
    C’est pareil pour "... -> Vulkan -> MoltenVk -> ..."
    À l’inverse, "Julia -> Metal" saute MSL et peut exploiter directement des optimisations spécifiques à Apple Silicon, comme la mémoire unifiée
    L’innovation ici, ce n’est pas le langage de shader, mais le fait d’utiliser un langage de programmation complet comme Rust
    Rust prend en charge de nombreuses fonctionnalités comme les newtypes, les traits, les macros, etc.

    • Avec rust-gpu, il n’est pas obligatoire de passer par la couche WebGPU
      rust-gpu est un backend de génération de code du compilateur
      L’architecture permet de compiler directement le MIR de Rust en SPIRV

    • « Le pipeline "Rust -> WebGPU -> SPIR-V -> MSL -> Metal" est-il solide en matière de performances ? »
      Fondamentalement, c’est une idée proche de la manière dont Apple utilise les optimisations Clang pour le GPU
      SPIR-V est une représentation intermédiaire, comme l’IR utilisé dans LLVM, donc elle peut être optimisée pour chaque système
      En théorie, on peut cibler avec une seule base de code tous les GPU de rastérisation pris en charge
      La pile Julia -> Metal est en revanche moins portable
      Pour un développeur limité à une seule plateforme, comme pour un plugin audio, ce n’est pas très important, mais pour des sociétés multiplateformes comme u-he ou Spectrasonics, un pipeline plus complexe basé sur SPIR-V peut être plus attractif

    • Pour le calcul numérique et les optimisations qui suivent, Julia est bien plus adapté que Rust, qui est un langage système
      Quand on regarde la matrice de compatibilité de Rust-CUDA, on voit qu’il y a très peu de demande pour Rust dans la programmation CUDA
      La plupart des fonctionnalités de CUDA que les gens apprécient sont absentes, et s’il y avait une vraie demande, on verrait davantage de progrès
      Les programmeurs CUDA ne semblent pas très enclins à utiliser Rust
      https://github.com/Rust-GPU/Rust-CUDA/blob/main/guide/src/features.md

  • Même quand j’ai du code que j’aimerais faire tourner sur GPU, toute la programmation GPU est tellement pénible que je finis par renoncer
    Le vrai usage de rust-gpu est peut-être de transformer des développeurs CPU en développeurs GPU, même au prix d’un certain sacrifice sur les performances
    Si on est déjà à l’aise avec le GPU et qu’on maîtrise cuda/vulkan/metal/dx, on ne verra probablement pas un grand intérêt dans ce type d’outil

  • Je suis un simple développeur web, donc c’est peut-être une question idiote, mais je n’ai jamais fait de programmation GPU
    Je me demande si WebGPU, en tant qu’API unique compatible avec tous les backends GPU, ne résout pas déjà tous ces problèmes
    WebGPU semble aussi être l’un des backends pris en charge, donc si c’est le cas, cela ne revient-il pas à empiler une abstraction sur une abstraction existante pour finir par appeler un backend GPU natif ?

    • Non
      WebGPU est une API qui permet au CPU de contrôler le GPU afin d’exécuter des shaders et d’autres tâches graphiques, comme D3D, Vulkan ou SDL GPU
      Rust-GPU est un langage permettant d’écrire le code shader réellement exécuté sur le GPU, à la manière de HLSL, GLSL ou WGSL

    • À l’époque où Microsoft avait de l’influence, il y avait DirectX
      Mais aujourd’hui, je ne sais pas trop dans quelle mesure les fabricants de GPU implémentent chacun des API dédiées à leurs propres technologies
      Il y a toutes sortes de fonctionnalités particulières comme DLSS, MFG, RTX, etc.
      Si j’étais le méchant d’un dessin animé, je pourrais volontairement rendre les API existantes lentes et ne fournir rapidement que de nouvelles API propriétaires plus rapides
      Je précise que je suis moi aussi développeur web, donc je ne maîtrise pas vraiment le sujet, mais au moins les LLM vont apprendre ce genre de choses

    • Je vois plutôt WebGPU comme une API du plus petit dénominateur commun
      Par exemple, l’éditeur Zed cible directement Metal sur Mac
      Et puis chacun a sa propre idée de ce que signifie « commun »
      Comme avec OpenGL contre Vulkan, les acteurs les plus puissants cherchent à faire de leur propre écosystème — CUDA, Metal, DirectX, etc. — le standard du marché

    • Si c’était vraiment aussi simple, CUDA ne serait pas aujourd’hui le fossé défensif aussi puissant de Nvidia

    • Ce projet repose en grande partie sur les efforts autour de l’implémentation WebGPU wgpu-rs
      WebGPU n’est pas optimal pour les applications natives
      Il a été conçu à partir d’anciennes versions de Vulkan, surtout pré-RTX, et depuis les API véritablement natives ont beaucoup évolué

  • C’est encore assez brut, mais j’ai vraiment du mal à croire que ce soit possible
    Si ce genre de progrès continue, j’ai l’impression qu’il y a un vrai potentiel pour briser la forte dépendance aux fournisseurs dans le logiciel GPU et permettre une concurrence réelle entre fabricants de matériel
    J’imagine qu’un jour on pourrait écrire des modèles de machine learning en Rust et les exécuter à la fois sur Nvidia et AMD
    Bien sûr, pour obtenir les meilleures performances, il faudra du code spécialisé pour chaque fournisseur, mais ça relève de l’optimisation
    Malgré tout, on pourrait avoir des kernels portables fonctionnant en multiplateforme

    • Il existe un framework de machine learning en Rust appelé https://burn.dev, avec plusieurs backends comme CUDA et ROCm
      Cela vaut le coup d’y jeter un œil

    • Un avenir où l’on écrit des modèles de machine learning en Rust pour les exécuter à la fois sur Nvidia et AMD me paraît difficile à imaginer dans les dix prochaines années
      En pratique, tout l’écosystème, avec jax et torch notamment, repose sur Python
      Faire basculer tous les développeurs du secteur vers des outils Rust semble presque inimaginable

  • Si on compte les couches d’abstraction

    1. code Rust spécialisé domaine
    2. abstraction de backend au-dessus des crates cust, ash, wgpu
    3. abstraction de plateforme, pilote et API dans wgpu et autres
    4. abstraction de pilote et de plateforme dans Vulkan, OpenGL, DX12, Metal
    5. abstraction du matériel spécifique au fournisseur dans le pilote (et il y a probablement encore d’autres couches là-dedans)
    6. matériel
      Cela fait au moins six niveaux de complexité cachée
      Je me demande s’il est réellement possible de traverser toutes ces couches tout en conservant, en performances, les spécificités de chaque plateforme
    • Ce que fait rust-gpu, au fond, c’est compiler en SPIRV, c’est-à-dire l’IR de Vulkan
      Donc les couches 2 et 3 peuvent être ignorées ou placées en parallèle
      On peut aussi réutiliser tels quels pour le développement de shaders GPU les outils de l’écosystème Rust, comme cargo, cargo test, cargo clippy et rust-analyzer
      En fait, je pense que la difficulté de la programmation GPU ne vient pas du fait que les architectures GPU seraient trop extraterrestres, mais du fait que l’écosystème entier est verrouillé par les fournisseurs et freiné par de vieux outils

    • La démo ressemble clairement à une machine de Rube Goldberg assez complexe, mais c’est parce que c’est la première fois que ce genre de chose devient possible
      Avec le temps, cela deviendra plus naturel et plus intégré
      Et un autre avantage de l’écosystème Rust, c’est qu’on peut travailler à un niveau d’abstraction aussi élevé ou aussi concret qu’on le souhaite
      Par exemple, on peut utiliser des fonctionnalités spécifiques à une plateforme via std::arch, ou même écrire de l’assembleur
      On peut aussi remplacer l’allocateur ou le gestionnaire de panique, et quand la fonctionnalité externally implemented items arrivera, cela offrira encore plus de flexibilité pour manier les couches d’abstraction comme on le souhaite

    • Bonne remarque
      Mais les couches 4 à 6 existent toujours aussi bien pour les shaders que pour le code CUDA
      Quant aux couches 1 et 3, elles sont en réalité simplement remplacées par d’autres couches, surtout en multiplateforme
      Même si ce projet Rust ajoute une couche d’abstraction, cela n’en fait qu’une de plus
      Et en tant que personne qui travaille concrètement sur les couches 4 à 6, je peux confirmer qu’il y a énormément de complexité cachée à l’intérieur
      Honnêtement, il y a même encore plus de couches :P

    • En pratique, la plupart des utilisateurs ne manipuleront au plus que les couches (3) ou (4)
      Cela n’ajoute donc pas réellement une énorme étape supplémentaire
      D’ailleurs, il y a aussi d’autres couches d’abstraction au-dessus du niveau 6
      Le firmware et la microarchitecture implémentent l’ensemble d’instructions tel que nous nous le représentons

    • Je ne pense pas que ce soit très différent du fait d’avoir des compilateurs et des runtimes distincts selon les architectures CPU
      Il y a aussi des conventions d’appel différentes, des questions d’endianness, etc., et au niveau matériel on trouve également firmware et microcode

  • Le fait que des crates existantes en no_std + no alloc puissent tourner sur GPU presque sans modification est vraiment impressionnant
    J’ai l’impression que cela ouvre énormément d’idées d’applications

    • Si le code suppose une exécution sur CPU, son comportement en matière de performances sera assez différent
  • C’est vraiment remarquable
    Il existe déjà énormément de projets GPU autour de Rust
    Celui-ci semble plus proche d’une abstraction de bas niveau que burn, et burn est lui-même plus bas niveau que candle
    Ce qui manque maintenant, ce serait peut-être d’ajouter des backends comme naga aux projets situés au-dessus
    On a l’impression que tout le monde construit quelque chose sur des bases différentes, mais cela vient peut-être du fait que le travail sur naga est relativement récent
    Je voudrais aussi ajouter que burn se concentre sur le support des plateformes
    Mais en regardant, le seul backend qui utilise naga semble être wgpu
    Donc au final, est-ce qu’utiliser simplement wgpu ne suffit pas ?
    En résumé, c’est soit wgpu/ash (vulkan, metal), soit cuda
    Petit ajout : un autre crate proche de cet effort
    https://github.com/tracel-ai/cubecl
    [0]: https://github.com/tracel-ai/burn
    [1]: https://github.com/huggingface/candle/

  • Je me demande si c’est vraiment du « Rust » qui s’exécute sur le GPU
    En survolant un peu le code, on dirait plutôt une structure qui repose au final sur un langage de shader, avec au-dessus une syntaxe Rust remplie de macros de programmation
    La programmation GPU est tellement différente qu’elle demande, selon moi, une attention particulière
    Ajouter cette abstraction peut rendre certaines optimisations impossibles

    • En pratique, il s’agit bien de code Rust fonctionnel compilé en bytecode spirv
  • Ce projet me réjouit vraiment
    J’ai l’impression qu’il aide énormément les développeurs qui ne veulent pas être pris dans les guerres de plateformes
    Des exemples comme https://github.com/cogentcore/webgpu sont aussi intéressants
    J’utilise golang et je veux simplement pouvoir tirer parti du GPU sur toutes les plateformes, donc ce genre de travail permet d’utiliser le GPU partout
    Merci vraiment à Rust