Pourquoi CUDA pour l'IA haute performance
Ce chapitre pose les limites structurelles des frameworks de deep learning et explique où se cachent réellement les gains de latence et de coût lorsqu'on sert un modèle sur GPU.
Table des matières
Pourquoi ce chapitre est le plus utile
Un ingénieur qui a déjà entraîné ou servi un modèle avec PyTorch a l'impression de maîtriser le GPUGPUIAProcesseur graphique parallélisant massivement les calculs matriciels ; indispensable à l'entraînement et à l'inférence des modèles de deep learning.Voir dans le glossaire : il appelle .cuda(), il regarde nvidia-smi grimper à 100 %, et la boucle d'entraînement tourne. Cette impression est trompeuse. Le framework masque une pile entière de décisions — lancement de kernels, disposition mémoire, synchronisation — qui déterminent si le GPU est réellement exploité ou simplement occupé. Ce chapitre pose les bases pour comprendre où se situe la frontière entre « ça marche » et « ça marche au prix du silicium payé ».
Cette formation ne remplace pas un cours d'introduction au deep learningapprentissage profondIASous-ensemble de l'apprentissage automatique fondé sur des réseaux de neurones à nombreuses couches. Chaque couche construit une abstraction plus élevée que la précédente.Voir dans le glossaire. Elle part du principe que vous savez déjà entraîner ou déployer un modèle, et que vous voulez comprendre pourquoi deux implémentations mathématiquement équivalentes peuvent afficher un facteur 5 à 20 d'écart en latence ou en coût d'inférenceinférenceIAUtilisation d'un modèle déjà entraîné sur une donnée nouvelle. Peu coûteuse à l'unité mais répétée à chaque requête, elle constitue le coût récurrent d'exploitation.Voir dans le glossaire. Si vous arrivez d'un poste MLOps, d'une équipe plateforme, ou d'un rôle de recherche appliquée qui commence à buter sur des contraintes de coût GPU, c'est exactement le public visé.
Le mur des abstractions
Les frameworks — PyTorch, TensorFlow, JAX — exposent un graphe d'opérations tensorielles. Chaque opération (matmul, softmax, layernorm) est traduite en un ou plusieurs kernels CUDA exécutés sur le GPU, via des bibliothèques comme cuBLAS, cuDNN, ou des kernels compilés à la volée (Triton, torch.compile). Cette traduction est utile la plupart du temps : elle donne une portabilité et une productivité que personne ne veut abandonner pour écrire du CUDA à la main sur chaque couche d'un modèle.
Le problème apparaît quand le graphe généré par le framework ne correspond pas au comportement matériel réel :
- Des opérations enchaînées qui pourraient être fusionnées en un seul kernel restent séparées, chacune lisant et écrivant en mémoire HBM.
- Des tenseurs mal alignés provoquent des accès mémoire non coalescents, invisibles dans le code Python.
- Un batch trop petit laisse des Streaming Multiprocessors inactifs alors que
nvidia-smiaffiche une utilisation proche de 100 %, car cette métrique ne dit rien du taux d'occupation réel. - Le compilateur de graphe échoue silencieusement à fusionner certaines opérations dès que le code contient du contrôle de flux dynamique, sans qu'aucune erreur ne soit remontée.
nvidia-smià 100 % ne signifie pas que le GPU travaille efficacement. Cette métrique mesure la présence d'au moins un kernel actif sur le GPU à l'instant de l'échantillonnage, pas le taux d'utilisation des unités de calcul. Un kernel qui attend sur la mémoire peut maintenir ce chiffre à 100 % en consommant une fraction du débit théorique.
Où part le temps : anatomie d'une passe d'inférence
Décomposer une passe forward sur GPU fait apparaître plusieurs postes de latence qui n'ont rien à voir avec la complexité algorithmique du modèle :
- Lancement de kernel — chaque appel CUDA a un coût fixe côté driver et côté file d'exécution, de l'ordre de quelques microsecondes. Un modèle qui enchaîne des centaines de petits kernels par tokentokenIAFragment de texte manipulé par un modèle de langage, généralement plus court qu'un mot — trois à quatre caractères en français. La tarification et la limite de contexte se comptent en tokens.Voir dans le glossaire généré paie ce coût à chaque étape.
- Transfert hôte-GPU — copier des donnéesdonnéesIAEnsemble d'informations structurées ou non utilisées pour entraîner, évaluer ou alimenter un modèle. La qualité, la quantité et la représentativité des données sont les facteurs décisifs pour les performances en apprentissage automatique.Voir dans le glossaire entre RAM système et mémoire GPU (PCIe, ou NVLink sur les configurations multi-GPU) prend un temps non négligeable si le pipeline ne le recouvre pas avec du calcul.
- Attente mémoire — la bande passante HBM est finie ; une opération limitée par la mémoire (memory-bound) n'accélère pas si on ajoute des cœurs de calcul, seulement si on réduit le volume de données lues et écrites.
- Calcul effectif — la seule portion qui bénéficie directement de la puissance brute en TFLOPS annoncée sur la fiche technique.
Pour l'inférence de grands modèles de langagegrand modèle de langageIAModèle entraîné à prédire le token suivant d'une séquence de texte. Toutes ses capacités apparentes — résumer, traduire, coder — découlent de cette unique tâche.Voir dans le glossaire, le troisième poste domine largement en génération token par token : chaque étape relit l'intégralité des poids (ou du cache clé-valeur) pour produire un seul token, ce qui rend l'opération structurellement memory-bound. C'est une des raisons pour lesquelles doubler la puissance de calcul brute d'un GPU n'accélère pas l'inférence dans les mêmes proportions.
La pile logicielle et matérielle
Comprendre CUDA avancé suppose de situer chaque couche de la pile, du code Python jusqu'au silicium.
Cette pile explique pourquoi un même modèle affiche des performances différentes selon la version de driver, la version de cuDNN, ou la façon dont le compilateur (torch.compile, XLA) a fusionné les opérations. Les chapitres suivants de cette formation descendent progressivement dans cette pile : modèle mémoire, écriture de kernels personnalisés, streams et recouvrement asynchrone, profiling avec Nsight, quantization, et déploiement multi-GPU.
Le poids économique du GPU
Un GPU de classe datacenter (H100, A100, ou équivalent) représente un investissement horaire significatif, que ce soit en capex amorti ou en location cloud. Le raisonnement « on ajoute des GPU pour aller plus vite » atteint vite ses limites budgétaires si l'efficacité par GPU reste basse.
Un service d'inférence traite 50 requêtes par seconde avec un taux d'utilisation réel des SM de 30 %. Faire passer ce taux à 60 % par un travail de profiling et d'optimisation des kernels (fusion, batching dynamique, réduction des transferts) permet théoriquement de traiter la même charge avec deux fois moins de GPU. Sur un déploiement de 20 GPU en location cloud, c'est l'équivalent de dix GPU libérés — sans changer une ligne du modèle, uniquement en changeant la façon dont il s'exécute sur le matériel.
Le tableau suivant résume les leviers qui jouent sur le coût par requête, du plus haut niveau (choix du modèle) au plus bas niveau (kernel) :
| Levier | Niveau | Gain typique | Effort |
|---|---|---|---|
| Changer d'architecture de modèle | Application | Variable, souvent important | Élevé |
| Quantization (FP16 → INT8/FP8) | Framework | 1.5x à 3x mémoire/débit | Moyen |
| Batching dynamique et continu | Serveur d'inférence | 2x à 5x débit | Moyen |
| Fusion de kernels | CUDA | 1.2x à 2x latence | Élevé |
| Gestion fine des streams et de la mémoire | CUDA | 1.1x à 1.5x | Élevé |
Les trois premiers leviers sont accessibles sans écrire de CUDA. Les deux derniers sont l'objet de cette formation : ils apportent des gains plus modestes en proportion, mais ce sont souvent les seuls qui restent disponibles une fois les optimisations de haut niveau épuisées — et à l'échelle d'une flotte de GPU en production, quelques points de pourcentage se traduisent en coûts d'infrastructure réels, sur des factures mensuelles à cinq ou six chiffres. La question n'est donc pas seulement technique : c'est un arbitrage entre coût d'ingénierie bas niveau et coût d'infrastructure récurrent, à évaluer projet par projet plutôt que par principe.
Silicium et inférence LLM : où ça se joue concrètement
Pour un grand modèle de langageLLMIAGrand modèle de langage (Large Language Model) entraîné sur d'énormes corpus pour prédire et générer du texte.Voir dans le glossaire servi en production, trois zones du silicium et de la pile logicielle concentrent l'essentiel des gains accessibles :
- Les Tensor Cores — unités dédiées au calcul matriciel en précisionprécisionIAProportion des alertes émises par un modèle qui sont justifiées. Elle s'oppose au rappel : améliorer l'une dégrade l'autre.Voir dans le glossaire réduite (FP16, BF16, FP8 sur les architectures récentes). Un kernel qui ne les sollicite pas correctement (mauvais alignement, mauvais type de données, tailles de matrices sous-optimales) laisse une capacité de calcul considérable inutilisée.
- Le cache clé-valeur (KV cache) — sa gestion mémoire détermine combien de requêtes peuvent être traitées simultanément sans saturer la HBM. Les techniques de pagination du KV cache, popularisées par des serveurs d'inférence comme vLLM, sont directement héritées de problématiques de gestion mémoire GPU bas niveau.
- Le recouvrement calcul/communication — sur un déploiement multi-GPU (tensor parallelism, pipeline parallelism), le temps passé à synchroniser les GPU entre eux via NVLink ou InfiniBand peut dépasser le temps de calcul si le recouvrement est mal conçu.
Ces trois zones interagissent directement avec un arbitrage central de la production : le compromis entre latence par requête et débit global. Augmenter la taille de batch améliore le débit agrégé du service en exploitant mieux les Tensor Cores, mais dégrade la latence individuelle de chaque requête, car chacune attend que le batch se remplisse ou que les autres requêtes du batch soient traitées. Le batching continu atténue ce compromis en insérant de nouvelles requêtes dans un batch déjà en cours d'exécution, mais ne l'élimine pas : il reste un paramètre de configuration à régler selon que le service vise un chatbot interactif ou un traitement par lots.
Le marketing autour d'un nouveau GPU met en avant les TFLOPS crête, une mesure atteinte dans des conditions synthétiques rarement représentatives d'une charge d'inférence réelle. Une charge memory-bound ou dominée par la synchronisation multi-GPU peut n'exploiter qu'une fraction de cette puissance annoncée, quel que soit le modèle de GPU utilisé.
Promesses marketing versus réalité de production
Il est utile de confronter dès ce premier chapitre les promesses habituelles aux contraintes réelles rencontrées en production :
| Promesse | Réalité en production |
|---|---|
| « Ce GPU est X fois plus rapide » | Vrai sur benchmark synthétique, rarement vrai sur une charge memory-bound réelle |
| « Il suffit d'augmenter le batch size » | Vrai jusqu'à saturation mémoire, puis dégradation ou erreurs OOM |
| « torch.compile suffit à tout optimiser » | Aide significativement, mais ne fusionne pas tout et échoue silencieusement sur certains graphes dynamiques |
| « Plus de GPU = plus rapide, linéairement » | Le passage au multi-GPU introduit un coût de communication qui érode le gain au-delà d'un certain nombre de cartes |
Ces écarts ne signifient pas que les outilstool callingIACapacité d'un agent ou d'un LLM à invoquer des outils externes (API, calcul, recherche) pendant le raisonnement.Voir dans le glossaire sont mal conçus. Ils signifient que les bornes de leur efficacité sont atteintes à un endroit précis, et que cet endroit se trouve exactement là où commence le travail couvert par cette formation : profiling, kernels, gestion mémoire, parallélisme.
Ce que couvre — et ne couvre pas — cette formation
Les onze chapitres suivants suivent une progression volontairement pratique : modèle mémoire GPU (hôte, global, partagée, registres), écriture et lancement de kernels, streams et recouvrement asynchrone, profiling avec Nsight Systems et Nsight Compute, quantization et précision mixte, puis parallélisme multi-GPU pour l'entraînement et l'inférence. Chaque chapitre s'appuie sur des exemples exécutables plutôt que sur des slides théoriques.
Cette formation ne couvre pas l'écriture de modèles depuis zéro, ni les architectures de réseaux de neuronesréseau de neuronesIAFonction mathématique composée de neurones artificiels organisés en couches, dont les coefficients sont ajustés pendant l'entraînement. Malgré son nom, il n'a presque rien de commun avec un cerveau.Voir dans le glossaire — ce sont des prérequis. Elle ne couvre pas non plus le choix d'un fournisseur cloud ou la négociation de contrats GPU : c'est un sujet business, pas un sujet d'ingénierie bas niveau.
Avant de lire les chapitres sur l'écriture de kernels, installez Nsight Systems et faites tourner un profil sur un script d'inférence que vous connaissez déjà. Repérer par vous-même où le temps est réellement dépensé rendra les chapitres suivants beaucoup plus concrets qu'une lecture linéaire.
Checklist avant d'investir dans l'optimisation bas niveau
Avant de descendre dans la pile CUDA, vérifiez que les optimisations à faible effort sont déjà en place :
- Le modèle tourne-t-il en précision réduite (FP16/BF16, voire FP8/INT8) sans perte de qualité inacceptable ?
- Le serveur d'inférence utilise-t-il le batching dynamique ou continu plutôt que des requêtes traitées une par une ?
- Le KV cache est-il géré par pagination plutôt que par allocation statique surdimensionnée ?
- Un profil a-t-il été réalisé pour identifier si la charge est compute-bound ou memory-bound avant de choisir une stratégie d'optimisation ?
- Le gain attendu d'un travail CUDA bas niveau a-t-il été estimé face au coût d'ingénierie et de maintenance qu'il introduit ?
Si ces cases ne sont pas cochées, il est probable que le retour sur investissement du travail bas niveau soit inférieur à celui des optimisations de plus haut niveau. Cette formation part du principe que ce travail préalable a été fait, et que la marge restante se trouve désormais dans le code CUDA lui-même.
Le chapitre suivant entre dans le premier de ces sujets bas niveau : le modèle mémoire du GPU, ses différents espaces (registres, mémoire partagée, mémoire globale, cache L2) et l'impact direct de leur usage sur la latence mesurée dans ce chapitre.
L'essentiel à retenir
Ce chapitre introductif montre pourquoi la maîtrise d'un framework comme PyTorch ne suffit pas à exploiter correctement un GPU en production. Il détaille les postes de latence d'une passe d'inférence (lancement de kernel, transfert, attente mémoire, calcul), situe le rôle du silicium dans l'inférence des grands modèles de langage, et confronte les promesses marketing des GPU à la réalité des déploiements. Il se termine sur une checklist des optimisations de haut niveau à épuiser avant d'investir dans du CUDA bas niveau, et pose le plan des onze chapitres suivants.
- Utilisation GPU réelle contre occupation apparente (nvidia-smi)
- Coût de lancement d'un kernel CUDA
- Charge memory-bound contre compute-bound
- Pile logicielle CUDA : framework, bibliothèques, driver, SM
- Tensor Cores et précision réduite
- Cache clé-valeur (KV cache) et pagination mémoire pour l'inférence LLM
- Coût de communication du parallélisme multi-GPU
Questions fréquentes
Faut-il apprendre CUDA si on utilise déjà PyTorch en production ?
Quelle est la différence entre une charge compute-bound et memory-bound ?
Pourquoi les benchmarks marketing des GPU sont-ils souvent trompeurs pour l'inférence de grands modèles de langage ?
Qu'est-ce que le cache clé-valeur (KV cache) et pourquoi sa gestion mémoire est-elle critique ?
Ai-je besoin d'un accès GPU pour suivre cette formation ?
Cette formation couvre-t-elle l'entraînement ou uniquement l'inférence ?
Par où commencer concrètement après avoir lu ce chapitre ?
Progression sauvegardée dans votre navigateur.
Quiz de validation
Quiz Player
Quiz de validation
Plusieurs réponses possibles — validez ensuite.
Vrai ou faux.
Quiz indisponible (données invalides).