Certains problèmes de performance donnent l’impression d’être personnels. Vous avez acheté un Ryzen ou un EPYC avec beaucoup de cœurs, vous l’avez alimenté en NVMe rapides, vous lui avez donné assez de RAM pour faire pâlir le cluster d’hier — et il se comporte toujours comme s’il montait un piano dans un escalier. Les graphes montrent le CPU « disponible », les disques « ok », le réseau « ok », pourtant la charge reste obstinément lente et irrégulière.
C’est généralement à ce moment qu’Infinity Fabric s’invite dans la conversation. Pas comme un composant que vous pouvez pointer avec un tournevis, mais comme l’interconnexion qui décide si vos chiplets coopèrent comme une équipe bien coordonnée — ou se disputent comme une revue de changement à 16h55.
Ce qu’est vraiment Infinity Fabric (et ce que ce n’est pas)
Infinity Fabric est l’architecture d’interconnexion évolutive d’AMD — la tuyauterie qui déplace les données et le trafic de cohérence entre les cœurs CPU, les caches, les contrôleurs mémoire et l’I/O. Sur les Ryzen et EPYC modernes, c’est la colle qui fait que l’approche par chiplets se comporte comme un seul CPU logique (la plupart du temps).
Si vous venez de l’ère des dies monolithiques, pensez-y ainsi : le « CPU » est désormais un petit campus. Vous avez plusieurs bâtiments (core chiplets), un bâtiment de services central (I/O die ou contrôleurs mémoire selon la génération), et un réseau entre eux. Infinity Fabric est ce réseau interne — commutateurs, liens, protocoles, arbitrage et timing. Quand il fonctionne bien, vous ne le remarquez pas. Quand ce n’est pas le cas, vos latences grimpent et votre débit devient étrangement inégal.
Ce que ce n’est pas : une horloge unique, un bus unique, ou un bouton magique de performance que vous mettez sur « rapide ». C’est un système en couches avec différents domaines : cœur-à-cœur, cœur-à-mémoire, cœur-à-I/O, et trafic de cohérence. La partie la plus pertinente pour les opérations est que certains chemins de latence le traversent, et sa vitesse effective dépend souvent de la façon dont vous configurez la mémoire, les horloges, la politique NUMA et les réglages d’alimentation du BIOS.
Pourquoi les équipes ops et performance devraient s’en préoccuper
Parce que la plupart des charges de production ne sont pas « CPU-bound » comme les tableaux de bench le suggèrent. Elles sont liées à la latence. Elles sont sensibles aux défauts de cache. Elles sont bavardes entre threads. Elles sont sensibles au NUMA. Ce sont des microservices qui se compliquent mutuellement la vie. Et Infinity Fabric fait partie de l’histoire de la latence chaque fois que des données doivent circuler entre chiplets, régions mémoire ou chemins I/O.
C’est là que les équipes se font piéger : l’utilisation moyenne du CPU semble correcte, mais la latence en pointe devient horrible. Ou un nœud de base de données fonctionne très bien sur une socket et étrangement moins bien sur un autre nœud « identique ». Ou un serveur de stockage tout-NVMe obtient moins de débit que ce que la fiche technique indique. Le fabric n’a pas de graphe dans la plupart des tableaux de bord, mais il a absolument un mot à dire.
Blague n°1 (courte et douloureusement exacte) : Infinity Fabric, c’est comme le Wi‑Fi au bureau — personne ne le budgète, tout le monde le blâme, et d’une façon ou d’une autre, il est toujours impliqué.
Faits et historique qui comptent vraiment en 2026
Voici des éléments concrets de contexte qui changent votre façon de dépanner :
- Infinity Fabric est arrivé comme interconnexion unificatrice à l’ère Zen pour scaler les cœurs et l’I/O sans dies monolithiques. Ce changement d’architecture explique pourquoi une « même famille de CPU » peut se comporter très différemment selon les générations et SKU.
- Les chiplets ont fait de la latence d’interconnexion un facteur de performance de premier plan. Dans les dies monolithiques, la latence inter-cœurs était principalement « on-die ». Avec les chiplets, certains trafics doivent maintenant sauter via le fabric, et votre charge tolère cela… ou pas.
- Sur de nombreuses générations de Ryzen, l’horloge du fabric (FCLK) et l’horloge mémoire (MCLK) étaient couplées pour une latence optimale. Découpler permet des fréquences mémoire plus élevées mais ajoute de la latence qui pénalise les charges sensibles aux extrêmes.
- EPYC a monté l’échelle en ajoutant des CCDs et un gros I/O die. Cela améliore le rendement et le nombre de cœurs, mais crée aussi une topologie : certains cœurs sont « plus proches » de certaines mémoires et I/O que d’autres.
- NUMA n’est pas optionnel sur les systèmes EPYC multi-CCD. Vous pouvez faire comme si c’était UMA, mais le matériel ne jouera pas le jeu. Le scheduler Linux va essayer, mais il ne peut pas lire vos intentions — ni la localité de votre cache.
- Les valeurs par défaut du BIOS optimisent souvent la puissance/thermique, pas la latence déterministe. Si vous exécutez des bases de données, des systèmes de trading ou des cibles de stockage, « économie d’énergie » peut silencieusement signifier « latence variable ».
- La virtualisation amplifie les erreurs de topologie. Une VM qui s’étend sur des nœuds NUMA peut transformer un CPU correct en générateur de mémoire distante avec une activité secondaire de paquets perdus.
- Les révisions de firmware et d’AGESA ont historiquement modifié le comportement mémoire/fabric — parfois en améliorant la stabilité, parfois en changeant les caractéristiques de performance. Le « même matériel » avant et après une mise à jour firmware n’est pas toujours identique.
Un modèle mental : budgets de latence et motifs de trafic
Quand on parle de « goulot Infinity Fabric », on entend souvent une des trois choses :
- Latence ajoutée : les requêtes traversent des frontières de chiplet ou de domaines NUMA plus souvent que prévu, allongeant le chemin critique pour les défauts de cache, les verrous et le code à fort IPC.
- Bande passante limitée : beaucoup de cœurs génèrent suffisamment de trafic mémoire pour saturer l’interconnexion et les contrôleurs mémoire, provoquant contention et files d’attente.
- Jitter : états d’alimentation, changements d’horloge ou contention créent des temps de service incohérents ; les moyennes semblent correctes, mais le p99 ressemble à une scène de crime.
En pratique, la plupart des incidents sont un mélange : une charge devient lourde en mémoire distante (latence) et subit de la contention d’interconnexion (bande passante) et voit des pointes dues aux migrations du scheduler (jitter). Votre travail est d’identifier ce qui domine et de choisir le correctif le moins risqué.
Pensez en termes de motifs de trafic :
- Applications multi-thread bavardes (verrous partagés, files partagées, pauses GC) souffrent quand les threads sautent entre CCDs/nœuds NUMA.
- Bases de données en mémoire tiennent à la latence mémoire et à l’accès prévisible. La mémoire distante transforme la « RAM rapide » en « RAM plus lente avec étapes supplémentaires ».
- Cibles de stockage tiennent à l’I/O et à la localité des interruptions. Une mauvaise affinité fait traiter vos interruptions NVMe sur des cœurs éloignés du root PCIe, ajoutant latence et pollution de cache.
- Hôtes de virtualisation tiennent à l’emplacement. Un hôte peut tourner parfaitement jusqu’à ce qu’une VM « bruyante » s’étale sur plusieurs nœuds et devienne un banc d’essai pour le fabric.
FCLK/UCLK/MCLK et la « taxe de synchronisation »
Sur de nombreuses plateformes AMD, vous rencontrerez trois horloges importantes pour le comportement mémoire et fabric :
- MCLK : horloge mémoire (liée au débit DDR).
- UCLK : horloge du contrôleur mémoire.
- FCLK : horloge du fabric.
Selon la génération et les options BIOS, elles peuvent tourner en 1:1 ou en ratios (souvent 1:2) lorsque vous poussez la fréquence mémoire. Le piège est que des débits DDR plus élevés donnent l’impression de « plus de bande passante », tandis que le changement de ratio ajoute de la latence qui pénalise précisément les charges dont vous vous préoccupez en production.
Voici la traduction opérationnelle :
- Si votre charge est sensible à la latence (bases de données, caches, services RPC), préférez généralement une configuration stable qui conserve une relation faible en latence entre fabric et mémoire, même si la bande passante maximale est légèrement inférieure.
- Si votre charge est liée au streaming/bande passante (certaines analyses, grands scans séquentiels), pousser la bande passante peut aider — jusqu’à ce que la contention survienne ailleurs.
La « taxe de synchronisation » apparaît comme une latence d’accès mémoire pire, pas nécessairement comme une bande passante mesurée plus faible. C’est pourquoi vous pouvez « optimiser » la vitesse DDR puis vous demander pourquoi le p99 s’est dégradé. Vous n’avez pas rendu la mémoire plus rapide. Vous avez rendu le système moins prévisible.
NUMA, chiplets, CCD/CCX : où vos cycles vont faire la navette
Sur EPYC en particulier, la topologie est le destin. Les cœurs sont groupés en CCDs (core chiplet dies), et ceux-ci se connectent à un I/O die qui héberge les contrôleurs mémoire et le PCIe. Chaque CCD possède son propre cache L3 ; l’accès cross-CCD est intrinsèquement plus coûteux que l’accès local.
NUMA expose cela sous la forme de plusieurs nœuds. Même sur une seule socket physique, vous pouvez avoir plusieurs nœuds NUMA selon les réglages du BIOS (modes NPS, mapping des CCD). Linux essaie ensuite d’ordonnancer les threads et d’allouer la mémoire pour réduire les accès distants. Le mot clé est « essaie ». Votre charge, votre pinning et vos politiques de cgroup peuvent annuler ces tentatives en quelques secondes.
Motifs courants qui piquent :
- Migration de threads : les threads sautent entre cœurs/nœuds NUMA à cause des décisions du scheduler, volant la localité du cache et augmentant les accès mémoire distants.
- Mémoire allouée sur le mauvais nœud : un processus démarre sur un nœud, y alloue de la mémoire, puis est planifié ailleurs. Maintenant chaque accès mémoire est un aller-retour via le fabric.
- Interruptions sur les mauvais cœurs : les interruptions NIC/NVMe traitées sur des cœurs distants entraînent une latence I/O plus élevée et des cycles CPU gaspillés.
PCIe et I/O : l’autre moitié de l’histoire
Infinity Fabric n’est pas seulement « CPU-vers-RAM ». Il fait aussi partie de la façon dont l’I/O est servi. Sur de nombreux systèmes, les périphériques PCIe sont rattachés à des racines PCIe spécifiques associées à certains nœuds NUMA (ou du moins à certains domaines de localité). Si le traitement des interruptions de stockage et le traitement I/O se produisent « loin » du chemin PCIe, vous payez en latence et en overhead CPU.
C’est là que l’ingénierie du stockage rencontre la topologie CPU. Votre RAID NVMe, ZFS, pile I/O en espace utilisateur SPDK ou OSD Ceph peut être techniquement « rapide », mais si ses threads les plus chauds sont planifiés sur des cœurs distants et que ses allocations mémoire atterrissent sur le mauvais nœud NUMA, vous construisez pratiquement un système de stockage bas-latence puis vous le faites passer par la route panoramique.
Tâches pratiques : 12+ commandes, ce qu’elles signifient, ce que vous décidez
Voici des tâches réelles que vous pouvez exécuter sur un hôte Linux. Elles n’afficheront pas magiquement « Infinity Fabric est triste », mais elles vous diront si la topologie, la localité mémoire, les horloges ou les interruptions vous sapent.
Task 1: Identify CPU model and basic topology
cr0x@server:~$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
CPU(s): 128
Thread(s) per core: 2
Core(s) per socket: 64
Socket(s): 1
NUMA node(s): 4
Model name: AMD EPYC 7xx3 64-Core Processor
NUMA node0 CPU(s): 0-31
NUMA node1 CPU(s): 32-63
NUMA node2 CPU(s): 64-95
NUMA node3 CPU(s): 96-127
Ce que cela signifie : Vous avez quatre nœuds NUMA sur une socket. C’est déjà votre panneau d’avertissement : la localité mémoire compte.
Décision : Si vous exécutez des charges sensibles à la latence, planifiez de pinner les processus/VM par nœud NUMA et d’aligner les allocations mémoire. Si vous pensiez UMA, arrêtez.
Task 2: Show detailed NUMA distances
cr0x@server:~$ numactl --hardware
available: 4 nodes (0-3)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
node 0 size: 128798 MB
node 0 free: 97211 MB
node 1 cpus: 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
node 1 size: 128829 MB
node 1 free: 99102 MB
node 2 cpus: 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
node 2 size: 128813 MB
node 2 free: 100118 MB
node 3 cpus: 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
node 3 size: 128820 MB
node 3 free: 99654 MB
node distances:
node 0 1 2 3
0: 10 16 16 16
1: 16 10 16 16
2: 16 16 10 16
3: 16 16 16 10
Ce que cela signifie : La distance locale est 10, la distance distante est 16. C’est un delta de latence significatif pour la mémoire chaude.
Décision : Pour les bases de données et les cibles de stockage, traitez chaque nœud NUMA comme un « mini-socket ». Gardez les threads chauds et leur mémoire sur le même nœud autant que possible.
Task 3: Visualize topology with hwloc
cr0x@server:~$ lstopo-no-graphics --no-io
Machine (512GB total)
NUMANode L#0 (P#0 128GB)
Package L#0
L3 L#0 (32MB)
L2 L#0 (512KB) + L1d L#0 (32KB) + L1i L#0 (32KB) + Core L#0 + PU L#0 (P#0)
...
NUMANode L#1 (P#1 128GB)
Package L#0
L3 L#1 (32MB)
...
Ce que cela signifie : Vous pouvez voir comment les cœurs mappent aux tranches L3 et aux nœuds NUMA. C’est votre plan de placement.
Décision : Utilisez cela pour choisir les ensembles de CPU pour les services et pour interpréter plus tard « pourquoi ce thread a migré ? ».
Task 4: Check kernel NUMA balancing status
cr0x@server:~$ cat /proc/sys/kernel/numa_balancing
1
Ce que cela signifie : L’équilibrage NUMA automatique est activé. Cela peut aider les charges générales, et cela peut aussi créer des migrations de pages (jitter) pour les services sensibles à la latence.
Décision : Pour des charges sensibles à la latence et pinnings, envisagez de désactiver cela au niveau système et de gérer explicitement le placement — ou au moins testez les deux options. Ne devinez pas.
Task 5: Observe NUMA placement and memory policy for a running process
cr0x@server:~$ pidof postgres
2147
cr0x@server:~$ numactl --show --pid 2147
policy: default
preferred node: current
physcpubind: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
cpubind: 0
nodebind: 0
membind: 0
Ce que cela signifie : Ce processus est effectivement confiné au nœud 0 pour le CPU et la mémoire. C’est bien — si la charge tient dans la bande passante mémoire et le cache du nœud 0.
Décision : Si le processus est volumineux et lié à la bande passante, vous pouvez scaler en le fragmentant sur plusieurs nœuds. S’il est sensible à la latence, gardez-le compact et local.
Task 6: Measure remote vs local memory access tendencies with numastat
cr0x@server:~$ numastat -p 2147
Per-node process memory usage (in MBs) for PID 2147 (postgres)
Node 0 56321.4
Node 1 112.7
Node 2 95.3
Node 3 88.9
Total 56618.3
Ce que cela signifie : La mémoire est majoritairement sur le nœud 0. Si les threads tournent aussi sur le nœud 0, vous êtes en bonne posture. Sinon, vous payez la latence du fabric.
Décision : Si vous voyez une part importante de mémoire sur des nœuds non locaux, corrigez le pinning ou le placement au démarrage. Pour les services : lancez-les sous numactl ou utilisez les politiques CPU/mémoire de systemd.
Task 7: Check for excessive page migrations (a jitter source)
cr0x@server:~$ grep -E 'pgmigrate|numa' /proc/vmstat | head
numa_pte_updates 1829401
numa_huge_pte_updates 0
numa_hint_faults 219884
numa_hint_faults_local 171102
numa_pages_migrated 48211
pgmigrate_success 47998
pgmigrate_fail 213
Ce que cela signifie : Des pages sont migrées. Un certain niveau de migration est normal avec l’équilibrage activé ; beaucoup de migrations sous charge est un signe que votre scheduler et votre politique mémoire se battent avec la charge.
Décision : Si le p99 de latence corrèle avec des pics de migration, réduisez les migrations : pinnez les threads, allouez la mémoire localement, envisagez de désactiver l’équilibrage NUMA automatique pour cette classe d’hôtes.
Task 8: Verify CPU frequency governor (latency vs power)
cr0x@server:~$ cpupower frequency-info | sed -n '1,18p'
analyzing CPU 0:
driver: amd-pstate-epp
CPUs which run at the same hardware frequency: 0
hardware limits: 1.50 GHz - 3.70 GHz
available cpufreq governors: performance powersave
current policy: frequency should be within 1.50 GHz and 3.70 GHz.
The governor "powersave" may decide which speed to use
current CPU frequency: 1.74 GHz
Ce que cela signifie : Vous êtes en politique « powersave ». C’est acceptable pour du calcul batch, et souvent mauvais pour la latence tail et la réactivité I/O.
Décision : Pour les nœuds critiques en latence, mettez le gouverneur en performance (et validez les températures). Faites-en une politique par rôle, pas un bricolage ponctuel.
Task 9: Set performance governor (controlled change)
cr0x@server:~$ sudo cpupower frequency-set -g performance
Setting cpu: 0
Setting cpu: 1
Setting cpu: 2
Setting cpu: 3
Ce que cela signifie : Vous avez réduit la variabilité des changements de fréquence.
Décision : Re-testez la latence p95/p99 et le débit. Si cela s’améliore, intégrez-le dans la configuration pour cette classe de systèmes.
Task 10: Check PCIe device NUMA locality (critical for NVMe/NIC)
cr0x@server:~$ lspci -nn | grep -E 'Non-Volatile|Ethernet' | head -n 3
01:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd NVMe SSD Controller [144d:a808]
41:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd NVMe SSD Controller [144d:a808]
81:00.0 Ethernet controller [0200]: Mellanox Technologies MT28908 Family [ConnectX-6] [15b3:1017]
cr0x@server:~$ cat /sys/bus/pci/devices/0000:01:00.0/numa_node
0
Ce que cela signifie : Ce périphérique NVMe est local au nœud NUMA 0 (ou du moins le noyau le croit). Si vos threads de stockage tournent sur le nœud 3, vous avez inventé de l’I/O distante.
Décision : Alignez la gestion des IRQ et les threads workers avec le nœud NUMA du périphérique. Pour les serveurs multi-périphériques, envisagez des files/workers par nœud.
Task 11: Inspect IRQ distribution (interrupt locality)
cr0x@server:~$ grep -E 'nvme|mlx5' /proc/interrupts | head -n 6
47: 12841 11993 12110 11888 PCI-MSI 1048576-edge nvme0q0
48: 42112 39877 40655 39221 PCI-MSI 1048577-edge nvme0q1
92: 11822 12031 11760 11911 PCI-MSI 524288-edge mlx5_comp0
93: 12203 12188 12002 12244 PCI-MSI 524289-edge mlx5_comp1
Ce que cela signifie : Les IRQs tombent sur un petit ensemble de CPUs (ici les premiers). Cela peut aller ; cela peut être terrible si ces CPUs ne sont pas locaux au périphérique ou s’ils sont occupés.
Décision : Si vous voyez des hotspots d’IRQ ou un traitement sur des nœuds erronés, changez l’affinité ou utilisez irqbalance avec une configuration NUMA-aware. Puis validez la latence.
Task 12: Pin an IRQ to CPUs local to a NUMA node (surgical)
cr0x@server:~$ cat /proc/irq/47/smp_affinity_list
0-3
cr0x@server:~$ sudo sh -c 'echo 0-31 > /proc/irq/47/smp_affinity_list'
cr0x@server:~$ cat /proc/irq/47/smp_affinity_list
0-31
Ce que cela signifie : Vous avez élargi l’ensemble de CPU ciblé par l’IRQ à 0–31 (souvent le nœud 0). Cela améliore la localité si le périphérique est sur le nœud 0.
Décision : Re-testez la latence I/O sous charge. Si c’est mieux, formalisez via des scripts udev/systemd (avec précaution) ou affinez la politique d’irqbalance plutôt que d’écrire manuellement.
Task 13: Check memory latency signals with perf (watch for stalled cycles)
cr0x@server:~$ sudo perf stat -e cycles,instructions,cache-misses,LLC-load-misses -p 2147 -- sleep 10
Performance counter stats for process id '2147':
18,421,334,112 cycles
12,102,884,551 instructions # 0.66 insn per cycle
221,433,112 cache-misses
98,331,220 LLC-load-misses
10.001112123 seconds time elapsed
Ce que cela signifie : Un faible IPC avec beaucoup de défauts de last-level cache peut indiquer une pression de latence mémoire — souvent amplifiée par des accès mémoire distants sur des topologies très dépendantes du fabric.
Décision : Si l’IPC chute sous charge et que vous suspectez de la mémoire distante, validez avec les stats NUMA et le placement. Ne sautez pas directement à « upgrade CPU ».
Task 14: Confirm Transparent Huge Pages status (can interact with migration/latency)
cr0x@server:~$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
Ce que cela signifie : THP est toujours activé. Parfois c’est bien ; parfois cela provoque des pics de latence à cause du défrag ou du comportement d’allocation, surtout avec des charges mixtes.
Décision : Pour les bases de données avec des bonnes pratiques connues, suivez-les. Si vous ne savez pas, testez sous une charge réaliste ; ne faites pas du « disable THP » ou « always THP » par mimétisme.
Task 15: Check if your process is bouncing across CPUs (migration)
cr0x@server:~$ pidstat -t -p 2147 1 5
Linux 6.8.0 (server) 01/10/2026 _x86_64_ (128 CPU)
12:00:01 PM UID TGID TID %usr %system %CPU CPU Command
12:00:02 PM 26 2147 2147 8.00 2.00 10.00 4 postgres
12:00:02 PM 26 2147 2161 6.00 1.00 7.00 37 postgres
12:00:02 PM 26 2147 2162 5.00 1.00 6.00 92 postgres
Ce que cela signifie : Les threads tournent sur les CPUs 4, 37, 92 — probablement différents nœuds NUMA. Ce n’est pas automatiquement erroné, mais c’est un signal d’alerte pour une base de données sensible à la latence.
Décision : Si la performance est incohérente, contraignez la base de données à un nœud (ou un ensemble de nœuds avec sharding explicite) et retestez.
Task 16: Validate memory bandwidth pressure with pcm-like signals (fallback: vmstat)
cr0x@server:~$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
12 0 0 987654 12345 456789 0 0 2 18 9000 22000 55 10 33 2 0
18 0 0 982110 12345 456792 0 0 0 12 9800 26000 62 12 24 2 0
Ce que cela signifie : Un grand nombre de changements de contexte et de threads en attente peut indiquer de la contention (verrous, ordonnancement) qui s’aggrave quand les threads s’étalent sur les nœuds NUMA. Ce n’est pas définitif, mais c’est un indice.
Décision : Combinez cela avec des vérifications de placement NUMA. Si la contention coïncide avec la dispersion cross-node, serrez l’affinité.
Mode d’intervention rapide
Ceci est la version « le pager sonne ». Vous n’avez pas le temps de devenir microarchitecte, vous avez le temps d’arrêter l’hémorragie.
First: Prove whether it’s locality/topology
- Vérifiez le nombre et le mapping des nœuds NUMA (
lscpu,numactl --hardware). Si NUMA>1, supposez que la localité importe jusqu’à preuve du contraire. - Vérifiez la dispersion des processus (
pidstat -t,ps -o psr). Si les threads chauds tournent sur des nœuds éloignés, suspectez la latence liée au fabric. - Vérifiez le placement mémoire (
numastat -p). Si la mémoire est majoritairement sur un nœud mais que les threads tournent ailleurs, vous avez probablement trouvé le coupable.
Second: Look for jitter sources that amplify fabric costs
- Gouverneur CPU / p-states (
cpupower frequency-info). « powersave » sur des nœuds critiques en latence est une victoire facile (en surveillant la thermique). - Migrations NUMA (
/proc/vmstatmigrations et hint faults). Une forte migration sous charge corrèle souvent avec des pointes tail. - Comportement THP / defrag (statut THP, recommandations spécifiques aux charges). Ce n’est pas toujours lié au fabric, mais cela se combine avec la latence.
Third: Validate I/O locality (storage and network)
- Nœud NUMA du périphérique (
/sys/bus/pci/devices/.../numa_node). - Distribution des IRQ (
/proc/interrupts, réglages d’affinité). Le traitement d’IRQ sur un mauvais nœud est la faute classique « pourquoi le NVMe est lent ». - Profondeur de queue / modération d’IRQ (spécifique au périphérique, mais commencez par confirmer que vous n’êtes pas bloqué par un seul cœur surchargé).
Si vous ne faites qu’une chose durant la première heure : rendez la charge locale — CPU, mémoire et interruptions I/O alignées — puis retestez. C’est le mode d’échec « façonné par le fabric » le plus courant et le plus rapide à corriger.
Erreurs courantes : symptôme → cause racine → correction
1) Symptom: Great average throughput, terrible p99 latency
Cause racine : Migration de threads et accès mémoire distants entre nœuds NUMA ; le fabric ajoute de la latence et augmente la variance.
Correction : Pinnez les threads chauds à un nœud NUMA, liez la mémoire localement (politiques systemd CPU/NUMA ou numactl), et réduisez les migrations (ajustez/désactivez l’équilibrage NUMA automatique pour cette classe).
2) Symptom: “Upgraded RAM speed” and performance got worse
Cause racine : Découplage FCLK/UCLK/MCLK ou changement de ratio ayant augmenté la latence mémoire effective ; la bande passante peut s’être améliorée mais la latence critique a été pénalisée.
Correction : Préférez des profils mémoire stables et basse-latence pour les charges sensibles. Validez avec les métriques p99 réelles, pas uniquement la bande passante synthétique.
3) Symptom: NVMe array benchmarks fine, production I/O latency is spiky
Cause racine : IRQ traitées sur des CPUs non locaux, ou threads de stockage programmés loin du root PCIe ; le saut fabric ajoute latence et défauts de cache.
Correction : Alignez l’affinité IRQ et les threads workers avec la localité du périphérique ; envisagez un modèle de queues/workers par nœud NUMA. Re-testez sous une concurrence réaliste.
4) Symptom: VM performance inconsistent across hosts “with same CPU”
Cause racine : Partitionnement NUMA différent dans le BIOS (réglages NPS), interleaving mémoire, ou changements firmware affectant la topologie et le comportement du fabric.
Correction : Standardisez les profils BIOS et les firmwares ; capturez la topologie via lscpu/lstopo lors du provisioning ; appliquez des règles VM NUMA-aware et du pinning.
5) Symptom: CPU utilization low, but the service is slow
Cause racine : Stalls dus à la latence mémoire (mémoire distante, défauts de cache), contention de verrous amplifiée par l’ordonnancement cross-node, ou overhead d’I/O interrompu sur de mauvais cœurs.
Correction : Utilisez perf stat et les stats NUMA pour confirmer le comportement de stall ; ajustez le placement ; réduisez le partage cross-node ; corrigez la localité des IRQ.
6) Symptom: “It’s only one socket, so NUMA can’t matter”
Cause racine : Les topologies par chiplet créent un comportement de type NUMA à l’intérieur d’une seule socket ; Linux l’expose en tant que nœuds NUMA pour une raison.
Correction : Traitez les systèmes multi-CCD mono-socket comme des systèmes NUMA. Intégrez la localité dans vos runbooks et votre capacity planning.
Trois mini-histoires d’entreprise du terrain
Mini-story 1: The incident caused by a wrong assumption
Ils avaient une passerelle de stockage « simple » : quelques serveurs EPYC, un réseau rapide, un cache NVMe, et une pile I/O en espace utilisateur. En staging tout volait. En production ça sautillait — la latence tail grimpait pendant les pics, et l’astreinte regardait des disques propres et des graphes réseau propres comme s’ils étaient victimes d’un gaslighting par Grafana.
L’hypothèse erronée était subtile : « une socket unique = mémoire uniforme ». L’équipe traitait la machine comme un pool plat de cœurs et de RAM. Ils ont laissé le noyau ordonnancer librement, et laissé les threads I/O flotter parce que « Linux fera le bon choix ». Linux a fait quelque chose de raisonnable. La charge avait besoin d’une chose spécifique.
Les périphériques PCIe NVMe étaient locaux à un nœud NUMA, mais les threads de complétion les plus occupés tournaient souvent sur un autre. Chaque complétion I/O incluait un saut fabric, plus des défauts de cache car les structures de données les plus chaudes vivaient dans une autre région L3. À faible charge, ça ne se voyait pas. À forte concurrence, ça comptait beaucoup.
Ils ont corrigé avec trois changements : (1) pinner les threads I/O sur des CPUs locaux aux contrôleurs NVMe, (2) lier l’allocation mémoire de ces threads au même nœud, (3) fixer l’affinité IRQ pour empêcher les complétions d’atterrir sur des CPUs aléatoires. La latence s’est stabilisée, le débit a augmenté, et l’astreinte a arrêté de voir des problèmes de performance fantômes qui n’apparaissaient qu’après le déjeuner.
Mini-story 2: The optimization that backfired
Une équipe plateforme voulait « de la performance gratuite » sur une flotte de serveurs applicatifs. Le plan : augmenter la fréquence mémoire dans le BIOS. L’outil du fournisseur indiquait une opération stable, le burn-in passait, et les graphes d’un bench synthétique semblaient excellents. Ils ont déployé par vagues parce qu’ils n’étaient pas imprudents — juste optimistes.
Puis l’incident : la latence p99 des API a augmenté, pas diminué. Pas partout. Juste certains services, surtout ceux utilisant beaucoup de petites structures en mémoire et réalisant des échanges fréquents entre threads. L’utilisation CPU a légèrement baissé (ce qui semblait « bon » si on ne savait pas mieux), mais les temps de réponse se sont empirés.
Le post-mortem fut inconfortable : les nouveaux réglages mémoire ont poussé le système dans une autre relation d’horloges qui a augmenté la latence effective d’accès mémoire. La bande passante s’est améliorée. La latence s’est dégradée. Les charges étaient liées à la latence et très sensibles aux motifs d’accès mémoire distants, si bien que le délai lié au fabric est devenu visible dans les métriques tail.
Ils ont rollbacké l’« upgrade » mémoire et re-testé avec deux profils : un pour les jobs batch lourds en bande passante, un pour les services basse-latence. Ils ont fini par standardiser des profils par rôle. L’optimisation des performances a cessé d’être un exercice universel « augmenter un nombre » et est redevenue ce qu’elle devait être : de l’ingénierie spécifique à la charge.
Mini-story 3: The boring but correct practice that saved the day
Une société financière exécutait un service sensible à la latence sur EPYC. Ils ne faisaient rien d’exotique. Ce qu’ils avaient, c’était de la discipline : chaque rôle serveur avait une config BIOS de base, une config noyau de base, et un petit « rapport de topologie » capturé lors du provisioning et stocké avec l’enregistrement de l’actif.
Un matin après une maintenance de routine, un sous-ensemble de nœuds montra une hausse de la latence tail. Pas catastrophique, mais suffisante pour déclencher des alertes. L’astreinte a comparé deux rapports de topologie : le layout NUMA avait changé. Même modèle, même quantité de RAM, mais un réglage BIOS avait modifié le partitionnement NUMA. Le comportement du scheduler a changé, la localité mémoire a changé, et le fabric a commencé à faire du travail supplémentaire.
Ils ont remis le profil BIOS connu-bon, rebooté les nœuds affectés, et le problème a disparu. Pas d’héroïsme. Pas de « deep dive » noyau. Juste un contrôle de configuration et de l’observabilité.
Blague n°2 : La meilleure correction de performance, c’est parfois un tableur. Ne le dites pas à vos développeurs, ils vont commencer à demander des tableaux croisés dynamiques.
Listes de contrôle / plan étape par étape
Checklist: Before you tune anything
- Capturez les sorties
lscpu,numactl --hardware, etlstopo-no-graphicspour la classe de nœuds. - Confirmez que les versions BIOS/firmware sont cohérentes sur la flotte pour les nœuds comparables.
- Définissez les métriques de succès : latence p50/p95/p99, débit, taux d’erreur, coût CPU par requête, et (pour le stockage) IOPS à une latence donnée.
- Exécutez un test représentatif de la charge. Les microbenchmarks synthétiques donnent des indices ; ils ne sont pas votre test d’acceptation.
Step-by-step: Make a service NUMA-sane (lowest-risk path)
- Choisissez une cible nœud NUMA en fonction des CPUs/mémoire disponibles et de la localité des périphériques (NVMe/NIC).
- Pinnez les threads du service aux CPUs de ce nœud (systemd
CPUAffinity=ou un wrapper). - Liez l’allocation mémoire à ce nœud (
numactl --membindou les politiques NUMA de systemd si utilisées). - Corrigez la localité des IRQ pour que les interruptions du périphérique atterrissent sur des CPUs locaux.
- Re-testez sous charge, comparez la latence tail et le nombre de cycles CPU par requête.
- Ce n’est qu’ensuite que vous touchez aux horloges mémoire, boosts ou aux réglages d’alimentation — car ils peuvent améliorer les moyennes tout en ruplant les extrêmes.
Step-by-step: Diagnose a “fabric-ish” performance regression after a change
- Confirmez ce qui a changé : profil BIOS, firmware, version du noyau, microcode, réglages mémoire, règles de placement VM.
- Comparez les sorties de topologie (nœuds NUMA, mapping CPU) avant/après le changement.
- Vérifiez migrations et dispersion de scheduling (
pidstat -t, migrations dans/proc/vmstat,numastat -p). - Vérifiez le gouverneur CPU et la politique p-state ; restaurez la politique précédente si nécessaire.
- Vérifiez la distribution des IRQ et le nœud NUMA des périphériques ; restaurez l’affinité si elle a dérivé.
- Si vous devez rollbacker, faites-le rapidement. Si vous devez garder le changement, implémentez des contrôles de localité pour compenser.
FAQ
1) Is Infinity Fabric “the same thing” as NUMA?
Non. NUMA est un modèle visible par le logiciel : le temps d’accès mémoire dépend du nœud propriétaire de la mémoire. Infinity Fabric est l’interconnexion matérielle qui rend souvent ces différences réelles sur les systèmes AMD à chiplets.
2) Why do I see multiple NUMA nodes on a single-socket EPYC server?
Parce que la socket contient plusieurs chiplets et domaines de localité. Le noyau expose cela pour que le scheduler et l’allocateur mémoire puissent mieux décider. L’ignorer est permis, mais ça ne coûte pas rien.
3) Should I disable kernel automatic NUMA balancing?
Parfois. Pour des charges mixtes génériques, cela peut aider. Pour des services pinés et sensibles à la latence, cela peut introduire un overhead de migration de pages et du jitter. Testez les deux approches sur un hôte canari avec une charge de production représentative.
4) Does faster DDR always improve performance on Ryzen/EPYC?
Non. DDR plus rapide peut améliorer la bande passante, mais si cela change les ratios d’horloge ou augmente la latence effective, certaines charges empirent — en particulier celles sensibles à la latence tail et aux chemins de défauts de cache.
5) How do I know if remote memory is hurting me?
Cherchez un décalage : des threads tournant sur un nœud alors que les allocations mémoire vivent sur d’autres (pidstat + numastat -p). Surveillez aussi les migrations élevées de pages et un IPC bas avec beaucoup de LLC misses.
6) Can virtualization hide Infinity Fabric effects?
Ça peut masquer la cause et amplifier la douleur. Si une VM s’étale sur des nœuds NUMA, elle peut subir fréquemment des accès mémoire distants. L’exposition vNUMA appropriée, le pinning et le sizing importent sur EPYC.
7) Is this only relevant for CPU-bound workloads?
Souvent, c’est plus pertinent pour les charges I/O et mixtes parce que la localité des interruptions et les motifs d’accès mémoire dominent le comportement tail. Le « CPU idle » ne signifie pas « rapide ».
8) What’s the single most effective operational control?
Placement conscient de la topologie : gardez les threads chauds, leur mémoire et leurs interruptions de périphérique dans le même domaine de localité. Cela réduit latence, jitter et cycles CPU gaspillés.
9) Should I always pin everything?
Non. Trop pinner peut causer une charge inégale, de la famine et une mauvaise utilisation. Pinnen les choses sensibles à la latence ou qui possèdent des chemins I/O. Laissez les jobs batch et le travail d’arrière-plan plus flexibles.
10) What’s a good reliability mindset for this kind of tuning?
Utilisez des expériences contrôlées, des canaris et des profils par rôle. Si vous ne pouvez pas revenir rapidement en arrière, vous ne « tunez » pas, vous jouez.
Prochaines étapes à faire cette semaine
Voici le parcours pratique qui ne va pas ruiner votre flotte :
- Inventoriez la topologie pour chaque type de nœud : stockez les sorties
lscpu/numactl/lstopoavec l’enregistrement de l’actif. - Choisissez un service sensible à la latence et rendez-le NUMA-local (CPU + mémoire + localité IRQ). Mesurez le p99 et le coût CPU.
- Standardisez un profil BIOS et noyau par rôle (latence vs débit vs batch). « Un profil unique pour tous » est la recette des surprises.
- Ajoutez deux tableaux de bord : (a) indicateurs mémoire distante/migrations (vmstat dérivé, stats numa), (b) distribution des IRQ par CPU et principaux consommateurs.
- Rédigez un runbook avec les étapes de diagnostic rapide ci-dessus. Le but n’est pas d’adorer Infinity Fabric ; le but est d’arrêter d’être surpris par la topologie.
Une citation à garder au mur, parce qu’elle résume bien tout le sujet : « L’espoir n’est pas une stratégie. » — General Gordon R. Sullivan