Votre tableau de bord indique « CPU 40 % » et pourtant votre latence p99 ressemble à une chute dans un escalier. Quelqu’un murmure « Hyper-Threading », une autre personne dit « SMT », et soudain vous êtes en réunion pour désactiver la moitié des CPU que vous avez payés.
Hyper-Threading (la marque Intel pour le simultaneous multithreading, SMT) n’est pas magique, et ce n’est pas une arnaque non plus. C’est un compromis : davantage de débit en échange du partage de ressources microarchitecturales, d’une complexité accrue du planificateur et de modes de défaillance bien réels. Si vous exploitez des systèmes en production, vous devez savoir quand le SMT est votre allié, quand c’est un coloc bruyant, et comment le prouver avec des chiffres.
Ce qu’est réellement Hyper-Threading (et ce que ce n’est pas)
Un cœur CPU dispose de pipelines, d’unités d’exécution, de caches, de buffers, de prédicteurs de branchements, de files de chargement/stockage et d’un tas de mécanismes de bookkeeping qui maintiennent les instructions en mouvement. Une grande partie de cette machinerie reste inoccupée lorsqu’un thread se bloque — souvent sur la mémoire, parfois sur des branchements, parfois sur la fin d’une I/O, parfois sur des hazards de pipeline.
Le SMT permet à un seul cœur physique de conserver deux contextes de thread matériel. Pensez-y comme deux « guichets » alimentant une seule « cuisine ». Quand un thread attend (par exemple sur un miss de cache), l’autre thread peut utiliser des ressources d’exécution qui seraient autrement sous-utilisées. Le résultat est généralement un débit agrégé plus élevé par cœur.
Le SMT ne double pas votre puissance de calcul. Il ne vous donne pas deux ALU, deux caches L1, deux unités virgule flottante ou deux contrôleurs mémoire. Il vous donne deux états architecturaux (jeux de registres et un peu de bookkeeping par thread), plus de la logique pour entrelacer l’émission des instructions. L’accélération dépend de la charge de travail, est souvent modeste et peut parfois être négative.
« Mais l’OS affiche deux fois plus de CPU. » Oui. L’OS voit des CPU logiques. C’est un problème de dénomination : les gens entendent « CPU » et supposent « moteur de calcul indépendant ». Un CPU logique est souvent une cible de planification, pas une garantie de performance.
Si vous avez besoin d’un modèle mental : le SMT, c’est comme laisser deux voitures partager une seule voie avec une fusion zipper astucieuse. Ça marche très bien quand le trafic est éclaté. Ça marche terriblement quand les deux conducteurs décident de faire la course côte à côte dans la même voie.
Blague n°1 : Hyper-Threading, c’est comme ajouter un second volant à votre voiture. Vous n’obtenez pas un deuxième moteur, mais vous avez deux fois plus de discussions.
Faits & histoire qui expliquent le comportement d’aujourd’hui
Quelques points concrets qui aident à ancrer la mythologie dans la réalité :
- Le SMT existait avant la marque « Hyper-Threading ». IBM avait livré du SMT dans des systèmes POWER haut de gamme bien avant que cela ne devienne courant sur x86.
- Intel a introduit Hyper-Threading commercialement à l’époque du Pentium 4. Cela aidait à masquer les longs stalls des pipelines, mais la conception du P4 rendait aussi les performances « en piques » et sensibles aux charges.
- Les planificateurs Linux et Windows ont appris la conscience SMT à leurs dépens. Les premiers planificateurs traitaient les siblings comme des cœurs indépendants ; cela aggravait les contentions. Les planificateurs modernes tentent de packer et d’étaler intelligemment.
- Les modèles de coût cloud ont normalisé le concept de « vCPU ». Un « vCPU » peut être un cœur entier, un thread sibling, ou une tranche temporelle, selon le fournisseur et la classe d’instance. Les opérateurs ont hérité de cette ambiguïté.
- La recherche sur les canaux auxiliaires a changé la posture de risque par défaut. Le SMT peut augmenter la surface de fuite (caches et prédicteurs partagés), poussant certains environnements vers « SMT off » ou vers des stratégies de core scheduling.
- Les cœurs modernes sont devenus plus larges et plus profonds. Une largeur d’exécution plus importante offre plus d’opportunités pour que le SMT améliore l’utilisation — jusqu’à atteindre des goulots partagés comme la bande passante L1/L2.
- Le NUMA est devenu courant. Les bénéfices du SMT peuvent être éclipsés par les pénalités mémoire cross-socket ; un mauvais placement de threads peut faire du SMT le bouc émissaire.
- La containerisation a amplifié les effets du planificateur. Cgroups, quotas et ensembles de CPU interagissant avec des siblings SMT peuvent créer des étrangetés de throttling et de latence.
Ce qui est partagé : la liste inconfortable
Le SMT fonctionne en partageant la plupart du cœur. Les ressources partagées varient selon la microarchitecture, mais la réalité pour l’opérateur reste consistante : les siblings se concurrencent.
Front-end partagé et ressources d’exécution
- Bande passante fetch/décodage d’instructions : deux threads se disputent les créneaux de décodage. Si les deux sont lourds, chacun reçoit moins.
- Unités d’exécution : entières, flottantes, vectorielles, crypto — c’est toujours un seul ensemble. Si les deux threads utilisent les mêmes unités, il y a contention frontale.
- Structures du prédicteur de branchements : les prédicteurs partagés peuvent réduire la précision sous charges mixtes, augmentant les pénalités de mispredict.
- Ressources load/store : les buffers de stores et les files de load peuvent saturer sous des siblings intensifs en mémoire.
Caches partagés et bande passante mémoire
- Caches L1/L2 : généralement partagés par les siblings SMT (L1i/L1d et L2 par cœur). Le thrash de cache est le résultat classique « SMT a empiré les choses ».
- Cache de dernier niveau partagé (LLC) : partagé entre cœurs. Le SMT peut augmenter la pression sur le LLC en autorisant plus de misses en suspens.
- Bande passante mémoire : le SMT peut améliorer l’utilisation du cœur mais aussi solliciter davantage le sous-système mémoire. Si vous êtes limité par la bande passante, le SMT peut empirer la latence aux extrêmes.
Ce qui est plus séparé qu’on ne le pense
Chaque thread matériel possède son propre état architectural de registres. C’est pourquoi l’OS peut le traiter comme un CPU. Mais « état » n’est pas « débit ». Le chemin chaud reste le cœur partagé.
À retenir pour l’opérateur : le SMT est un pari que votre charge a des stalls qui peuvent se chevaucher sans se battre pour le même goulot. Si vous êtes déjà limité par la largeur d’exécution, la bande passante L1 ou la bande passante mémoire, le SMT peut simplement ajouter de la concurrence.
Planificateurs : où vit la « tromperie »
Le SMT devient intéressant dans le planificateur. Le travail du planificateur est de décider : dois-je placer deux threads exécutables sur des CPU logiques siblings du même cœur, ou dois-je les étaler sur des cœurs physiques séparés ?
Pack vs spread : pourquoi la réponse dépend de l’objectif
Pour le débit, il peut être bénéfique de remplir les siblings d’un même cœur avant de réveiller un autre cœur. Cela laisse d’autres cœurs inactifs (économie d’énergie) et peut améliorer la localité de cache si ces threads partagent des données.
Pour la latence, il est souvent préférable d’éviter la contention entre siblings. Garder un thread par cœur réduit les interférences et rend les performances plus prévisibles.
Linux : conscience SMT, CFS et le problème du « semble inactif »
Le Completely Fair Scheduler (CFS) de Linux tente d’équilibrer les tâches exécutables entre les CPU. Le SMT ajoute de la topologie : les CPU sont regroupés en cœurs, les cœurs en sockets, les sockets en nœuds NUMA. Le planificateur utilise cela pour décider si un CPU est « assez inactif » et ce que « inactif » signifie en termes SMT.
Voici la faute : un CPU logique sibling peut apparaître inactif même lorsque son cœur physique est occupé. Pour le planificateur, c’est encore un endroit pour exécuter du travail. Pour votre budget de latence, c’est un piège.
Windows et hyperviseurs : même problème, autres boutons
Les hyperviseurs planifient des vCPU sur des pCPU. Si l’hôte a du SMT, l’hyperviseur doit aussi décider si deux vCPU doivent partager un cœur. Certains s’en sortent bien ; d’autres font ce que vous leur avez demandé (ce qui peut être pire).
En pratique : si vous êtes sous KVM/VMware/Hyper-V, traitez le SMT comme une contrainte de topologie, pas comme un détail accessoire. « Nous avons 32 vCPU » n’est pas une réponse ; c’est le début d’un débat.
Quand le SMT aide, nuit, ou vous trompe
Le SMT a tendance à aider
- Charges mixtes : un thread intensif en calcul, l’autre en stalls mémoire ; ils se complètent.
- Forte I/O + travail en espace utilisateur : les threads passent du temps bloqués, se réveillent brièvement, puis se bloquent à nouveau. Le SMT peut réduire les bulles d’inactivité.
- Compilations et fermes de build : nombreuses tâches indépendantes ; le débit s’améliore en général, sans linéarité.
- Certaines charges web : beaucoup de petites tâches, stalls fréquents, intensité CPU par thread modérée.
Le SMT a tendance à nuire
- Systèmes basse-latence : troquer la déterminisme pour du débit est une mauvaise affaire quand vous vendez du p99.
- Borne par la bande passante mémoire : deux siblings peuvent tirer plus fort sur le même L1/L2 et la mémoire, augmentant la mise en file et la latence.
- Calcul vectoriel intensif : si les deux threads sollicitent les mêmes unités vectorielles larges, vous obtenez de la contention sans beaucoup de bénéfice de recouvrement.
- Bases de données sensibles au cache par cœur : le bruit ajouté par les siblings peut augmenter les misses de cache et la contention de verrous, surtout avec un mauvais pinning.
Le SMT « vous trompe » via une utilisation trompeuse
L’échec de diagnostic le plus courant : vous regardez l’utilisation CPU et concluez qu’il y a « de la marge ». Avec le SMT, l’utilisation est étalée sur des CPU logiques. Vous pouvez être saturé au niveau des cœurs tout en voyant « 50 % » sur un système 2-way SMT si chaque cœur physique exécute un thread saturé et que le sibling est majoritairement inactif.
Si vous vous souciez des performances, vous devez penser en cœurs physiques et cycles par instruction, pas en « pourcentage CPU ».
Sécurité et isolation : la partie que personne ne veut gérer
Le SMT augmente l’état microarchitectural partagé entre deux threads matériels sur le même cœur. Cela crée des surfaces supplémentaires pour les canaux auxiliaires. Selon votre modèle de menace, cela compte.
Conseils pratiques :
- Dans des environnements mono-tenant avec des charges de confiance, le SMT est généralement acceptable du point de vue risque ; la performance et la prévisibilité dominent la décision.
- Dans des environnements multi-tenant, surtout lorsque les locataires sont non dignes de confiance, le SMT peut devenir une question de politique. « On l’a activé pour le débit » n’est pas une justification de sécurité suffisante.
- Certains environnements atténuent le risque en désactivant le SMT, en pinning les locataires sur des cœurs, ou en utilisant des contrôles de type « core scheduling ». Si vous ne comprenez pas l’isolation de votre plateforme, ne faites pas semblant de la comprendre — mesurez et définissez une politique.
Citation (idée paraphrasée) du Dr Richard Cook : les systèmes complexes fonctionnent en mode dégradé ; les incidents proviennent du travail normal et des compromis normaux, pas d’« erreurs rares ».
Tâches pratiques : commandes, sorties, et décisions (12+)
Le but des commandes n’est pas d’admirer la sortie. C’est de prendre une décision : garder le SMT, le désactiver, changer la planification, pinner les charges, ou arrêter d’accuser le CPU et partir chasser le vrai goulot.
Tâche 1 : Confirmer la topologie CPU (cœurs vs threads)
cr0x@server:~$ lscpu | egrep 'Model name|Socket|Core|Thread|CPU\(s\)|NUMA node'
CPU(s): 32
Model name: Intel(R) Xeon(R) CPU
Thread(s) per core: 2
Core(s) per socket: 8
Socket(s): 2
NUMA node(s): 2
Ce que cela signifie : 32 CPU logiques, 16 cœurs physiques, 2 sockets, SMT=2.
Décision : La planification de capacité et le pinning doivent se faire en cœurs et sockets. « 32 CPU » n’est pas votre vrai plafond de calcul.
Tâche 2 : Cartographier les relations de siblings (quels CPU logiques partagent un cœur)
cr0x@server:~$ for c in /sys/devices/system/cpu/cpu[0-9]*/topology/thread_siblings_list; do echo "$c: $(cat $c)"; done | head
/sys/devices/system/cpu/cpu0/topology/thread_siblings_list: 0,16
/sys/devices/system/cpu/cpu1/topology/thread_siblings_list: 1,17
/sys/devices/system/cpu/cpu2/topology/thread_siblings_list: 2,18
/sys/devices/system/cpu/cpu3/topology/thread_siblings_list: 3,19
Ce que cela signifie : cpu0 et cpu16 sont siblings sur le même cœur physique, etc.
Décision : Pour le pinning critique en latence, évitez de placer deux threads lourds sur des IDs siblings.
Tâche 3 : Vérifier si le SMT est activé à l’exécution
cr0x@server:~$ cat /sys/devices/system/cpu/smt/active
1
Ce que cela signifie : 1 = SMT actif ; 0 = SMT inactif.
Décision : Si vous déboguez de l’imprévisibilité, enregistrez cet état dans la timeline d’incident. Cela change l’interprétation du « %CPU ».
Tâche 4 : Vérifier l’état des mitigations de vulnérabilités du noyau (affecte souvent la politique SMT)
cr0x@server:~$ grep -H . /sys/devices/system/cpu/vulnerabilities/* | head
/sys/devices/system/cpu/vulnerabilities/l1tf:Mitigation: PTE Inversion; VMX: conditional cache flushes, SMT vulnerable
/sys/devices/system/cpu/vulnerabilities/mds:Mitigation: Clear CPU buffers; SMT vulnerable
/sys/devices/system/cpu/vulnerabilities/spectre_v2:Mitigation: Retpolines; STIBP: disabled; RSB filling
Ce que cela signifie : La plateforme rapporte « SMT vulnerable » pour certaines failles.
Décision : Si vous êtes multi-tenant ou régulé, votre décision « SMT on/off » peut être dictée par la sécurité, pas par la performance.
Tâche 5 : Trouver la contention CPU et la pression de la file d’exécution rapidement
cr0x@server:~$ uptime
15:42:11 up 23 days, 4:18, 2 users, load average: 22.31, 20.87, 18.92
Ce que cela signifie : Une charge moyenne ~20 sur une machine 16 cœurs peut être acceptable ou catastrophique. Avec le SMT, « 32 CPU » vous incite à relativiser. Ne le faites pas.
Décision : Traitez la charge par rapport aux cœurs physiques. Si la charge dépasse de façon persistante les cœurs et que la latence est élevée, vous êtes probablement contraint par le CPU ou bloqué sur des I/O non interruptibles.
Tâche 6 : Voir si vous manquez de CPU ou si vous êtes bloqué sur I/O (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
18 0 0 214512 92344 812304 0 0 2 8 9200 18000 62 18 18 2 0
20 0 0 214488 92344 812312 0 0 0 0 9401 19012 64 17 18 1 0
22 0 0 214460 92344 812320 0 0 0 4 9602 20011 66 16 17 1 0
Ce que cela signifie : Un r élevé (file d’exécution) avec un id faible suggère une pression CPU. Un wa faible suggère que ce n’est pas de l’attente I/O.
Décision : Si la file d’exécution reste élevée et que la latence se corrèle, investiguez la planification/contestation SMT avant de courir après les disques.
Tâche 7 : Voir la saturation par CPU logique (mpstat) et repérer le déséquilibre des siblings
cr0x@server:~$ mpstat -P ALL 1 1 | head -n 20
Linux 6.1.0 (server) 01/09/2026 _x86_64_ (32 CPU)
15:42:35 CPU %usr %nice %sys %iowait %irq %soft %steal %idle
15:42:36 all 58.20 0.00 16.10 1.20 0.00 1.10 0.00 23.40
15:42:36 0 95.00 0.00 4.00 0.00 0.00 0.00 0.00 1.00
15:42:36 16 12.00 0.00 3.00 0.00 0.00 0.00 0.00 85.00
Ce que cela signifie : Le CPU0 est saturé tandis que son sibling CPU16 est majoritairement inactif. Ce n’est pas « normal » — cela implique un thread unique très lourd qui sature le cœur.
Décision : Si votre charge est à thread unique chaud, le SMT n’apportera rien. Envisagez d’étendre horizontalement, de corriger le parallélisme ou de pinner pour éviter les interférences des siblings.
Tâche 8 : Identifier les threads exacts et leur placement CPU
cr0x@server:~$ ps -eLo pid,tid,psr,pcpu,comm --sort=-pcpu | head
9123 9123 0 94.7 java
9123 9155 16 12.1 java
2201 2201 3 8.2 nginx
Ce que cela signifie : Un processus Java a un thread sur le CPU0 consommant ~95%. Un autre thread est sur le CPU16 (sibling) mais en consomme moins.
Décision : Si le thread chaud est sensible à la latence, réservez un cœur physique entier (garder le sibling silencieux) via cpuset/affinité.
Tâche 9 : Vérifier le throttling CFS dû aux quotas cgroup (containers + SMT = amusant)
cr0x@server:~$ cat /sys/fs/cgroup/cpu.stat
usage_usec 9812331221
user_usec 7201123000
system_usec 2611208221
nr_periods 128833
nr_throttled 21992
throttled_usec 981223122
Ce que cela signifie : La charge est fréquemment throttlée. Cela peut ressembler à « CPU idle mais lent » parce que le planificateur applique des quotas.
Décision : Si le throttling est élevé, corrigez les demandes/limites CPU avant d’accuser le SMT. Le SMT peut amplifier la perception car la comptabilité des CPU logiques ne se mappe pas proprement au débit par cœur.
Tâche 10 : Mesurer les tempêtes de changements de contexte (souvent un symptôme de contention sibling)
cr0x@server:~$ pidstat -w 1 3
Linux 6.1.0 (server) 01/09/2026 _x86_64_ (32 CPU)
15:43:10 UID PID cswch/s nvcswch/s Command
15:43:11 1000 9123 2200.00 3100.00 java
15:43:11 0 2201 800.00 1200.00 nginx
Ce que cela signifie : Des changements de contexte volontaires/non-volontaires élevés peuvent indiquer une contention de verrous, une pression sur la file d’exécution, ou trop de threads exécutables se disputant les ressources du cœur.
Décision : Si les taux de cswitch montent avec la latence, réduisez la concurrence exécutable, pinner les threads critiques, ou reconsidérez la taille des pools de threads.
Tâche 11 : Chercher des migrations CPU (accélérateur de désemballement cache)
cr0x@server:~$ perf stat -e context-switches,cpu-migrations,task-clock -a -- sleep 5
Performance counter stats for 'system wide':
220,441 context-switches
18,902 cpu-migrations
160,002.11 msec task-clock
5.001823308 seconds time elapsed
Ce que cela signifie : Beaucoup de migrations en 5 secondes signifie que des threads rebondissent entre CPU, perdant la chaleur du cache et parfois atterrissant sur des siblings de façon imprévisible.
Décision : Si les migrations sont élevées, examinez l’affinité, les cpusets et les réglages du planificateur ; envisagez le pinning pour les services critiques en latence.
Tâche 12 : Déterminer si vous êtes bloqué sur la mémoire (misses LLC, cycles, IPC)
cr0x@server:~$ perf stat -e cycles,instructions,cache-misses,LLC-load-misses -a -- sleep 5
Performance counter stats for 'system wide':
18,223,441,112 cycles
10,002,112,009 instructions
88,120,331 cache-misses
21,002,114 LLC-load-misses
5.001402114 seconds time elapsed
Ce que cela signifie : IPC ≈ 10.0B / 18.2B ≈ 0.55, ce qui est faible pour de nombreuses charges serveur ; beaucoup de LLC misses indique que vous êtes bloqué sur la mémoire.
Décision : Si les stalls mémoire dominent, le SMT peut empirer la latence aux extrêmes en augmentant le parallélisme mémoire et les files d’attente. Testez SMT off et/ou réduisez la concurrence.
Tâche 13 : Vérifier l’affinité des IRQ (mauvais placement d’IRQ peut mimer la douleur SMT)
cr0x@server:~$ grep -E 'eth0|nvme|mlx' /proc/interrupts | head
55: 1200033 0 0 0 IR-PCI-MSI 524288-edge eth0-TxRx-0
56: 0 0 0 0 IR-PCI-MSI 524289-edge eth0-TxRx-1
Ce que cela signifie : Si les interruptions sont concentrées sur un CPU qui exécute aussi des threads applicatifs chauds (ou son sibling), vous pouvez obtenir des pics de latence.
Décision : Répartissez les IRQ sur des cœurs physiques (ou isolez-les) avant de faire du SMT le coupable.
Tâche 14 : Vérifier l’utilisation CPU par processus vs l’illusion « %CPU »
cr0x@server:~$ top -b -n 1 | head -n 15
top - 15:44:12 up 23 days, 4:20, 2 users, load average: 22.10, 21.03, 19.12
%Cpu(s): 58.1 us, 16.2 sy, 0.0 ni, 23.9 id, 1.0 wa, 0.0 hi, 0.8 si, 0.0 st
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9123 app 20 0 12.1g 2.1g 302m R 620.0 6.7 188:22.11 java
Ce que cela signifie : Le processus utilise ~6,2 CPU logiques de temps. Sur un système 16 cœurs/32 threads, cela peut encore être « acceptable » ou être « on est à court de capacité », selon la contention sibling et les objectifs p99.
Décision : Comparez la demande du processus aux cœurs physiques, et corrélez avec la file d’exécution et les compteurs perf, pas seulement l’« idle% » de top.
Tâche 15 : Désactiver temporairement le SMT (pour des tests contrôlés)
cr0x@server:~$ echo off | sudo tee /sys/devices/system/cpu/smt/control
off
Ce que cela signifie : Le SMT est maintenant désactivé (à l’exécution, si supporté). Certains systèmes requièrent un reboot ou un réglage firmware.
Décision : Faites un test A/B sur une charge représentative. Si le p99 s’améliore notablement avec une perte de débit acceptable, conservez le SMT désactivé pour ce tier.
Tâche 16 : Confirmer l’état SMT après le changement
cr0x@server:~$ cat /sys/devices/system/cpu/smt/active
0
Ce que cela signifie : SMT inactive.
Décision : Rebaseliner les alertes : les seuils CPU% et de capacité doivent être ajustés car le « total CPU » a changé.
Blague n°2 : Désactiver le SMT pour corriger la latence, c’est comme enlever le siège passager pour améliorer les temps au tour. Ça marche, mais vos collègues poseront des questions.
Feuille de diagnostic rapide
Quand vous êtes en on-call, vous n’avez pas le temps d’un séminaire. Vous avez besoin d’un chemin rapide vers « CPU ? », « mémoire ? », « I/O ? », ou « planificateur/quota ? » Voici un ordre pratique qui évite les pièges courants.
Première étape : établir la topologie et si le « %CPU » ment
- Topologie :
lscpuet listes de siblings. Connaître cœurs vs threads vs sockets. - File d’exécution :
vmstat 1etuptime. Comparez la charge etraux cœurs physiques. - Saturation par CPU :
mpstat -P ALL. Recherchez des CPU logiques peggés avec des siblings inactifs.
Deuxième étape : décider si c’est exécution CPU ou stalls mémoire
- IPC + misses de cache :
perf stat -e cycles,instructions,LLC-load-misses. - Migrations :
perf stat -e cpu-migrationsetpidstat -w. - Points chauds de threads :
ps -eLo ... --sort=-pcpu.
Troisième étape : éliminer la « fausse pression CPU » due aux quotas et interruptions
- Throttling cgroup :
cat /sys/fs/cgroup/cpu.stat(ou équivalent cgroup v2). - Hotspots d’IRQ :
/proc/interruptset masques d’affinité. - Vérification de l’attente I/O :
vmstatet (si disponible) stats par disque. Unwaélevé change l’analyse.
Si, après ces étapes, vous avez : file d’exécution élevée, idle faible, IPC faible, beaucoup de LLC misses et pics de latence — le SMT est un suspect principal. Si vous avez un throttling élevé ou de nombreuses migrations, le SMT peut être innocent ; vos paramètres de planification et limites sont en cause.
Erreurs courantes : symptômes → cause racine → correctif
1) « Le CPU est seulement à 50 %, donc ce ne peut pas être le CPU »
Syndromes : latence p99 mauvaise ; top montre beaucoup d’inactivité ; un ou deux threads sont peggés.
Cause racine : Un thread chaud sature un cœur physique ; les CPU logiques siblings gonflent le dénominateur.
Correctif : Regardez les stats par CPU logique et les paires de siblings ; pinner les threads chauds sur des cœurs isolés ; scale-out ou supprimer les goulots sériels.
2) « Nous avons doublé les vCPU, donc nous avons doublé la capacité »
Syndromes : Après migration vers des instances riches en SMT, le débit augmente un peu mais la latence tail s’aggrave ; les effets de voisins bruyants augmentent.
Cause racine : Le SMT donne des gains partiels, pas une mise à l’échelle linéaire ; les ressources partagées augmentent les interférences.
Correctif : Rebaseliner avec des compteurs perf et p99 ; réserver des cœurs pour les tiers sensibles à la latence ; arrêter d’équivaloir vCPU et cœurs dans la documentation de dimensionnement.
3) « Désactiver le SMT va corriger les performances »
Syndromes : SMT off améliore un indicateur mais le débit global chute ; les coûts CPU augmentent ; certains services ralentissent.
Cause racine : La charge est orientée débit et bénéficie du recouvrement des stalls ; le SMT n’était pas le goulot.
Correctif : Garder le SMT activé ; se concentrer sur la localité mémoire (NUMA), I/O, contention de verrous ou réglage du GC. N’utiliser l’affinité que lorsqu’elle apporte de la prévisibilité.
4) « Pinner tout, c’est de l’ingénierie de performance »
Syndromes : Excellents benchmarks, vie réelle médiocre ; lourde charge de maintenance ; stalls périodiques quand un CPU pinné reçoit des tempêtes d’IRQ.
Cause racine : Le sur-pinning réduit la flexibilité du planificateur et crée un couplage fragile à la topologie et aux interruptions.
Correctif : Pinner uniquement le strict nécessaire ; laisser le reste planifiable. Gérer explicitement l’affinité IRQ et les CPU de housekeeping.
5) « Les containers sont isolés, donc le SMT n’a pas d’importance »
Syndromes : Un container monte en pic et la latence d’un autre augmente même avec des limites CPU séparées.
Cause racine : Les limites sont basées sur le temps, pas exclusives au cœur ; les siblings partagent des ressources ; le throttling cgroup crée du jitter.
Correctif : Utiliser des cpusets pour l’isolation quand vous avez besoin de déterminisme ; éviter de placer deux containers intensifs sur des siblings ; surveiller les compteurs de throttling.
6) « NUMA est un détail matériel ; on peut l’ignorer »
Syndromes : Chute de performance étrange lors de l’augmentation de threads ; beaucoup de LLC misses ; trafic cross-socket.
Cause racine : Allocations mémoire et threads répartis sur des sockets ; le SMT devient une distraction face aux accès mémoire distants.
Correctif : Utiliser un placement conscient NUMA (numactl, cpusets) ; garder la mémoire et les threads locaux ; tester le SMT au sein d’un socket.
Trois mini-récits d’entreprise du terrain
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise exploite une API client-facing avec des objectifs p99 stricts. Ils migrent vers une nouvelle flotte qui a « deux fois plus de CPU » sur le papier. Les achats sont contents. Les finances sont ravies. L’on-call s’apprête à apprendre un nouveau hobby : la sonnerie.
Le premier jour de gros trafic arrive. La latence médiane semble correcte. Le p99 et le p99.9 partent en vrille par à-coups. L’autoscaling réagit en retard parce que la moyenne CPU est « seulement » de 55 %. Le canal d’incident se remplit de captures d’écran de dashboards rassurants, ce qui est la preuve la plus insultante qui soit.
Quelqu’un finit par vérifier le comportement par cœur : quelques threads critiques saturent des cœurs physiques tandis que les siblings restent majoritairement inactifs. Le service a quelques chemins chauds sérialisés (logging et signature de requête) qui ne scalent pas avec plus de threads exécutables. Le SMT a rendu l’utilisation confortable en apparence alors que la vraie ressource limitante — le débit par cœur unique — était déjà au maximum.
La correction n’a pas été « désactiver SMT » immédiatement. Ils ont d’abord arrêté de se mentir : les alertes sont passées du « %CPU » à la file d’exécution et à la saturation par cœur ; la capacité a été ré-estimée en cœurs physiques. Ensuite ils ont optimisé les chemins sériels et pinné les threads les plus sensibles à la latence sur des cœurs dédiés. Le SMT est resté activé pour la flotte générale, désactivé pour le tier le plus strict.
Mini-récit 2 : L’optimisation qui a mal tourné
Une autre organisation exploite un pipeline de type Kafka avec forte compression. Un ingénieur remarque une forte utilisation CPU et décide de « maximiser l’usage CPU » en augmentant les threads workers jusqu’à ce que tous les CPU logiques soient occupés. Ça paraît brillant en test de charge. Tout affiche 95 % d’utilisation. La diapositive s’écrit d’elle-même.
En production, la latence tail du traitement des messages augmente. Le retard des consommateurs s’accumule pendant les heures de pointe. Le système ne plante pas ; il est juste… plus lent, d’une façon qui vous fait remettre en question vos choix de carrière.
Le postmortem montre que le système est sensible à la bande passante mémoire et au cache. La compression/décompression martèlent les unités d’exécution partagées et thrashent les caches par cœur. Doubler le nombre de threads exécutables signifie que les siblings se battent pour la même bande passante L1/L2, tandis que le sous-système mémoire sert plus de misses en suspens. Le débit n’a pas doublé ; la mise en file a augmenté.
Revenir en arrière sur le nombre de threads améliore immédiatement le p99. Ils réintroduisent plus tard la concurrence prudemment : un worker par cœur physique pour l’étape de compression, plus de flexibilité en aval là où le travail est I/O-bound. Ils ajoutent aussi des contrôles de régression basés sur les compteurs perf pour que « plus de threads » doive se justifier par l’IPC et les métriques de cache, pas seulement par le %CPU.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une plateforme de paiements exploite une architecture en couches : API stateless, base de données stateful, et une file. Rien d’exotique. L’équipe n’est pas obsédée par les micro-optimisations. Elle est obsédée par des baselines reproductibles.
À chaque rafraîchissement matériel, ils effectuent une caractérisation standard de performance : topologie enregistrée, état SMT enregistré, disposition des nœuds NUMA enregistrée, et une charge connue rejouée. Ils gardent un petit « laboratoire canari » qui reflète la production en versions de noyau et mitigations. Oui, c’est ennuyeux. C’est pourquoi ça marche.
Un jour, une mise à jour du noyau change les mitigations et modifie légèrement le comportement du planificateur. Le run de baseline capture une régression p99 cohérente dans le tier base de données avec SMT activé. Le changement est suffisamment petit pour avoir été ignoré en production comme « variabilité du trafic », mais le replay en lab est stable.
Ils publient une politique : les nœuds DB tournent SMT off ; les nœuds applicatifs tournent SMT on. Ils documentent aussi pourquoi : la charge DB est sensible au cache et priorise la latence tail. Le résultat n’est pas héroïque ; il est prévisible. Et la prévisibilité, c’est ce qui vous aide à opérer à 3 h du matin.
Listes de contrôle / plan étape par étape
Checklist A : Décider si le SMT doit être activé pour un tier de service
- Classer l’objectif : priorité débit ou priorité latence ?
- Mesurer la baseline avec SMT activé : p50/p95/p99, débit, taux d’erreur.
- Mesurer la baseline avec SMT désactivé : même charge, même version.
- Comparer avec les compteurs matériels : IPC, LLC misses, migrations, context switches.
- Décider par tier : il est normal d’avoir des politiques SMT différentes pour DB vs nœuds stateless.
- Mettre à jour les seuils de monitoring : les alertes %CPU changent de sens quand le nombre de CPU logiques change.
Checklist B : Pinner sans se tirer une balle dans le pied
- Identifier les paires de siblings depuis
thread_siblings_list. - Choisir des cœurs physiques à réserver pour le service sensible à la latence.
- Maintenir les périphériques IRQ lourds en dehors de ces cœurs (ou isoler les IRQ sur des CPU de housekeeping).
- Pinner uniquement les threads/processus critiques. Laisser le reste planifiable.
- Re-vérifier les migrations et context switches après pinning ; un pinning qui augmente les migrations est un signe d’alerte.
Checklist C : Sanity pour containers et cgroups
- Vérifier les compteurs de throttling ; s’ils sont élevés, corriger les limites avant d’ajuster le SMT.
- Utiliser des cpusets pour l’isolation quand vous avez besoin de déterminisme.
- Éviter de packer deux containers intensifs sur des threads siblings si vous vous souciez du p99.
- Re-tester après modifications sous une charge réaliste ; les tests synthétiques manquent souvent les pathologies du planificateur.
FAQ
1) L’Hyper-Threading, est-ce la même chose que des « cœurs supplémentaires » ?
Non. C’est deux threads matériels partageant un même cœur. Vous obtenez des contextes de planification supplémentaires, pas des ressources d’exécution doublées.
2) Dois-je désactiver le SMT pour les bases de données ?
Souvent, pour les bases sensibles à la latence et au cache, désactiver le SMT peut améliorer la prévisibilité du p99. Mais ne le faites pas en copiant-collant — testez A/B avec des compteurs perf.
3) Pourquoi l’utilisation CPU semble faible alors que la latence est élevée ?
Parce que l’utilisation est moyennée sur des CPU logiques. Un cœur physique saturé peut être masqué par des siblings inactifs. Regardez les stats par CPU et la file d’exécution.
4) Le SMT aide-t-il la performance mono-thread ?
Pas directement. La performance mono-thread concerne surtout le comportement turbo, la localité cache et la microarchitecture. Le SMT peut nuire si un sibling vole des ressources.
5) Le SMT peut-il causer du jitter dans des systèmes temps réel ou basse-latence ?
Oui. Les ressources partagées créent des interférences variables. Si vous vendez une faible latence tail, vous voulez typiquement un thread exécutable par cœur (ou isoler les siblings).
6) Dans Kubernetes, une « CPU » demandée est-elle un cœur physique ?
Pas nécessairement. C’est une unité de planification et de quota. Sans cpusets et placement soigné, les charges peuvent partager des cœurs et siblings de façon imprévisible.
7) Si je désactive le SMT, est-ce que j’ai toujours une meilleure sécurité ?
Vous réduisez certains partages inter-threads sur le même cœur, ce qui peut diminuer certains risques de side-channel. Mais la sécurité est en couches ; mitigations, isolation des locataires et patching restent essentiels.
8) Quelle est la règle la plus simple pour les opérateurs ?
Pour les tiers orientés débit : SMT généralement activé. Pour les tiers stricts en latence : tester SMT off et envisager de garder les siblings au calme pour les threads critiques.
9) Pourquoi les performances ont-elles empiré après un tuning « plus de threads » ?
Parce que plus de threads exécutables peut augmenter la contention, les context switches, les misses de cache et la pression sur la bande passante mémoire. Le SMT facilite le déclenchement de ces phénomènes.
10) Quel est le meilleur indicateur pour décider si le SMT aide ?
Utilisez des métriques de bout en bout (p99, débit) plus des indicateurs microarchitecturaux (IPC, LLC misses) sous une charge représentative. Le %CPU seul ne suffit pas.
Conclusion : prochaines étapes pratiques
L’Hyper-Threading n’est pas des threads magiques. C’est une astuce d’utilisation qui se comporte parfois comme un repas gratuit et parfois comme un appartement partagé aux murs fins. Votre travail est de classifier la charge, mesurer le goulot, et choisir une politique que vous pourrez expliquer pendant un incident.
- Reformulez votre modèle mental : la capacité, ce sont les cœurs physiques et la bande passante mémoire, pas le nombre de CPU logiques.
- Adoptez la feuille de diagnostic rapide : topologie → file d’exécution → saturation par CPU → compteurs perf → throttling cgroup.
- Faites du SMT une décision par tier : gardez-le activé là où le débit compte ; envisagez de le désactiver (ou d’isoler les siblings) là où le p99 compte.
- Arrêtez d’utiliser le %CPU comme couverture émotionnelle : associez-le aux vues par cœur, aux migrations et à l’IPC.
Si vous ne faites rien d’autre : réalisez un test A/B contrôlé avec SMT activé vs désactivé pour votre tier le plus sensible à la latence, et conservez les preuves. Les opinions sont bon marché ; les compteurs perf ne le sont pas.