Efficacité GPU : pourquoi « mieux » ne signifie pas toujours « plus grand »

Cet article vous a aidé ?

Quelqu’un achète le GPU haut de gamme brillant. Le tableau de bord s’illumine. La facture aussi. Et le job d’entraînement ? Il s’exécute… à peu près à la même vitesse que l’ancien serveur.

Si vous avez déjà fixé nvidia-smi montrant 20–40% d’utilisation pendant que vos parties prenantes demandent pourquoi leur « mise à niveau » n’a rien amélioré, vous êtes au bon endroit. La dure vérité : les GPU ne sont pas des accélérateurs magiques ; ce sont des coprocesseurs pointilleux et gourmands en bande passante. Vous ne « les utilisez » pas. Vous les nourrissez.

Idée centrale : « plus grand » n’est pas automatiquement « meilleur »

« Meilleur GPU » signifie généralement plus de calcul, plus de tensor cores, plus de bande passante mémoire, peut‑être plus de HBM et parfois plus de VRAM. Mais votre charge de travail est une chaîne de montage, pas une seule machine. Si la station la plus lente est le prétraitement CPU, les lectures stockage, les transferts PCIe, l’all‑reduce ou la latence de lancement des kernels, alors un GPU plus grand reste là… à attendre poliment.

En production, l’efficacité GPU n’est pas « pourcentage d’utilisation ». C’est le débit métier par dollar, par watt et par heure‑ingénieur. Un grand GPU majoritairement inactif n’est pas un produit premium ; c’est un radiateur coûteux bien marketé.

Énoncé pertinent pour la décision :

  • Si vous ne parvenez pas à garder occupé un GPU milieu de gamme, un GPU haut de gamme ne réparera pas votre pipeline.
  • Si vous avez une charge stable liée au calcul, un GPU plus grand peut être une victoire pure—mais seulement si l’interconnexion, la mémoire et la pile logicielle montent en échelle avec lui.
  • Si vous passez à plusieurs GPU, le réseau et les opérations collectives font partie de votre « GPU », que cela vous plaise ou non.

Une citation à coller sur l’écran : « L’espoir n’est pas une stratégie. » — Gene Kranz

Oui, c’est cliché. C’est aussi ce que je dis quand quelqu’un propose « achetons juste des GPU plus gros » sans mesurer où le temps est réellement dépensé.

Un modèle mental de production pour l’efficacité GPU

1) Pensez en étapes, pas en puces

Une boucle typique d’entraînement/inférence comprend des étapes qui peuvent se bloquer indépendamment :

  1. Source des données : object storage, NFS, NVMe local, base de données, feature store.
  2. Décodage/augmentation : transformations CPU, décodage d’images, tokenisation, compression.
  3. Transfert hôte→device : pinned memory vs pageable, PCIe vs NVLink, copies asynchrones.
  4. Calcul GPU : kernels, tensor cores, opérations liées à la mémoire, réductions.
  5. Device→device / collectives : all‑reduce multi‑GPU, attention sharded, pipeline parallelism.
  6. Checkpointing/logging : latence système de fichiers, tempêtes de métadonnées, écritures synchrones.

Le « GPU plus grand » accélère souvent seulement l’étape 4. Si l’étape 1 ou 2 est le goulot, l’étape 4 n’a pas d’importance. Si l’étape 5 domine à l’échelle, l’étape 4 devient une erreur d’arrondi.

2) Comprenez ce que « utilisation » cache

nvidia-smi GPU‑Util est un signal grossier : « le GPU a fait quelque chose récemment ? » Ce n’est pas une garantie d’efficacité. Un GPU peut afficher 95% d’utilisation tout en exécutant des kernels limités par la mémoire qui n’utilisent presque pas les tensor cores, ou en tournant sur de très petits kernels avec un gros overhead de lancement. Il peut afficher 30% tout en fournissant un excellent débit si la charge est éclatée et superposée.

3) Les GPU plus grands ont plus d’appétit

Quand vous passez d’un GPU plus petit à un plus grand, vous augmentez aussi le « débit d’alimentation » minimal requis :

  • Plus de calcul : signifie que vous avez besoin de batchs plus grands, plus de travail parallèle, de kernels fusionnés ou de plus de concurrence.
  • Plus de bande passante : signifie que vous avez besoin de schémas d’accès mémoire qui peuvent l’exploiter ; l’accès aléatoire ne deviendra pas magiquement séquentiel.
  • Plus de VRAM : peut réduire les transferts CPU‑GPU et permettre des modèles/batches plus grands—mais seulement si votre framework l’utilise intelligemment.

4) Latence et overhead ne diminuent pas

L’overhead de lancement de kernel, l’overhead de l’interpréteur Python, les synchronisations par étape, le logging et la coordination des dataloaders peuvent dominer pour les petits modèles ou les petites tailles de batch. Un GPU plus rapide raccourcit le temps de calcul, rendant l’overhead une plus grande fraction du total. Félicitations : vous vous êtes « optimisé » vers un nouveau goulot.

Blague #1 (courte et pertinente) : Un GPU plus grand ne peut pas réparer un dataloader lent plus qu’une tasse de café plus grande ne guérit l’insomnie.

5) L’efficacité est un contrat de pile

En termes SRE, votre GPU est en aval de : stockage, ordonnancement CPU, comportement de l’allocation mémoire, runtime de conteneur, drivers, bibliothèques, et parfois d’un ordonnanceur de cluster avec des opinions. Si une couche viole le contrat—I/O lente, voisins bruyants, mauvais pinning NUMA, mauvaise version CUDA—vous n’êtes pas « bound par le GPU », vous êtes « bound par tout le reste ».

Faits et contexte historique (qui comptent réellement)

Ce n’est pas de la trivia pour l’apéro. Chacun se raccorde à un vrai mode de défaillance ou à une décision d’architecture.

  1. Les GPU sont devenus généraux par accident et persistance : les premiers travaux « GPGPU » utilisaient des API graphiques pour faire du calcul avant que CUDA ne le rende grand public. Ce legs explique pourquoi les schémas d’accès mémoire restent si importants.
  2. Le pari de CUDA en 2007 : le modèle de programmation NVIDIA a rendu le calcul GPU accessible, mais il a aussi créé un écosystème où les incompatibilités driver/toolkit peuvent silencieusement coûter en performance.
  3. PCIe a été un limiteur récurrent : pour beaucoup de charges, les transferts hôte‑device restent un goulot même quand le calcul GPU progresse rapidement.
  4. HBM a changé la donne pour les modèles gourmands en bande passante : la mémoire à haute bande passante aide, mais les kernels limités par la mémoire exigent toujours localité et coalescence ; la bande passante ne corrige pas les mauvais schémas d’accès.
  5. Les tensor cores ont déplacé la conversation sur le « dtype optimal » : la précision mixte peut être transformative, mais ce n’est pas gratuit—le loss scaling, la numérisation et l’overhead de conversion peuvent mordre.
  6. NVLink/NVSwitch sont apparus parce que PCIe ne suffisait pas : l’échelle multi‑GPU dépend souvent plus de l’interconnect et de la topologie que des FLOPS bruts.
  7. Le deep learning a popularisé les « input pipelines » comme travail de performance à part entière : on a appris à la dure que le décodage JPEG peut devenir votre « goulot GPU ». Ce n’est pas le GPU.
  8. Le checkpointing s’est compliqué avec la taille des modèles : sauvegarder l’état peut devenir un problème de stockage distribué et de métadonnées, pas juste « écrire un fichier ».

Plan de diagnostic rapide (vérifications 1/2/3)

Premier point : le GPU est‑il réellement affamé ?

  • Regardez l’utilisation et l’usage mémoire par processus.
  • Vérifiez si la consommation électrique GPU est proche de l’attendu en état stable.
  • Vérifiez si le CPU est saturé ou si l’iowait est élevé.

Si l’utilisation GPU est faible et que CPU/iowait est élevé, arrêtez de blâmer le GPU. Réparez l’alimentation.

Second point : lié au calcul, à la mémoire ou à l’overhead/lancement ?

  • Comparez les TFLOPS atteints aux TFLOPS théoriques (approximativement) à l’aide des métriques du profileur.
  • Vérifiez le débit mémoire GPU et les indicateurs d’occupation SM.
  • Cherchez beaucoup de petits kernels, des points de synchronisation, ou de l’overhead Python.

Si vous êtes lié par la mémoire, « plus de calcul » n’aidera pas beaucoup. Si vous êtes limité par l’overhead, un GPU plus rapide aggrave le problème.

Troisième point : si multi‑GPU, l’interconnect est‑il le vrai goulot ?

  • Vérifiez la topologie : présence de NVLink, génération PCIe, placement NUMA.
  • Vérifiez la part de temps NCCL all‑reduce dans vos traces de profileur.
  • Vérifiez la saturation réseau (pour multi‑nœud) : RDMA, TCP, compteurs des switches.

Si les opérations collectives dominent, monter en gamme vers un GPU plus grand par nœud peut battre le scaling out—ou l’inverse—selon la topologie. Mesurez, ne visez pas à l’intuition.

Tâches pratiques : commandes, sorties et décisions

Ce sont les vérifications que vous pouvez exécuter lors d’un incident bridge sans config de profileur sur mesure. Chaque tâche inclut : commande, sortie typique, ce que cela signifie et la décision à prendre.

Task 1: Confirm GPU visibility, driver health, and basic load

cr0x@server:~$ nvidia-smi
Wed Jan 21 12:11:03 2026
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15    Driver Version: 550.54.15    CUDA Version: 12.4               |
|-----------------------------------------+----------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
|                                         |                      |               MIG M. |
|=========================================+======================+======================|
|   0  NVIDIA A100-PCIE-40GB          On  | 00000000:65:00.0 Off |                    0 |
|  33%   62C    P0              185W / 250W |  12450MiB / 40536MiB |     38%      Default |
+-----------------------------------------+----------------------+----------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|=======================================================================================|
|    0   N/A  N/A     21983      C   python3                                     12340MiB |
+---------------------------------------------------------------------------------------+

Ce que cela signifie : versions driver/toolkit, consommation électrique, mémoire utilisée, util et quel processus occupe le GPU.

Décision : Si GPU‑Util est faible et la puissance est basse en « état stable », vous êtes probablement affamé ou lié par l’overhead. Remontez dans la pile.

Task 2: Watch utilization and power over time (spot starvation)

cr0x@server:~$ nvidia-smi dmon -s pucvmt
# gpu   pwr gtemp mtemp    sm   mem   enc   dec  mclk  pclk
# Idx     W     C     C     %     %     %     %   MHz   MHz
    0   92    56     -    12     8     0     0  1215  1410
    0  210    64     -    85    72     0     0  1215  1410
    0   75    55     -     9     6     0     0  1215  1410

Ce que cela signifie : des pics élevés de SM% suivis de creux inactifs signifient souvent des stalls de pipeline d’entrée ou des points de sync.

Décision : Si vous voyez un motif en « scie », investiguez le dataloader, les copies hôte‑device et la contention CPU.

Task 3: Identify CPU saturation vs iowait (classic feeder failure)

cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.5.0 (server) 	01/21/2026 	_x86_64_	(64 CPU)

12:12:11 PM  CPU   %usr %nice  %sys %iowait  %irq %soft  %steal  %idle
12:12:12 PM  all   420.0  0.0   18.0   60.0   0.0  2.0    0.0   0.0
12:12:12 PM   12    97.0  0.0    1.0    2.0   0.0  0.0    0.0   0.0
12:12:12 PM   13    95.0  0.0    3.0    2.0   0.0  0.0    0.0   0.0

Ce que cela signifie : un %usr élevé sur de nombreux cœurs suggère du prétraitement CPU ou de l’overhead Python ; un %iowait élevé suggère des stalls de stockage.

Décision : Si %iowait est élevé, profilez le stockage. Si %usr est saturé, optimisez le décodage/tokenisation, augmentez le nombre de workers ou externalisez les transformations.

Task 4: Check process-level I/O stalls (is your dataset slow?)

cr0x@server:~$ iostat -xz 1 2
Linux 6.5.0 (server) 	01/21/2026 	_x86_64_	(64 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          30.12    0.00    2.10   22.45    0.00   45.33

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s  w_await aqu-sz  %util
nvme0n1         420.0   58240.0     0.0   0.00    8.10   138.67    15.0   2048.0    2.30   3.60  82.00

Ce que cela signifie : un %util élevé et un r_await élevé indiquent que le dispositif est occupé et que les lectures attendent.

Décision : Si le stockage est chaud, mettez en cache les datasets localement, augmentez prudemment le parallélisme de lecture, ou changez de format (moins de petits fichiers).

Task 5: Spot “too many small files” (metadata pain)

cr0x@server:~$ find /datasets/vision/train -type f | head -n 5
/datasets/vision/train/000001.jpg
/datasets/vision/train/000002.jpg
/datasets/vision/train/000003.jpg
/datasets/vision/train/000004.jpg
/datasets/vision/train/000005.jpg

Ce que cela signifie : un dataset image par fichier peut écraser les performances métadonnées sur des systèmes de fichiers en réseau.

Décision : Consolidez en formats shardés (tar/LMDB/fichiers record), ou staged localement sur NVMe.

Task 6: Check PCIe link speed and width (quiet throughput killer)

cr0x@server:~$ nvidia-smi -q -d pcie | sed -n '1,80p'
==============NVSMI LOG==============

GPU 00000000:65:00.0
    PCIe Generation
        Current                       : 3
        Max                           : 4
    Link Width
        Current                       : x8
        Max                           : x16
    Tx Throughput                     : 1200 KB/s
    Rx Throughput                     : 980 KB/s

Ce que cela signifie : fonctionner en Gen3 x8 alors que vous attendiez Gen4 x16 réduit réellement la bande passante hôte‑device.

Décision : Reseat la carte, vérifiez les réglages du BIOS, contrôlelez les risers, vérifiez le câblage du slot et confirmez qu’elle ne partage pas de lanes avec d’autres périphériques.

Task 7: Validate NUMA locality (CPU feeds GPU through the right socket)

cr0x@server:~$ nvidia-smi topo -m
        GPU0    CPU Affinity    NUMA Affinity
GPU0     X      0-31            0

Legend:
  X    = Self

Ce que cela signifie : les cœurs CPU 0–31 sont locaux à GPU0. Utiliser le mauvais socket peut augmenter la latence et réduire la bande passante effective.

Décision : Pinner les workers du dataloader et le processus principal au nœud NUMA local lorsque la performance compte.

Task 8: Check thermal or power throttling (the invisible handbrake)

cr0x@server:~$ nvidia-smi -q -d PERFORMANCE | sed -n '1,120p'
==============NVSMI LOG==============

GPU 00000000:65:00.0
    Performance State               : P2
    Clocks Throttle Reasons
        Idle                        : Not Active
        Applications Clocks Setting  : Not Active
        SW Power Cap                : Active
        HW Slowdown                 : Not Active

Ce que cela signifie : SW Power Cap: Active suggère que votre job atteint une limite de puissance (ou un cap configuré) et réduit ses clocks.

Décision : Ajustez la limite de puissance (si la politique le permet), améliorez le refroidissement, ou acceptez que votre GPU « plus grand » ne tourne pas aux fréquences annoncées.

Task 9: Confirm CPU frequency isn’t stuck low (container host misconfig)

cr0x@server:~$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
powersave

Ce que cela signifie : des CPUs en powersave peuvent ralentir le prétraitement et la coordination, affamant les GPU.

Décision : Définissez le gouverneur performance sur les nœuds dédiés à l’entraînement (en respectant la politique ops).

Task 10: Confirm hugepages / pinned memory pressure symptoms (H2D copy stalls)

cr0x@server:~$ grep -E 'HugePages|MemAvailable' /proc/meminfo
MemAvailable:   18432000 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0

Ce que cela signifie : pas définitif, mais une faible mémoire disponible combinée à un dataloading intensif peut augmenter le paging et ralentir les transferts.

Décision : Réduisez la pression mémoire hôte, considérez les réglages de pinned memory, et évitez de sursouscrire la RAM avec trop de workers.

Task 11: Check container cgroup CPU limits (self-inflicted starvation)

cr0x@server:~$ cat /sys/fs/cgroup/cpu.max
200000 100000

Ce que cela signifie : quota 200ms par période de 100ms plafonne effectivement à 2 CPU de temps. Pour un job GPU, c’est souvent ridicule.

Décision : Augmentez les requests/limits CPU pour que votre pipeline d’entrée puisse respirer ; adaptez le CPU à la classe GPU.

Task 12: Check dataloader worker starvation (Python-side symptoms)

cr0x@server:~$ ps -o pid,pcpu,pmem,cmd -C python3 --sort=-pcpu | head
  PID %CPU %MEM CMD
21983 780.2 12.4 python3 train.py --config prod.yaml
22010  95.1  1.2 python3 -c from multiprocessing.spawn import spawn_main; spawn_main(...)
22011  94.6  1.2 python3 -c from multiprocessing.spawn import spawn_main; spawn_main(...)

Ce que cela signifie : plusieurs processus worker sont actifs ; si seul le processus principal est occupé, vos workers peuvent être bloqués sur I/O, du travail lié au GIL, ou une mauvaise config.

Décision : Ajustez le nombre de workers, déplacez les transforms vers du code natif vectorisé, précalculez des features, ou utilisez des formats de dataset plus rapides.

Task 13: Observe network throughput for multi-node training

cr0x@server:~$ sar -n DEV 1 2 | grep -E 'IFACE|mlx|eth'
12:15:01 PM     IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s
12:15:02 PM       eth0   8200.00   7900.00  910000.00  880000.00
12:15:03 PM       eth0   8300.00   8100.00  920000.00  895000.00

Ce que cela signifie : si le réseau est saturé durant les phases all‑reduce, le scaling se fige.

Décision : Si vous êtes saturé, revoyez les réglages NCCL, la topologie, la compression de gradients, ou le nombre de nœuds.

Task 14: Identify filesystem latency spikes during checkpointing

cr0x@server:~$ dmesg | tail -n 8
[123456.120001] nfs: server nfs01 not responding, still trying
[123456.421019] nfs: server nfs01 OK
[123457.002341] INFO: task python3:21983 blocked for more than 120 seconds.

Ce que cela signifie : votre étape d’entraînement peut aller bien ; l’écriture d’un checkpoint bloque tout.

Décision : Rendez le checkpointing asynchrone, écrivez localement puis téléversez, ou changez de backend de stockage.

Où les gros GPU échouent : goulots d’étranglement courants par couche

Pipeline d’entrée : la taxe silencieuse sur le débit

Si vous vous entraînez sur images, vidéo ou grands corpus textuels, vous faites tourner une usine :

  • Lire des octets compressés
  • Décoder (souvent CPU)
  • Transformer/augmenter
  • Batcher, coller, padding
  • Copier vers le GPU

Chaque étape peut être « assez rapide » pour un GPU plus petit et soudainement insuffisante pour un plus grand. La mise à niveau ne casse pas le code ; elle casse les hypothèses sur la marge dont vous disposiez.

Les GPU haut de gamme amplifient les pipelines faibles. Ce n’est pas une métaphore ; c’est de la théorie des files d’attente de base. Quand le temps de service d’une étape diminue, l’étape suivante devient le goulot.

Overhead côté CPU : mort par mille synchronisations

Python est merveilleux pour exprimer une intention. Il n’est pas merveilleux pour exécuter des opérations petites et fréquentes dans une boucle serrée tout en coordonnant un device capable d’ingérer des téraflops. Si votre boucle par‑étape inclut du logging, de l’agrégation de métriques, des synchronisations fréquentes device, ou du travail CPU non vectorisé, un GPU plus rapide raccourcit la fenêtre de calcul et amplifie l’overhead CPU.

Coupables courants :

  • Appeler .item() ou forcer la synchronisation à chaque étape
  • Changements de forme fréquents empêchant la fusion de kernels
  • Taille de batch trop petite (surtout en inference)
  • Prétraitement par échantillon excessif en Python

Transfert hôte‑device : PCIe n’est pas une suggestion

Quand on parle de « vitesse GPU », on oublie souvent le bus. La bande passante et la latence PCIe sont des propriétés fixes de votre plateforme, pas de vos aspirations. Si votre modèle déplace répétitivement des données entre CPU et GPU, ou si vous avez des copies pageable et des points de sync, vous pouvez être limité par les transferts bien avant le calcul.

Anti‑patron typique : « On gardera les embeddings sur CPU pour économiser de la VRAM. » Sur un gros GPU, cela peut être catastrophique.

Kernels limités par la mémoire : quand les FLOPS importent peu

Certaines opérations sont liées à la bande passante mémoire ou au comportement du cache, pas au calcul. Pensez aux opérations élémentaires, certaines couches de normalisation, lookups d’embeddings et patterns d’attention non optimisés pour la localité. Vous pouvez acheter plus de calcul et voir peu d’amélioration.

Signaux :

  • GPU‑Util élevé mais consommation électrique modeste par rapport à l’attendu
  • Le profileur montre une faible utilisation des tensor cores
  • Débit mémoire proche du pic tandis que l’occupation SM est « occupée » mais pas productive

Multi‑GPU : les taxes de scaling arrivent vite et ne partent pas

Le training multi‑GPU est une dispute avec la physique. À un moment, vous passez plus de temps à coordonner qu’à calculer. Les GPU plus grands peuvent aider en réduisant le nombre de GPU nécessaires pour un batch/modèle donné, ce qui réduit l’overhead collectif. Ou ils peuvent nuire en encourageant des counts de nœuds plus élevés avec une interconnexion faible, rendant l’all‑reduce dominant.

La topologie compte. NVLink vs PCIe. Placement des sockets. Oversubscription des switches. Si vous ne connaissez pas votre topologie, vous ne connaissez pas votre performance.

Checkpointing et observabilité : le phénomène « tout allait bien jusqu’à l’écriture »

Le checkpointing est souvent synchronisé. Si vous écrivez depuis chaque rang ou sur un filesystem partagé lent, votre job semblera « mystérieusement lent » même si le calcul est sain. Les pics de latence du stockage deviennent des pics d’inactivité GPU.

Blague #2 (courte et pertinente) : Écrire des checkpoints sur un filesystem partagé aux heures de pointe est une excellente façon d’apprendre ce qu’est la « contre‑pression », émotionnellement.

Trois mini‑histoires du monde de l’entreprise (anonymisées)

Mini‑histoire 1 : l’incident causé par une mauvaise hypothèse

Une entreprise a déployé de nouveaux nœuds GPU pour accélérer un retrain de modèle de recommandation. La demande de changement était propre : GPU plus gros, même code, même dataset. L’hypothèse était simple et réconfortante : « le calcul était le goulot ».

En quelques jours, le SLA d’entraînement a glissé. Les GPU signalaient une faible utilisation, et le canal on‑call s’est rempli de captures d’écran d’accélérateurs inactifs et de factures cloud pas inactives du tout. Quelqu’un a blâmé les drivers. Quelqu’un d’autre a blâmé le framework. Le classique.

Le vrai problème était plus ennuyeux : le dataset vivait sur un filesystem réseau partagé. Les anciens nœuds avaient des GPU plus petits qui prenaient plus de temps par étape, donc la latence du filesystem restait masquée par le calcul. Les nouveaux GPU ont raccourci tellement le calcul que chaque étape s’est heurtée à un mur de lecture/métadonnées. Le job ne s’est pas accéléré ; il est devenu plus sensible.

Le correctif a été de staged data : rsync nocturne (ou sync depuis l’object store) vers le NVMe local, plus sharding des petits fichiers en plus gros blobs. L’utilisation a grimpé, le temps d’entraînement a chuté, et l’incident s’est arrêté. La leçon n’était pas « les fichiers réseaux c’est mal ». La leçon : mesurez le pipeline de bout en bout avant de ne changer qu’une station.

Mini‑histoire 2 : l’optimisation qui s’est retournée contre eux

Une autre équipe a optimisé pour la mémoire GPU en réduisant agressivement la taille de batch et en activant le gradient checkpointing partout. L’usage VRAM semblait excellent. Le modèle rentrait confortablement, laissant de la marge pour l’avenir. Tout le monde a célébré—discrètement, car célébrer est aussi de l’overhead.

Puis le débit s’est effondré. Les GPU étaient « occupés », mais le temps par étape a augmenté. Le profileur montrait plus de recomputation (prévisible) et une inondation de petits kernels (moins prévisible). Le batch plus petit augmentait l’overhead par étape, réduisait les opportunités de fusion de kernels, et détériorait l’efficacité de l’all‑reduce parce que le ratio calcul/communication avait chuté.

Ils avaient optimisé la mauvaise contrainte. Le système n’était pas lié par la VRAM ; il était lié par la latence/l’overhead. L’« optimisation mémoire » a coûté en calcul, synchronisation et communication.

Le plan de récupération fut pragmatique : augmenter la taille de batch jusqu’à ce que les kernels redeviennent conséquents, checkpoint seulement les couches nécessaires, et utiliser la précision mixte avec validation attentive. L’usage mémoire a remonté. Le débit a remonté davantage. La métrique métier—modèles entraînés par jour—a enfin correspondu à la dépense matérielle.

Mini‑histoire 3 : la pratique ennuyeuse mais correcte qui a sauvé la journée

Une équipe plateforme avait une règle qui agaçait tout le monde : chaque image de nœud GPU avait une petite « suite de sanity performance » qui tournait après le provisioning. Elle vérifiait la largeur PCIe, le gouverneur CPU, la compatibilité driver/bibliothèque, et des tests de bande passante NCCL basiques. Les gens appelaient ça de la bureaucratie.

Une semaine, un lot de nouveaux serveurs est arrivé. Les jobs tournaient, mais la performance multi‑GPU était affreuse sur un sous‑ensemble de nœuds. La suite a immédiatement signalé ces nœuds : les GPU avaient négocié une largeur PCIe réduite à cause d’un riser défectueux, et certains nœuds avaient un réglage BIOS limitant la vitesse de lien. Les GPU étaient corrects. La plateforme pas.

Parce que les vérifications tournaient automatiquement, l’équipe a mis en quarantaine les nœuds défaillants avant qu’ils ne polluent la flotte d’entraînement. Pas d’incident prolongé, pas de chasse au trésor, pas de postmortem accusant le mauvais coupable.

C’était ennuyeux. C’était correct. Et ça a sauvé des jours de temps d’ingénieur—qui est la ressource la plus chère dans le bâtiment.

Erreurs courantes : symptôme → cause racine → correctif

1) Faible GPU‑Util, usage CPU élevé

Symptôme : GPU à 10–40%, CPU saturé.

Cause racine : prétraitement CPU (decode/tokenisation/augmentation), overhead Python, trop peu de workers dataloader.

Correctif : Augmentez les workers, déplacez les transforms vers des ops vectorisées/natives, mettez en cache les données prétraitées, pinner au nœud NUMA correct, assurez‑vous que les limites CPU du conteneur ne vous étouffent pas.

2) Faible GPU‑Util, iowait élevé

Symptôme : creux d’inactivité GPU correspondant à des pics de latence stockage.

Cause racine : dataset sur stockage réseau lent, trop de petits fichiers, contention métadonnées.

Correctif : shardez les datasets, stagez sur NVMe local, utilisez read‑ahead/caching, évitez les ouvertures de fichier par échantillon, rendez le checkpointing asynchrone.

3) GPU‑Util élevé mais débit décevant

Symptôme : 90–100% util, mais temps par étape médiocre.

Cause racine : kernels limités par la mémoire, mauvaise fusion de kernels, attention non optimisée, conversions de dtype.

Correctif : utilisez des kernels fusionnés quand possible, ajustez la taille de batch/sequence pour améliorer l’efficacité, validez la précision mixte, profilez pour trouver les opérateurs chauds.

4) Régression de performance après passage à un GPU « plus grand »

Symptôme : le nouveau GPU est plus lent ou à peine plus rapide.

Cause racine : taille de batch inchangée menant à la domination de l’overhead ; lien PCIe négocié à la baisse ; throttling power ; mauvais gouverneur CPU.

Correctif : retunez la taille de batch, validez la Gen/largeur PCIe, vérifiez les raisons de throttling, définissez le gouverneur CPU adéquat, vérifiez la localité NUMA.

5) Le scaling multi‑GPU stagne après 2–4 GPU

Symptôme : doubler les GPU ne double pas le débit.

Cause racine : all‑reduce dominant, interconnect faible, topologie mauvaise, batch par GPU trop petit.

Correctif : augmentez le travail par GPU, optimisez les collectives (tailles de bucket), utilisez moins de nœuds avec des GPU plus gros, ou améliorez l’alignement réseau/interconnect.

6) Ralentissements aléatoires ou « toutes les 10 minutes ça s’arrête »

Symptôme : entraînement régulier interrompu par des stalls périodiques.

Cause racine : checkpointing/logging, pauses GC, hiccups filesystem, compactions en arrière‑plan.

Correctif : échelonnez les checkpoints, écrivez local puis téléversez, réduisez le logging synchrone, surveillez la latence filesystem, évitez les hotspots métadonnées partagées.

Listes de contrôle / plan pas à pas

Étape par étape : rendre un GPU plus grand réellement plus rapide

  1. Établissez une base : mesurez images/s, tokens/s ou requêtes/s sur l’ancien GPU avec le même code et le même snapshot de dataset.
  2. Confirmez la santé de la plateforme : version driver, Gen/largeur PCIe, gouverneur CPU, topologie NUMA, limites de puissance.
  3. Vérifiez l’alimentation : débit/latence du stockage, santé des workers du dataloader, marge CPU, pression mémoire.
  4. Retunez la taille de batch : un GPU plus grand veut souvent des batches plus volumineux ; si vous ne le pouvez pas, attendez des rendements décroissants.
  5. Profilez une exécution représentative : identifiez les kernels principaux, le temps d’attente CPU, le temps H2D, le temps de collectives.
  6. Corrigez le plus gros stall : n’« optimisez » pas tout. Traitez le goulot qui domine le temps mur.
  7. Validez la correction : surtout avec la précision mixte et les kernels fusionnés—les régressions silencieuses de précision sont de vraies pannes, juste plus lentes.
  8. Recontrôlez l’efficacité par dollar : parfois deux GPU « plus petits » bien alimentés battent un grand GPU affamé.

Checklist : ce qu’il faut éviter quand on achète des GPU

  • Acheter pour des TFLOPS de pointe sans cartographier le goulot de votre charge (calcul vs mémoire vs I/O vs communication).
  • Supposer que la VRAM résout tout ; elle résout certaines choses et en introduit d’autres (checkpoints plus gros, démarrages plus longs, pannes plus coûteuses).
  • Ignorer la topologie d’interconnexion pour le multi‑GPU (lignes PCIe, présence NVLink, placement NUMA).
  • Ignorer le CPU : un CPU faible peut affamer un GPU monstre.
  • Ignorer le stockage : « le dataset est sur NFS partagé » n’est pas un plan de performance.

Checklist : « contrôles SRE ennuyeux » qui gardent les flottes GPU rapides

  • Tests d’acceptation au niveau nœud : Gen/largeur PCIe, tests de bande passante, vérifications des raisons de throttling.
  • Images standardisées avec compatibilité driver/bibliothèque verrouillée.
  • Dashboards montrant util GPU, puissance, bande passante mémoire, CPU iowait et latence stockage.
  • Automation de quarantaine pour les nœuds qui négocient une largeur réduite ou montrent des erreurs Xid répétées.
  • Runbooks spécifiques aux charges (training vs inference vs batch embedding jobs).

FAQ

1) Si mon utilisation GPU est faible, est‑ce que le GPU est le problème ?

Généralement non. La faible utilisation est souvent un symptôme de famine : prétraitement CPU, stockage, réseau ou synchronisation. Confirmez avec la consommation électrique et le CPU/iowait.

2) Pourquoi le passage à un GPU plus rapide a empiré mon job ?

Vous avez raccourci la tranche de calcul, donc l’overhead fixe (Python, coordination du dataloader, I/O) est devenu dominant. Les GPU plus grands réduisent le temps de calcul ; ils ne réduisent pas vos mauvaises habitudes.

3) Augmenter la taille de batch est‑ce toujours la solution ?

Non. Cela peut améliorer l’efficacité des kernels et amortir l’overhead, mais ça peut nuire à la convergence, augmenter la mémoire, ou déplacer les goulots vers la communication. Réglez avec des métriques, pas par foi.

4) PCIe vs NVLink : quand faut‑il s’en soucier ?

Souciez‑vous‑en quand vous faites du multi‑GPU sur un nœud ou des transferts hôte‑device fréquents. Si les collectives ou les transferts pair à pair sont significatifs, la topologie est déterminante.

5) La précision mixte n’a pas accéléré. Pourquoi ?

Vous pouvez être lié par la mémoire, par l’overhead, ou perdre du temps à convertir les dtypes. Ou votre modèle a des ops qui ne bénéficient pas des tensor cores. Profilez pour vérifier où le temps est passé.

6) Quelle est la manière la plus rapide de dire si je suis limité par le stockage ?

Cherchez un iowait élevé et une forte utilisation disque/réseau pendant l’entraînement, plus des motifs en scie de SM% GPU. Validez ensuite avec iostat et les logs filesystem.

7) Pourquoi le multi‑GPU ne scale‑t‑il pas linéairement ?

Parce que vous payez des coûts de coordination (all‑reduce, synchronisation, stragglers). En ajoutant des GPU, la communication croît et finit par dominer à moins que la charge par GPU n’augmente aussi.

8) Dois‑je acheter un énorme GPU ou plusieurs petits ?

Ça dépend de votre goulot. Si vous êtes lié par la communication, moins de GPU plus gros peut être mieux. Si vous êtes throughput‑bound avec beaucoup de jobs indépendants, plusieurs GPU plus petits gagnent opérationnellement.

9) L’ordonnancement Kubernetes peut‑il nuire à l’efficacité GPU ?

Oui. Limites CPU, pression mémoire, voisins bruyants et mauvais alignement NUMA peuvent affamer les GPU. Si vous traitez les pods GPU comme des pods web stateless, vous découvrirez de nouvelles formes de tristesse.

10) Quel est un objectif d’utilisation GPU « sain » ?

Il n’y a pas un seul chiffre. Pour un entraînement stable, 80–95% peut être bon. Pour une inference bursty, une utilisation plus faible peut être optimale si les SLO de latence sont respectés. Suivez le débit et la latence tail, pas les métriques d’ego.

Étapes pratiques suivantes

Si vous êtes sur le point d’acheter des GPU plus gros, ou que vous l’avez déjà fait et que vous le regrettez, faites ceci dans l’ordre :

  1. Exécutez le plan de diagnostic rapide et capturez une fenêtre de 10 minutes d’util GPU, puissance, usage CPU, iowait et stats stockage/réseau.
  2. Validez les bases de la plateforme : Gen/largeur PCIe, raisons de throttling, affinité NUMA, gouverneur CPU, limites CPU conteneur.
  3. Réparez l’alimentation avant de toucher le code modèle : shardez les données, stagez localement, réduisez l’overhead des petits fichiers, ajoutez du parallélisme là où c’est utile.
  4. Puis retunez la charge : taille de batch, précision mixte, opportunités de fusion de kernels, réglages de communication multi‑GPU.
  5. Institutionnalisez les vérifications ennuyeuses : tests d’acceptation sur nœuds, dashboards montrant la famine, et un runbook nommant les principaux goulots que vous avez réellement vus.

La chute n’est pas « n’achetez pas de gros GPU ». Achetez‑les quand la charge le mérite. Mais traitez la performance GPU comme tout autre système de production : mesurez le goulot, changez une chose, vérifiez, répétez. Le matériel est rapide. Les systèmes sont lents. C’est pour ça que vous avez un travail.

← Précédent
Planification de capacité ZFS : concevoir la croissance sans reconstruction
Suivant →
Latences RAM sans douleur : MHz vs CL et quoi acheter

Laisser un commentaire