Pourquoi deux GPU sont souvent pires qu’un seul (Les vraies raisons)

Cet article vous a aidé ?

Vous avez acheté un deuxième GPU en vous attendant à voir les courbes grimper. À la place, votre entraînement est devenu saccadé, la latence d’inférence étrange, et la moitié de votre « utilisation GPU »
s’est avérée être une barre de progression pour l’attente. Les tableaux de bord semblent occupés. L’horloge murale a l’air gênée.

Voici la partie que personne ne met en avant : deux GPU n’équivalent pas automatiquement à « deux fois plus de calcul ». Souvent, cela signifie deux fois plus de modes de défaillance, deux fois plus de consommation,
et une nouvelle catégorie de goulots que vous n’aviez pas hier.

Le mythe : « Deux GPU = 2× plus rapide »

Le mythe persiste parce qu’il est vrai dans un monde étroit : les charges de travail embarrassantes parallèles avec peu de synchronisation, peu de mouvements de données, et une pile logicielle qui
répartit réellement le travail entre les appareils de manière efficace. Ce monde existe. Ce n’est simplement pas le monde dans lequel la plupart des équipes évoluent.

Dans les systèmes en production, l’échelle n’est rarement bloquée par les FLOPs bruts. Elle est bloquée par tout ce qui les entoure : copies mémoire, overhead de lancement de kernel, bulles de pipeline, contention CPU,
topologie PCIe, placement NUMA, comportement des pilotes, throttling thermique, et la réalité opérationnelle qu’avoir plus de matériel augmente la surface d’attaque pour les bizarreries.

Donc quand les gens disent « multi-GPU », ce qu’ils veulent généralement dire c’est « j’ai ajouté de la complexité et maintenant je négocie avec la physique. »

Les vraies raisons pour lesquelles deux GPU déçoivent

1) La VRAM n’est pas additive (la plupart du temps), et c’est la première trahison

Deux GPU de 24 Go ne deviennent pas magiquement 48 Go de mémoire utilisable pour le modèle. À moins de sharder explicitement le modèle (parallélisme tensoriel, pipeline, expert),
chaque GPU a besoin de sa propre copie des paramètres (ou de grands sous-ensembles), plus des activations, de l’état de l’optimiseur et de la mémoire de travail.

L’entraînement en data-parallel est le mode multi-GPU « facile » courant : chaque GPU exécute le même modèle sur des mini-batches différents. Vous obtenez plus de débit seulement si vous arrivez à les alimenter
et si la synchronisation (réduction des gradients) ne gruge pas le gain. Mais la capacité mémoire pour le modèle lui-même ne double pas. Vous avez toujours un plafond par GPU.

Ce plafond devient un piège de planification gênant : les équipes achètent un deuxième GPU « pour la capacité », puis découvrent qu’elles avaient besoin d’un GPU unique plus grand, ou d’une autre stratégie
de parallélisme, ou des deux.

2) Overhead de synchronisation : la taxe que vous ne voyez pas jusqu’à ce qu’il soit trop tard

La plupart des entraînements multi-GPU ne sont pas limités par le calcul ; ils sont limités par la vitesse à laquelle les GPU peuvent se mettre d’accord sur ce qui vient de se passer. En data-parallel, c’est un all-reduce des gradients.
Plus vous ajoutez de GPU, plus vous passez de temps à coordonner.

Même avec des liens haut débit, la synchronisation n’est pas gratuite. Elle est aussi en rafales : vous avez une belle série de kernels, puis une grosse phase de communication où l’utilisation chute et vous
faites semblant que c’est « ok » parce que « le GPU est alloué ».

L’overhead de communication devient particulièrement pénible lorsque :

  • La taille de batch par GPU devient trop petite (l’efficacité des kernels chute, l’overhead domine).
  • Votre modèle comporte de nombreux petits tenseurs (plus d’appels de réduction, sensibilité à la latence).
  • Votre interconnexion est uniquement PCIe et le trafic concurrence l’I/O hôte.
  • La topologie force du trafic inter-socket CPU (pénalité NUMA déguisée en « comm GPU »).

3) Topologie PCIe : vos GPU sont « proches » dans le châssis, loin en réalité

Deux GPU peuvent être connectés de plusieurs façons qui semblent identiques sur un bon de commande et radicalement différentes en performance :

  • Les deux GPU sous le même root complex PCIe (souvent mieux).
  • Chaque GPU attaché à un socket CPU différent (commun dans les serveurs dual-socket).
  • Partage de lanes ou bifurcation qui réduit la bande passante effective.
  • Trafic routé via un pont chipset (pénalités de latence et de bande passante).

Si GPU0 est « local » à CPU0 et GPU1 est « local » à CPU1, et que votre processus est épinglé sur le mauvais socket, félicitations : vous avez inventé une régression de performance rien qu’avec l’achat.

4) P2P et NVLink ne sont pas magiques ; ce sont des contrats que l’on peut casser par accident

L’accès peer-to-peer (P2P) permet à un GPU de lire la mémoire d’un autre sans passer par la RAM hôte. NVLink élève encore le plafond. Mais vous ne les obtenez pas toujours :

  • Le P2P peut être désactivé par des particularités de plate-forme, des réglages IOMMU, des couches de virtualisation ou des paramètres BIOS.
  • La disponibilité de NVLink dépend du SKU exact du GPU et du câblage de la machine.
  • Même quand ils sont présents, votre framework peut ne pas choisir la voie optimale sans configuration correcte.

Le multi-GPU échoue souvent non pas parce que le matériel est lent, mais parce que votre pile bascule silencieusement vers des copies médiées par l’hôte et que votre « interconnexion rapide » devient une rumeur.

5) Les goulots CPU s’aggravent quand vous ajoutez des GPU

Un seul GPU peut masquer beaucoup de fautes CPU. Deux GPU les rendent visibles. Le CPU doit :

  • Décoder et augmenter plus de données par unité de temps (pression sur les data loaders).
  • Lancer plus de kernels (overhead pilote, overhead Python, overhead framework).
  • Orchestrer des communications collectives (initialisation NCCL, gestion des streams).
  • Gérer plus d’interruptions et de bookkeeping DMA.

Si votre pipeline est déjà limite, doubler le nombre de GPU double simplement le rythme auquel vous atteignez « en attente d’entrée ».

6) I/O et stockage : le goulot discret qui ruine la « mise à l’échelle »

Si vous entraînez sur des images ou des datasets shardés et que vous n’avez pas un vrai plan I/O, deux GPU sont une façon très coûteuse de découvrir que votre stockage est correct « pour un job » mais pas pour deux.
J’ai vu des entraînements multi-GPU transformer un système stable en une multitude de petites lectures et d’opérations métadonnées.

Ajouter des GPU augmente la demande pour :

  • Débit de lecture des datasets (MB/s et IOPS).
  • Opérations métadonnées (beaucoup de petits fichiers, listing d’object store, indexation tar).
  • Écritures de checkpoints (pics d’écriture, pression fsync).

Quand le stockage devient le limiteur, l’utilisation GPU devient du théâtre : « occupé » n’est pas synonyme de « productif ».

7) Microstutter et irrégularité pour les workloads graphiques / interactifs

En jeu et visualisation, le multi-GPU a historiquement reposé sur des astuces comme le rendu par images alternées. Cela peut augmenter la moyenne de FPS tout en rendant les temps de trame irréguliers.
Les humains remarquent l’irrégularité. Les graphiques de benchmark, non.

Voilà pourquoi « ça monte à 120 FPS » peut encore paraître pire qu’un GPU unique stable à 75 FPS. L’unité qui compte n’est pas « frames per second ». C’est « combien de temps a pris la dernière trame », répété, sous charge.

8) Les chemins logiciels multi-GPU sont moins testés et plus fragiles

Le code mono-GPU est le chemin par défaut. Le multi-GPU est le « mode avancé », et les modes avancés attirent des cas limites comme des mouches attirées par une tapette.

Points de fragilité typiques :

  • Deadlocks dans les collectives distribuées lorsqu’un rang lève une exception et que les autres restent en attente.
  • Timeouts et hangs qui n’apparaissent que sous jitter réseau ou forte charge.
  • Non-déterminisme dû à l’exécution asynchrone et visibilité de debug réduite.
  • Réinitialisations de pilote qui anéantissent le job entier, pas seulement un appareil.

9) Mathématique de la fiabilité : deux GPU doublent les façons de perdre un run

Deux appareils signifient deux fois plus de ventilateurs, deux fois plus de VRAM, deux fois plus de chances qu’un d’entre eux soit limite en température, et deux fois plus d’opportunités pour un riser ou un connecteur d’alimentation
capricieux d’introduire des fautes intermittentes.

Les jobs d’entraînement sont longs. Les jobs longs amplifient les événements rares. Le mode de défaillance passe de « performance » à « est-ce que cela finira avant qu’il n’arrive quelque chose ? »

Une citation qui tient souvent bien en exploitation est de John Allspaw : « La fiabilité est la fonctionnalité. » C’est tout le jeu une fois que votre système est en production.

10) Alimentation, thermique et comportement de boost : le deuxième GPU peut ralentir les deux

C’est un classique du type « pourquoi ma mise à niveau est-elle plus lente ? ». Deux GPU consomment plus et rejettent plus de chaleur dans la même boîte. Cela peut provoquer :

  • Des fréquences de boost plus basses à cause des limites de puissance.
  • Du throttling thermique parce que le flux d’air est maintenant bloqué par une seconde brique chaude.
  • Des courbes de ventilation qui montent, des plaintes de bruit, et finalement « quelqu’un a changé le BIOS ».

Si le châssis n’est pas conçu pour des charges soutenues dual-GPU, vous pouvez vous retrouver avec deux GPU tournant plus lentement qu’un seul GPU dans un environnement thermique sain.

Blague n°1 : Ajouter un deuxième GPU pour la vitesse, c’est comme ajouter un deuxième volant pour le contrôle. Ça augmente surtout le nombre d’opinions dans la voiture.

11) Inférence multi-GPU : le débit peut augmenter, la latence empire souvent

Pour la mise en production, vous vous souciez du p95 et p99, pas seulement des tokens/sec sur un mardi calme. Répartir l’inférence sur plusieurs GPU (parallélisme tensoriel) peut améliorer le débit pour de très grands modèles,
mais ajoute de la communication inter-GPU sur le chemin critique de chaque requête.

Cela signifie :

  • Plus d’opportunités pour la latence de queue aux extrêmes à cause de synchronisation et mise en file d’attente.
  • Plus de sensibilité au jitter (ordonnancement OS, interruptions, voisins bruyants).
  • Une logique d’agrégation plus complexe, qui peut se retourner contre vous si le trafic est en rafales.

Deux GPU peuvent être excellents pour le débit agrégé en inférence par lots importants. Ils peuvent être catastrophiques pour des objectifs de latence interactive.

12) Le débogage se complique, et votre réponse aux incidents le ressentira

Quand un job mono-GPU ralentit, vous pouvez généralement trouver rapidement le coupable : limité par la mémoire, par le calcul, ou assoiffé d’I/O. Avec deux GPU, vous ajoutez de nouvelles catégories :

  • Distribution de charge déséquilibrée (un GPU attend l’autre).
  • Inefficacité des communications collectives (le « mur all-reduce »).
  • Mismatch de topologie (P2P désactivé, mauvais nœud NUMA, mauvais slot PCIe).

La vérité désagréable : le debug de performance multi-GPU n’est pas « 2× plus dur ». C’est « plus de dimensions que vous ne pouvez garder en tête à 2 h du matin. »

Faits et historique qui expliquent le mess

  • SLI et CrossFire étaient conçus pour le graphisme, utilisant souvent le rendu par images alternées ; ils optimisaient le FPS moyen, pas le pacing des images.
  • Le microstutter est devenu un problème connu du multi-GPU parce que les premiers pipelines multi-GPU produisaient des temps de trame inégaux même quand le FPS moyen semblait correct.
  • Le multi-GPU CUDA s’appuyait initialement beaucoup sur PCIe ; l’industrie a vite appris que les copies médiées par l’hôte sont une impasse pour de nombreuses charges.
  • NVIDIA a introduit NVLink pour résoudre les limites de bande passante et de latence inter-GPU que PCIe ne pouvait pas corriger pour les grands systèmes multi-GPU.
  • NCCL est devenu la norme pour les collectives parce que l’entraînement distribué a besoin d’un all-reduce performant ; les implémentations naïves écrasaient la mise à l’échelle.
  • « Pooler la mémoire GPU » n’est pas le modèle par défaut ; la plupart des modèles traitent les GPU comme des espaces d’adresses séparés sauf si vous gérez explicitement le partage.
  • Les générations PCIe comptent : PCIe 3.0 vs 4.0 vs 5.0 peut changer si la communication est une nuisance ou le coût dominant.
  • Les serveurs dual-socket ont créé des pièges de topologie : attacher des GPU à travers des sockets peut introduire des pénalités de latence et de bande passante qui ressemblent à une « inefficacité du framework ».
  • Le deep learning moderne a rapidement heurté le « mur de communication » ; au-delà d’un certain point, ajouter des GPU donne des retours marginaux à moins de modifier le batch/le modèle/la stratégie de parallélisme.

Trois mini-récits d’entreprise (comment ça échoue en pratique)

Mini-récit 1 : L’incident causé par une fausse supposition

Une entreprise de taille moyenne a déployé un nouveau pipeline d’entraînement pour un modèle de recommandation. Ils l’avaient validé sur une station de travail à GPU unique : stable, prévisible et « suffisamment rapide ».
L’étape suivante était de « simplement ajouter un autre GPU » sur le serveur de staging pour réduire le temps d’entraînement de moitié avant une deadline.

L’hypothèse était propre et fausse : ils pensaient que le data-parallel était gratuit. Ils ont doublé les GPU, doublé la taille de batch et s’attendaient au même temps par étape. Ce qu’ils ont obtenu
fut un job d’entraînement qui a bien démarré puis ralenti dramatiquement après les premières minutes.

La panne n’était pas liée au calcul. C’était l’I/O. Leur dataset vivait sur un système de fichiers réseau avec beaucoup de petits fichiers, et le deuxième GPU a doublé la pression sur le loader.
Le serveur métadonnées a commencé à thrash. D’autres services partageant le même stockage ont ralenti aussi, parce que « l’entraînement » était devenu un déni de service distribué avec des logs polis.

L’incident a escaladé parce que les symptômes étaient trompeurs : l’utilisation GPU flottait autour de 40–60 %, ce qui semblait « ok » pour ceux qui confondent utilisation et progrès.
Pendant ce temps, le temps par étape avait explosé. Ils ont essayé de « réparer les GPU » en réinstallant des pilotes et en ajustant les réglages NCCL, ce qui revenait à changer des pneus pour réparer une fuite de carburant.

La résolution fut ennuyeuse : consolider le dataset en shards plus gros, ajouter un cache NVMe local pour les jobs d’entraînement, et limiter le parallélisme des data-loaders. Le deuxième GPU
n’a pas causé le problème ; il l’a exposé avec un mégaphone.

Mini-récit 2 : L’optimisation qui s’est retournée contre eux

Une autre équipe servait un modèle de type LLM pour des outils internes. La latence comptait : les ingénieurs l’utilisaient de façon interactive, et tout au-delà de quelques secondes paraissait « cassé ».
Ils avaient un gros GPU, coûteux mais stable.

Quelqu’un a suggéré de diviser le modèle sur deux GPU plus petits en utilisant le parallélisme tensoriel. Le tableau ressemblait bien : plus de VRAM agrégée, moins cher par boîtier, et « ça devrait scaler ».
Ils l’ont construit, déployé, et ont vu la latence p99 empirer même si le débit s’améliorait dans des benchmarks synthétiques.

La cause racine était sur le chemin critique : chaque étape de génération de token nécessitait une communication cross-GPU. Sous trafic réel, la taille des requêtes variait, et l’agrégation n’était pas parfaite.
Ils ont donc obtenu mise en file + synchronisation. Le système passait plus de temps à coordonner qu’à calculer. Pire, la latence en queue a explosé quand un GPU chauffait un peu et downclockait,
parce que l’autre GPU devait attendre aux points de synchronisation.

Ils ont essayé une parade de réglages : taille de batch, priorités de stream, affinage des threads, tours de boutons dans le serveur d’inférence. Ça a amélioré un peu, mais l’architecture fondamentale
restait hostile à la latence.

La correction fut contre-intuitive mais évidente après coup : revenir à un GPU plus grand pour les endpoints sensibles à la latence, et garder la configuration dual-GPU uniquement pour les jobs batch hors-ligne
où le débit compte et les queues sont négligeables.

Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Une équipe plateforme gérait un cluster GPU partagé pour plusieurs groupes produits. Ils en avaient assez des ralentissements mystères et des tickets « c’était rapide hier ». Plutôt que de chasser chaque bizarrerie de workload,
ils ont adopté une pratique simple et ennuyeuse : standardiser le placement GPU et documenter la topologie.

Pour chaque modèle de serveur, ils ont produit une carte simple : quels slots PCIe correspondent à quel nœud NUMA, quelles paires de GPU ont des chemins P2P rapides, et quels réglages BIOS sont requis
pour un comportement stable. Ils ont intégré ces réglages dans le provisioning. Ils ont aussi ajouté un test pré-vol qui fait échouer le nœud si le P2P est désactivé de façon inattendue.

Des mois plus tard, un fournisseur a expédié une révision de carte mère de remplacement. Tout a démarré. Aucun signal d’alarme. Mais les jobs multi-GPU ont commencé à mal scaler sur un sous-ensemble de nœuds.
Parce que l’équipe avait des contrôles de topologie et des tests de bande passante de référence dans le CI pour la flotte, ils l’ont détecté rapidement et mis en quarantaine avant que les clients ne remarquent.

Le postmortem fut court. La réponse : « le routage matériel a changé ; le chemin P2P diffère ; ajuster le placement et mettre à jour la référence. » Personne n’en a raffolé. Personne n’a perdu une semaine.
Voilà à quoi ressemble le « ennuyeux » quand ça marche.

Mode d’intervention rapide

Quand deux GPU sous-performent, ne commencez pas par réécrire votre modèle. Commencez par prouver où le temps part. L’objectif n’est pas d’être ingénieux ; c’est d’être rapide.

Première étape : confirmer que le problème est réel (et mesurer l’horloge murale)

  • Comparez le temps par étape / tokens par seconde / requêtes par seconde, pas « utilisation GPU ».
  • Mesurez la latence p50/p95/p99 pour l’inférence, pas seulement la moyenne.
  • Testez mono-GPU vs deux GPU avec les mêmes versions logicielles et le même slice du dataset.

Deuxième étape : identifier la classe de goulot (calcul, mémoire, comms, CPU, I/O, thermique)

  • Si les GPU sont occupés mais que la mise à l’échelle est mauvaise : suspectez la communication et la synchronisation.
  • Si les GPU sont inactifs : suspectez la pipeline d’entrée, le CPU, ou le stockage.
  • Si la performance se dégrade avec le temps : suspectez le thermique, les limites de puissance, ou la fragmentation/GC mémoire.

Troisième étape : valider la topologie et les hypothèses d’interconnexion

  • Vérifiez la vitesse/largeur du lien PCIe et si le P2P est activé.
  • Vérifiez la localité NUMA : les threads CPU alimentant GPU0 doivent généralement être sur le même socket que GPU0.
  • Confirmez que NVLink est réellement présent et actif (le cas échéant).

Quatrième étape : vérifiez les trucs système ennuyeux

  • Clocks et throttling (température, caps de puissance).
  • Erreurs pilote / Xid et réinitialisations.
  • Saturation disque et réseau pendant les checkpoints et lectures de dataset.

Cinquième étape : ce n’est qu’ensuite que vous réglez le framework

  • Paramètres NCCL, tailles de buckets DDP, accumulation de gradients, précision mixte, options de compilation.
  • Tailles de batch qui gardent les GPU efficaces sans faire exploser les communications.
  • Chevauchement communication/calcul lorsque supporté.

Blague n°2 : La mise à l’échelle multi-GPU, c’est quand vous payez deux moteurs et passez votre temps à déboguer la boîte de vitesses.

Tâches pratiques : commandes, sorties et décisions (12+)

Ce ne sont pas des « astuces ». Ce sont les vérifications que vous lancez quand vous êtes en on-call et que quelqu’un jure que le deuxième GPU « ne sert à rien ».
Chaque tâche inclut : la commande, ce que signifie la sortie, et la décision à prendre ensuite.

Task 1: Confirm both GPUs exist and the driver sees them

cr0x@server:~$ nvidia-smi -L
GPU 0: NVIDIA A10 (UUID: GPU-aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee)
GPU 1: NVIDIA A10 (UUID: GPU-ffffffff-1111-2222-3333-444444444444)

Signification : Si un GPU manque, tout le reste est du bruit. Les périphériques manquants peuvent venir de l’alimentation, du placement, du BIOS ou du pilote.
Décision : Si les deux ne sont pas listés, arrêtez et réparez le matériel/firmware/pilote avant de débroussailler la mise à l’échelle logicielle.

Task 2: Check PCIe link speed and width (the “why is my bus slow?” check)

cr0x@server:~$ nvidia-smi -q -d PCI | sed -n '1,120p'
GPU 00000000:65:00.0
    PCIe Generation
        Current                     : Gen4
        Max                         : Gen4
    Link Width
        Current                     : x8
        Max                         : x16
GPU 00000000:B3:00.0
    PCIe Generation
        Current                     : Gen4
        Max                         : Gen4
    Link Width
        Current                     : x16
        Max                         : x16

Signification : GPU0 fonctionne en x8 alors qu’il supporte x16. Cela peut réduire la bande passante host/device et affecter aussi les chemins P2P.
Décision : Reseat le GPU, vérifiez le câblage des slots, les réglages BIOS, les risers, et si un autre périphérique a pris des lanes.

Task 3: Map GPUs to NUMA nodes (so you stop paying cross-socket tax)

cr0x@server:~$ nvidia-smi topo -m
        GPU0    GPU1    CPU Affinity    NUMA Affinity
GPU0     X      SYS     0-31            0
GPU1    SYS      X      32-63           1

Signification : GPU0 est local au nœud NUMA 0 ; GPU1 est local au nœud NUMA 1 ; le chemin GPU-to-GPU est SYS (passe par l’interconnexion système/CPU).
Décision : Si votre workload synchronise fortement entre GPU, attendez-vous à une pire mise à l’échelle. Envisagez de garder les rangs multi-GPU sur le même socket quand possible,
ou choisissez un serveur avec une meilleure interconnexion GPU.

Task 4: Confirm P2P access is enabled (and not silently disabled)

cr0x@server:~$ nvidia-smi topo -p2p n
        GPU0    GPU1
GPU0     X      OK
GPU1    OK      X

Signification : Le P2P est permis entre les GPU. Si vous voyez « NS » ou « Disabled », vos communications peuvent rebondir via la mémoire hôte.
Décision : Si P2P n’est pas OK, vérifiez les réglages IOMMU, le mode de virtualisation, les toggles BIOS, et les combinaisons pilote/kernel.

Task 5: Check clocks, power, and thermals (performance that melts)

cr0x@server:~$ nvidia-smi --query-gpu=index,temperature.gpu,power.draw,power.limit,clocks.sm,clocks.mem,clocks.current.sm --format=csv
index, temperature.gpu, power.draw, power.limit, clocks.sm, clocks.mem, clocks.current.sm
0, 84, 147.23 W, 150.00 W, 1710 MHz, 5001 MHz, 1410 MHz
1, 72, 132.10 W, 150.00 W, 1710 MHz, 5001 MHz, 1680 MHz

Signification : GPU0 est proche des limites thermiques et ne tient pas les fréquences SM cibles (current plus bas que nominal). Cela seul peut casser l’efficacité de la synchronisation multi-GPU.
Décision : Améliorez le flux d’air, ajustez les courbes de ventilateur, réduisez la limite de puissance prudemment, ou replacez les GPU pour éviter qu’un seul n’étouffe l’autre.

Task 6: Look for ECC or memory errors (the “it’s slow because it’s sick” check)

cr0x@server:~$ nvidia-smi -q -d ECC | sed -n '1,120p'
ECC Mode
    Current ECC                     : Enabled
    Pending ECC                     : Enabled
ECC Errors
    Volatile
        Single Bit
            Device Memory           : 0
        Double Bit
            Device Memory           : 0
    Aggregate
        Single Bit
            Device Memory           : 2
        Double Bit
            Device Memory           : 0

Signification : Des erreurs single-bit agrégées existent. Cela ne vous fera peut-être pas planter tout de suite, mais cela peut corréler avec du matériel marginal.
Décision : Si les erreurs augmentent, planifiez une maintenance et envisagez de retirer le GPU des workloads sensibles à la latence ou des entraînements longs.

Task 7: Check for Xid errors in kernel logs (driver/GPU faults)

cr0x@server:~$ sudo dmesg -T | grep -i -E 'NVRM|Xid' | tail -n 20
[Mon Jan 13 09:41:02 2026] NVRM: Xid (PCI:0000:65:00): 31, Ch 0000000b, engmask 00000101, intr 10000000
[Mon Jan 13 09:41:02 2026] NVRM: GPU 0000:65:00.0: GPU has fallen off the bus.

Signification : « Fallen off the bus » n’est pas un problème de tuning. C’est de la stabilité : alimentation, intégrité PCIe, surchauffe ou matériel défaillant.
Décision : Arrêtez de chasser la performance. Stabilisez le nœud : reseat, swap des câbles, valider la tête PSU, mises à jour firmware, et envisager un RMA.

Task 8: Confirm CPU and memory pressure (data loaders and launch overhead)

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

09:52:11 AM  CPU   %usr  %nice  %sys  %iowait  %irq  %soft  %idle
09:52:12 AM  all  68.20   0.00  14.10    0.40  0.00   1.20  16.10
09:52:12 AM   0  98.00   0.00   2.00    0.00  0.00   0.00   0.00
09:52:12 AM  32  96.00   0.00   4.00    0.00  0.00   0.00   0.00

Signification : Certains CPUs sont épuisés et saturés (probablement des workers data-loader ou threads du framework). La famine GPU commence souvent ici.
Décision : Améliorez l’efficacité des loaders, utilisez la mémoire épinglée judicieusement, ajustez le nombre de workers, et épinglez les processus sur le nœud NUMA approprié.

Task 9: Check disk throughput and iowait during training

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

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          42.11    0.00   9.33   18.70    0.00   29.86

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   w_await aqu-sz  %util
nvme0n1         320.0  86500.0     10.0   3.03    5.10   270.3     55.0  22000.0    7.80   2.10  92.0

Signification : Un iowait élevé et le NVMe à ~92 % d’utilisation suggèrent que le stockage est le limiteur. Deux GPU ont juste rendu la file d’attente plus profonde.
Décision : Déplacez le dataset localement, préchargez, sharde, mettez en cache, ou augmentez la bande passante de stockage avant d’ajouter plus de GPU.

Task 10: Check network throughput if dataset or checkpoints are remote

cr0x@server:~$ sar -n DEV 1 3
Linux 6.5.0 (server)  01/13/2026  _x86_64_  (64 CPU)

09:55:21 AM     IFACE   rxpck/s   txpck/s     rxkB/s     txkB/s   rxcmp/s   txcmp/s  rxmcst/s
09:55:22 AM      eth0   6200.00   5800.00   930000.00  210000.00      0.00      0.00     12.00

Signification : RX proche du line-rate sur un lien 10Gb/25Gb peut être votre plafond. Si votre job lit des données à distance, c’est une limite dure.
Décision : Mettez en cache localement, compressez et shardez plus intelligemment, ou améliorez le réseau/chemin stockage avant d’attendre des gains multi-GPU.

Task 11: Verify process-to-GPU mapping (you might be using one GPU twice)

cr0x@server:~$ nvidia-smi pmon -c 1
# gpu        pid  type    sm   mem   enc   dec   command
# Idx          #   C/G     %     %     %     %   name
    0      24819    C     72    61     0     0   python
    0      24820    C     69    58     0     0   python
    1          -    -      -     -     -     -   -

Signification : Deux processus sont sur GPU0 ; GPU1 est inutilisé. C’est courant quand CUDA_VISIBLE_DEVICES est mal réglé ou le lanceur est incorrect.
Décision : Corrigez votre commande de lancement, l’environnement, ou l’assignation du scheduler. Ne touchez pas à NCCL avant d’utiliser réellement le deuxième GPU.

Task 12: Check NCCL’s view of topology (when comms are the issue)

cr0x@server:~$ NCCL_DEBUG=INFO NCCL_TOPO_DUMP_FILE=/tmp/nccl-topo.xml python -c "import torch; import torch.distributed as dist; print('ok')"
NCCL INFO NET/Plugin: No plugin found (libnccl-net.so), using internal implementation
NCCL INFO CUDA Dev 0 [0] PCIe/Gen4 x8
NCCL INFO CUDA Dev 1 [1] PCIe/Gen4 x16
NCCL INFO Topo detection done
ok

Signification : NCCL rapporte les largeurs de lien et a écrit un dump de topologie que vous pouvez inspecter hors ligne. Le x8 est une preuve accablante.
Décision : Corrigez d’abord les problèmes PCIe ; puis relancez les tests de mise à l’échelle. Aucun réglage de bucket ne compense des lanes manquantes.

Task 13: Check GPU memory usage and fragmentation pressure

cr0x@server:~$ nvidia-smi --query-gpu=index,memory.total,memory.used,memory.free --format=csv
index, memory.total, memory.used, memory.free
0, 24564 MiB, 23890 MiB, 674 MiB
1, 24564 MiB, 1200 MiB, 23364 MiB

Signification : GPU0 est proche d’un OOM tandis que GPU1 est presque vide. Cela pointe vers un déséquilibre : mauvais placement des devices, modèle non shardé, ou mismatch de rang.
Décision : Corrigez l’assignation des devices ; vérifiez que chaque rang utilise le GPU prévu ; envisagez une stratégie de sharding si la capacité modèle est l’objectif.

Task 14: Confirm NUMA locality for the process (stop remote memory traffic)

cr0x@server:~$ numactl --show
policy: default
preferred node: current
physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
membind: 0

Signification : Le processus est lié aux CPUs 0–15 et au nœud mémoire 0. Si ce rang pilote GPU1 sur le nœud NUMA 1, vous payez des coûts d’accès mémoire distant.
Décision : Épinglez chaque rang sur le socket local à son GPU (ou au moins évitez la mémoire cross-socket).

Erreurs courantes : symptômes → cause racine → correction

1) Symptom: GPU utilization is “high” but wall-clock is worse

  • Cause racine : Overhead de synchronisation (all-reduce) domine ; l’utilisation inclut le temps passé bloqué dans les comms ou en attente aux barrières.
  • Correction : Augmentez le batch par GPU (ou utilisez l’accumulation de gradients), ajustez les tailles de bucket, chevauchez comms et calcul, et validez P2P/NVLink/topologie.

2) Symptom: One GPU is busy, the other is mostly idle

  • Cause racine : Assignation de device incorrecte (CUDA_VISIBLE_DEVICES, lanceur, scheduler), ou défaillance de rang laissant un process solo.
  • Correction : Validez le mapping avec nvidia-smi pmon ; imposez le mapping rang→device ; échouez vite quand un rang sort.

3) Symptom: Scaling is decent at first, then degrades over time

  • Cause racine : Throttling thermique ou comportement de limite de puissance ; parfois fragmentation mémoire et comportement de l’allocateur.
  • Correction : Surveillez temps/fréquences ; améliorez le flux d’air ; standardisez caps de puissance et réglages BIOS/fan ; réduisez les pics si nécessaire.

4) Symptom: Training becomes unstable—hangs, deadlocks, “NCCL timeout”

  • Cause racine : Un rang a une exception/OOM et sort ; les autres attendent dans les collectives pour toujours. Parfois jitter réseau ou échecs P2P contribuent.
  • Correction : Activez une gestion d’erreur robuste et des timeouts ; assurez-vous que tous les rangs abortent ensemble ; capturez les logs par rang ; simplifiez la topologie.

5) Symptom: You added a GPU to fit a bigger model, and it still OOMs

  • Cause racine : Le data-parallel ne pool pas la VRAM ; le modèle complet doit toujours tenir par GPU.
  • Correction : Utilisez le sharding du modèle (tensor/pipeline/expert parallel), checkpointing d’activations, quantification, ou achetez un GPU unique plus grand.

6) Symptom: Inference throughput improves but p95/p99 latency gets worse

  • Cause racine : Communication cross-GPU sur le chemin critique ; l’agrégation/la mise en file amplifient le jitter ; les points de synchronisation créent des queues.
  • Correction : Gardez les endpoints sensibles à la latence sur un GPU unique ; réservez le multi-GPU au batch/offline ; ou repensez l’agrégation et le routage des requêtes.

7) Symptom: Same model, same code, but performance varies across identical-looking servers

  • Cause racine : Différences de topologie PCIe, paramètres BIOS par défaut, négociation de lanes (x8 vs x16), ou P2P désactivé sur certains nœuds.
  • Correction : Établissez une baseline et imposez des contrôles de topologie dans le provisioning ; quarantenez les nœuds non conformes ; standardisez le firmware.

8) Symptom: Filesystem and network “randomly” slow down when training runs

  • Cause racine : Fanout des data-loaders et pics de checkpoints ; pression sur les métadonnées due aux petits fichiers ; contention sur le stockage partagé.
  • Correction : Shardez et mettez en cache les datasets ; réduisez les petits fichiers ; échelonnez les checkpoints ; limitez les workers ; déplacez l’I/O lourd hors des chemins partagés.

Checklists / plan étape par étape

Checklist décisionnelle : devez-vous acheter un deuxième GPU ou un GPU unique plus grand ?

  1. Si vous avez besoin de plus de VRAM pour un seul modèle, privilégiez un GPU plus grand à moins d’être prêt à implémenter et opérer du parallélisme modèle.
  2. Si vous avez besoin de plus de débit pour de nombreux jobs indépendants, deux GPU fonctionnent souvent bien—lancez deux processus séparés, évitez la synchronisation.
  3. Si vous avez besoin d’une faible latence, préférez un GPU par chemin de requête. La latence multi-GPU est un problème de coordination.
  4. Si votre plateforme est limitée par l’I/O, n’ajoutez pas de GPU. Réparez le stockage/réseau d’abord ou vous créerez de l’inactivité coûteuse.
  5. Si vos serveurs sont dual-socket, validez la topologie avant l’achat ; « deux GPU » peut signifier « chemin SYS pour toujours ».

Étape par étape : rendre deux GPU acceptables pour l’entraînement

  1. Baseline mono-GPU : mesurez le temps par étape, le débit et les clocks GPU en état stable.
  2. Validez les chemins matériels : PCIe x16 là où attendu ; P2P OK ; affinité NUMA connue.
  3. Corrigez la pipeline d’entrée : cache local, datasets shardés, nombres de workers raisonnables, évitez les fichiers minuscules.
  4. Scalez le batch de façon responsable : augmentez le batch global seulement si cela n’affecte pas la convergence ; sinon utilisez l’accumulation de gradients.
  5. Réduisez l’overhead de communication : fusionnez les petits tenseurs quand possible ; ajustez les buckets DDP ; chevauchez comms et calcul.
  6. Stabilisez le thermique : caps de puissance et flux d’air cohérents ; vérifiez que les clocks ne s’effondrent pas après 10 minutes.
  7. Opérationnalisez : timeouts, collecte de logs par rang, checks de santé pour P2P et largeur de lien PCIe.

Étape par étape : utiliser efficacement deux GPU (le pattern « jobs séparés »)

  1. Exécutez des processus indépendants épinglés à un GPU chacun (pas de collectives).
  2. Épinglez les cœurs CPU et la mémoire de chaque processus sur le nœud NUMA du GPU.
  3. Limitez le débit d’écriture des checkpoints pour éviter que les deux jobs ne provoquent des pics I/O simultanés.
  4. Surveillez le débit par job et les clocks par device ; imposez une marge thermique.

FAQ

1) Est-ce que deux GPU sont parfois meilleurs qu’un seul ?

Oui—souvent. C’est mieux quand vous pouvez exécuter deux workloads indépendants, ou quand votre stratégie de parallélisme modèle est mature et que votre interconnexion/topologie la supporte.
C’est pire quand vous êtes forcé dans une synchronisation fréquente ou quand votre pipeline est limité par l’I/O ou le CPU.

2) Pourquoi la VRAM ne s’additionne-t-elle pas automatiquement entre GPU ?

Parce que chaque GPU est un domaine mémoire séparé. La plupart des modes d’entraînement par défaut répliquent les paramètres sur chaque device. Pooler la mémoire demande du sharding explicite et
de la communication, ce qui change le modèle d’exécution et les modes de défaillance.

3) Si j’ai NVLink, ai-je encore des problèmes multi-GPU ?

NVLink aide la bande passante et la latence entre GPU, mais il ne résout pas les goulots CPU, stockage, mauvais sizing de batch, throttling thermique, ou chemins logiciels fragiles.
Il réduit une taxe ; il n’élimine pas toutes les taxes.

4) Pourquoi mon deuxième GPU est sous-utilisé dans PyTorch ?

Causes courantes : lanceur incorrect (ne spawn pas les rangs), CUDA_VISIBLE_DEVICES mal réglé, affinité de process épinglant tout sur un GPU, ou crash d’un rang.
Vérifiez avec nvidia-smi pmon et assurez-vous que chaque rang sélectionne un device unique.

5) Deux GPU peuvent-ils rendre le jeu plus fluide ?

Historiquement, le multi-GPU pouvait augmenter le FPS moyen mais aggraver le pacing (microstutter). Le support moderne est limité et souvent pas rentable.
Un GPU plus puissant reste généralement la bonne réponse pour des temps de trame constants.

6) Pourquoi l’inférence multi-GPU nuit-elle autant à la latence ?

Parce que la communication inter-GPU devient partie du chemin critique de chaque requête. Tout jitter—mise en file, dérive thermique, ordonnancement OS—se transforme en latence tail.
Le débit peut s’améliorer tandis que le p99 empire. Les utilisateurs en production remarquent le p99.

7) Quel est le goulot caché le plus courant quand on ajoute des GPU ?

La pipeline d’entrée : stockage et CPU. Deux GPU peuvent tirer des données plus vite que votre filesystem, object store ou prétraitement ne peuvent fournir. Les GPU « attendent efficacement ».

8) Comment savoir si je suis limité par le PCIe ?

Les symptômes incluent une mauvaise mise à l’échelle malgré une capacité compute élevée, des transferts host-device fréquents, et une topologie montrant des chemins SYS ou une largeur de lien réduite.
Vérifiez nvidia-smi -q -d PCI pour Gen et width, et validez le statut P2P avec nvidia-smi topo.

9) Dois-je activer tous les réglages NCCL que je trouve ?

Non. Validez d’abord la topologie, P2P, la largeur de lien PCIe et le placement NUMA. Ensuite, tunez. Si la plomberie physique est mauvaise, les réglages NCCL ne sont que différentes façons d’être lent.

10) Si j’ai déjà acheté deux GPU, quelle est la meilleure façon de les utiliser ?

Si vous n’êtes pas prêt pour le parallélisme modèle, le pattern à plus fort ROI est souvent d’exécuter deux jobs indépendants—un par GPU—plutôt que de forcer un entraînement synchronisé ou une inférence répartie.

Étapes suivantes concrètes

Si votre configuration deux-GPU sous-performe, traitez-la comme un incident SRE : identifiez la ressource limitante, validez les hypothèses, et ne changez l’architecture qu’après.
Voici la séquence pratique qui fonctionne plus souvent qu’elle n’en a le droit :

  1. Mesurez ce qui compte : temps par étape, débit et latence tail—choisissez un objectif et optimisez-le.
  2. Prouvez la topologie : largeur/vitesse PCIe, P2P activé, affinité NUMA correcte.
  3. Stabilisez les clocks : limites de puissance, thermique, et signes de throttling ou erreurs Xid.
  4. Réparez l’alimentation : stockage, réseau, prétraitement CPU, sharding, caching, tempêtes de checkpoints.
  5. Choisissez la bonne stratégie multi-GPU : jobs indépendants, data-parallel, ou vrai model-parallel—ne prétendez pas qu’ils sont interchangeables.

Si vous achetez du matériel : pour la plupart des équipes, le meilleur rapport performance / heures ingénierie vient d’un GPU unique plus grand plutôt que de deux plus petits—à moins que votre workload ne se sépare naturellement
en tâches indépendantes. Le tableau Excel inclut rarement « déboguer un hang distribué à 3 h du matin ». Le planning, si.

← Précédent
MySQL vs PostgreSQL pour le SaaS multi-tenant : isolation des locataires qui survit à la croissance
Suivant →
Permissions des bind mounts Docker sur Windows : la configuration la moins contraignante

Laisser un commentaire