Toute revue de CPU semble décisive jusqu’à ce que vous essayiez de servir du trafic réel. Votre service n’est pas « Cinebench ». C’est un cocktail désordonné : appels système, TLS, JSON, pauses GC, interruptions, défauts de cache et un appel à la base de données qui parfois se réveille du mauvais côté du NUMA.
Si vous avez déjà déployé une mise à niveau CPU « sûre » et que vous avez quand même reçu une alerte pour la latence p99, vous connaissez déjà le problème : vous n’avez pas mesuré la bonne chose. Voici une méthode pragmatique pour tester les CPU avec votre charge de travail, avec des exécutions répétables et des décisions que vous pourrez défendre dans un postmortem.
Principes : ce que « performance CPU » signifie en production
Les tests CPU déraillent quand on pose la mauvaise question. La mauvaise question : « Quel CPU est le plus rapide ? » La bonne question : « Quel CPU fournit à ma charge de travail la latence et le débit dont j’ai besoin, avec un coût et un risque opérationnel acceptables ? »
La performance CPU en production est un problème à trois corps :
- Travail effectué (débit) : requêtes/sec, jobs/min, lignes scannées/sec.
- Temps pour le faire (latence) : en particulier la latence aux queues (p95/p99/p999).
- Stabilité : variance sous bruit — tâches d’arrière-plan, jitter, throttling, voisins bruyants.
Et « CPU » n’est souvent pas seulement « CPU ». C’est :
- Microarchitecture : IPC, caches, prédicteur de branchements, préfetchers.
- Sous-système mémoire : bande passante, latence, NUMA, défauts de page.
- Comportement du noyau : ordonnancement, changements de contexte, interruptions, cgroups.
- Thermiques et limites d’alimentation : comportement turbo qui est superbe 30 secondes puis s’effondre.
- Logiciel : options du compilateur, bibliothèques crypto, configuration du GC, verrous.
L’objectif des tests n’est pas de trouver un seul nombre. C’est de déterminer l’enveloppe d’exploitation : sous quelle charge le p99 grimpe, quand les files d’attente de run se forment, quand vous commencez à thrash les caches, et quel réglage fait réellement bouger l’aiguille.
Idée paraphrasée (John Allspaw, opérations/fiabilité) : « Vous ne pouvez pas revendiquer la fiabilité à moins de pouvoir la démontrer dans des conditions réalistes. » Cela s’applique aussi à la performance.
Une règle à laquelle je serai agaçantement constant : priorisez la latence d’abord, puis le coût. Les tests axés uniquement sur le débit conduisent à de mauvaises décisions CPU parce qu’ils cachent la douleur ressentie par les utilisateurs aux extrêmes.
Blague n°1 : Faire des benchmarks sur une machine de labo au repos, c’est comme tester un détecteur de fumée dans le vide — techniquement impressionnant, opérationnellement inutile.
Faits intéressants et contexte historique
Ce ne sont pas des anecdotes pour le plaisir. Elles expliquent pourquoi les tests CPU modernes ne peuvent pas être réduits à un seul label « GHz ».
- Les « guerres MHz » ont pris fin pour une raison. Vers le milieu des années 2000, la fréquence n’a plus augmenté proprement à cause de la densité de puissance et des fuites ; la performance s’est déplacée vers le multicœur et les gains microarchitecturaux.
- L’exécution spéculative a tout changé. L’exécution hors ordre et la spéculation ont amélioré l’IPC, mais ont aussi créé des classes de vulnérabilités (Spectre/Meltdown) qui ont introduit ensuite des mitigations affectant certains workloads.
- Le turbo n’est pas une promesse. Les CPU modernes boostent dynamiquement selon les limites de puissance/température/courant ; deux exécutions « identiques » peuvent différer si le refroidissement ou les réglages BIOS diffèrent.
- Le NUMA est ancien, mais reste un des principaux coupables. Les systèmes multi-socket NUMA existent depuis des décennies ; le mode défaillant (latence mémoire distante) surprend encore les équipes migrant depuis des nœuds plus petits.
- Les compteurs perf Linux n’ont pas été conçus pour les tableaux de bord. Les compteurs matériels de performance ont été conçus pour les concepteurs de puces et le profilage bas niveau ; les utiliser en exploitation nécessite une interprétation soignée.
- L’hyper-threading (SMT) dépend du workload. Il peut augmenter le débit lors des stalls, mais aggraver la latence de queue sur les services sensibles aux verrous ou aux caches.
- La virtualisation a mûri, puis les cgroups l’ont complexifiée à nouveau. Le surcoût VM a diminué, mais les quotas CPU des conteneurs ont introduit un nouveau mode d’échec : un throttling qui ressemble à une « latence mystérieuse ».
- Les grandes pages ne sont pas une victoire universelle. Les huge pages réduisent les misses TLB, mais augmentent la fragmentation et peuvent nuire quand la mémoire est dynamique ou surcommittée.
- Un « CPU plus rapide » peut ralentir le système. Si le CPU traite les requêtes plus vite, vous pouvez déplacer le goulet d’étranglement vers le stockage, les verrous ou des services en aval — alors votre p99 se détériore.
La méthode simple : construire un harnais de charge de travail fiable
Voici la méthode que je recommande quand vous voulez comparer des CPU ou valider un changement de tuning. Elle est simple, pas simpliste. Vous lancerez la même charge sur les candidats, recueillerez un petit ensemble de métriques et prendrez une décision basée sur la latence aux queues et les signaux de saturation.
1) Définissez la charge comme un adulte
« Trafic API » n’est pas une charge. Une charge est une distribution :
- Mix de requêtes (endpoints, types de requêtes, ratios lecture/écriture)
- Tailles des payloads (petit, typique, pathologique)
- Modèle de concurrence (threads, async, tailles des pools de connexions)
- Temps de réflexion / schéma d’arrivée (de type Poisson vs rafales)
- SLO qui vous importe (p99 sous N RPS)
Choisissez 1–3 scénarios représentatifs. Ne construisez pas tout un univers. Mais ne choisissez pas uniquement le chemin heureux non plus.
2) Rendez l’environnement ennuyeux volontairement
La répétabilité est une fonctionnalité. Votre environnement de test doit supprimer le bruit variable autant que possible :
- Verrouillez le gouverneur de fréquence CPU sur
performancepour les tests (ou au moins enregistrez-le). - Désactivez les tempêtes cron d’arrière-plan, les mises à jour de paquets et l’antivirus opportuniste.
- Assurez des versions de noyau, réglages BIOS, microcode et mitigations identiques entre les candidats — ou acceptez consciemment les différences et enregistrez-les.
- Ne comparez pas un hôte bare-metal à une VM surengagée et ne l’appelez pas « test CPU ». Ce serait comique.
3) Testez pour l’état stable, pas pour le théâtre du warm-up
La plupart des services ont des effets de warm-up : compilation JIT, caches, pools de connexions, cache filesystem, ARC, page cache, entraînement du prédicteur de branchements, même caches DNS. Vous voulez deux phases :
- Warm-up : assez long pour atteindre un comportement stable.
- Mesure : durée fixe où vous échantillonnez les métriques.
Quand les équipes sautent cela, elles optimisent pour « la première minute », puis se demandent pourquoi la tranche de 30 minutes fond. Vos CPU ne sont pas notés sur leur charisme.
4) Mesurez la distribution de la latence et les signaux de saturation CPU ensemble
Si vous enregistrez seulement l’utilisation CPU, vous êtes aveugle. Si vous enregistrez seulement la latence, vous ne savez pas où regarder. Associez-les :
- Latence : p50/p95/p99, plus le max (avec prudence).
- Débit : RPS, QPS, jobs/sec.
- Saturation CPU : file d’exécution (run queue), throttling, iowait (à interpréter avec soin).
- Compteurs perf (échantillonnage) : cycles, instructions, branches, cache misses.
5) Comparez à « douleur utilisateur » égale, pas à utilisation égale
Quand vous comparez deux CPU, ne faites pas correspondre « CPU = 70% ». Faites correspondre le point SLO (par ex., « p99 < 200ms »). Un CPU peut atteindre 70% en étant confortable, un autre à 50% peut déjà thrash à cause du cache ou des limites de fréquence.
6) Décidez avant d’exécuter : quel résultat change votre décision
Notez vos critères d’acceptation :
- « À 10k RPS, p99 < 150ms et taux d’erreur < 0.1%. »
- « Throttling CPU < 1% sous quota. »
- « Perf montre une IPC dans ±10% de la baseline ; pas de pic anormal de cache miss. »
Si vous ne décidez pas à l’avance, vous négocierez avec les graphiques après coup. Et les graphiques gagnent toujours.
Tâches pratiques (commandes, sorties, décisions)
Ci-dessous des tâches pratiques que vous pouvez exécuter sur Linux. Chacune inclut : une commande, ce que la sortie signifie et quelle décision prendre. Choisissez le sous-ensemble qui correspond à votre environnement, mais ne sautez pas celles qui révèlent que « le CPU n’est pas le CPU ».
Task 1: Record CPU model, topology, and SMT status
cr0x@server:~$ lscpu
Architecture: x86_64
CPU(s): 32
Thread(s) per core: 2
Core(s) per socket: 16
Socket(s): 1
Model name: Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz
NUMA node(s): 1
L3 cache: 48 MiB
Ce que cela signifie : Vous savez maintenant ce que vous testez réellement : cœurs, sockets, SMT, tailles de cache, NUMA. Les « 32 CPUs » peuvent être 16 cœurs avec SMT.
Décision : Si vous comparez des machines, assurez-vous que la topologie est comparable, ou incluez explicitement les différences SMT/NUMA dans votre conclusion. Si votre service est sensible à la latence, planifiez un test A/B avec SMT activé vs désactivé.
Task 2: Check CPU frequency governor and current frequency behavior
cr0x@server:~$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
ondemand
Ce que cela signifie : « ondemand » peut ajouter du jitter et sous-booster pendant de courtes pointes, selon le noyau et la plateforme. Sur des serveurs, vous voulez souvent un comportement cohérent.
Décision : Pour le benchmarking, définissez le gouverneur sur performance (ou enregistrez-le et acceptez la variabilité). Si vous ne pouvez pas le modifier (cloud), au moins enregistrez-le et exécutez plus longtemps pour lisser le comportement de boost.
Task 3: Verify turbo/boost capability (when available)
cr0x@server:~$ cat /sys/devices/system/cpu/intel_pstate/no_turbo
0
Ce que cela signifie : 0 signifie que le turbo est autorisé. Si c’est 1, votre « CPU rapide » pourrait se comporter comme un CPU poli.
Décision : Gardez le turbo cohérent entre les hôtes de test. Si un hôte a le turbo désactivé (BIOS ou OS), votre comparaison est déjà corrompue.
Task 4: Check for CPU throttling due to cgroups (containers/Kubernetes)
cr0x@server:~$ cat /sys/fs/cgroup/cpu.stat
usage_usec 987654321
user_usec 800000000
system_usec 187654321
nr_periods 12345
nr_throttled 2345
throttled_usec 456789012
Ce que cela signifie : nr_throttled et throttled_usec indiquent que le noyau arrête de force votre workload pour appliquer le quota CPU.
Décision : Si le throttling est non négligeable pendant votre test, votre « benchmark CPU » est un benchmark de quota. Augmentez le quota, retirez les limites pour le test, ou traitez le « throttling par requête » comme une métrique primaire.
Task 5: Watch run queue and CPU usage over time
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
2 0 0 8123456 123456 789012 0 0 0 2 900 1400 25 5 70 0 0
8 0 0 8121200 123456 789100 0 0 0 0 1200 6000 80 10 10 0 0
10 0 0 8119000 123456 789200 0 0 0 0 1300 7000 85 10 5 0 0
9 0 0 8118000 123456 789250 0 0 0 0 1250 6800 83 12 5 0 0
3 0 0 8120000 123456 789300 0 0 0 0 1000 3000 50 7 43 0 0
Ce que cela signifie : r est les processus exécutables. Si r dépasse régulièrement le nombre de cœurs, vous êtes saturé CPU ou en contention. Des pics de cs (context switches) peuvent indiquer une contention sur les verrous ou une surallocation.
Décision : Si la file d’exécution augmente pendant que la latence s’envole, vous avez probablement besoin de plus de CPU (ou moins de threads) ou vous frappez un verrou. Si la file d’exécution est faible mais la latence élevée, le goulot est ailleurs.
Task 6: Inspect per-core utilization and steal time (virtualization)
cr0x@server:~$ mpstat -P ALL 1 3
Linux 6.1.0 (server) 01/12/2026 _x86_64_ (32 CPU)
12:00:01 AM CPU %usr %nice %sys %iowait %irq %soft %steal %idle
12:00:02 AM all 62.00 0.00 8.00 0.10 0.00 1.50 5.00 23.40
12:00:02 AM 0 90.00 0.00 5.00 0.00 0.00 0.00 2.00 3.00
12:00:02 AM 1 10.00 0.00 5.00 0.00 0.00 0.00 20.00 65.00
12:00:03 AM all 64.00 0.00 7.50 0.00 0.00 1.20 6.50 20.80
Ce que cela signifie : %steal montre l’hyperviseur qui prend du temps CPU. Un steal élevé rend la latence bruyante et invalide les comparaisons entre exécutions.
Décision : Si le steal > 1–2% pendant les tests, passez à des hôtes/instances dédiés ou considérez l’environnement comme « non adapté à la comparaison CPU ».
Task 7: Check NUMA layout and whether your process is bouncing memory
cr0x@server:~$ numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
node 0 size: 64250 MB
node 0 free: 12000 MB
node 1 cpus: 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
node 1 size: 64250 MB
node 1 free: 15000 MB
node distances:
node 0 1
0: 10 21
1: 21 10
Ce que cela signifie : L’accès mémoire distant (distance 21 vs 10) est significativement plus lent. Si votre processus est planifié sur le nœud 0 mais accède fréquemment à la mémoire du nœud 1, votre CPU paraît « lent ».
Décision : Pour les tests, fixez la locality CPU et mémoire pour la charge (ou testez explicitement avec et sans pinning). Si vous migrez vers du dual-socket, traitez NUMA comme une exigence de première classe.
Task 8: Detect CPU migrations (a common latency killer)
cr0x@server:~$ perf stat -e task-clock,context-switches,cpu-migrations,page-faults -p 1234 -- sleep 10
Performance counter stats for process id '1234':
10012.34 msec task-clock # 0.999 CPUs utilized
120,345 context-switches # 12.014 K/sec
3,210 cpu-migrations # 320.593 /sec
8,765 page-faults # 875.539 /sec
10.012345678 seconds time elapsed
Ce que cela signifie : Des milliers de migrations CPU par seconde peuvent détruire la localité du cache et causer du jitter. Les context switches peuvent indiquer une surallocation de threads, des verrous ou de l’IO bloquante.
Décision : Si les migrations sont élevées, envisagez le pinning CPU, réduire les threads exécutables, ou corriger la pression du scheduler (p.ex. placement cgroup). Si les context switches sont élevés et la latence en pics, cherchez la contention sur les verrous ou des pools de threads excessifs.
Task 9: Measure IPC and cache behavior with perf (macro view)
cr0x@server:~$ perf stat -e cycles,instructions,branches,branch-misses,cache-references,cache-misses -a -- sleep 10
Performance counter stats for 'system wide':
32,100,000,000 cycles
45,500,000,000 instructions # 1.42 insn per cycle
8,900,000,000 branches
120,000,000 branch-misses # 1.35% of all branches
2,100,000,000 cache-references
210,000,000 cache-misses # 10.00% of all cache refs
10.000987654 seconds time elapsed
Ce que cela signifie : L’IPC et les taux de cache-miss varient énormément selon les CPU et les charges. Un CPU à fréquence supérieure peut perdre face à un autre avec un meilleur comportement cache sur des services réels.
Décision : Si l’IPC s’effondre ou si les cache misses grimpent sous charge, concentrez-vous sur la localité mémoire, les structures de données et le modèle de concurrence — pas seulement « acheter des cœurs plus rapides ». Utilisez cela pour expliquer pourquoi un CPU « pire » synthétiquement peut gagner pour votre service.
Task 10: Confirm whether you’re actually CPU-bound or stalled on memory
cr0x@server:~$ perf stat -e cycles,instructions,stalled-cycles-frontend,stalled-cycles-backend -p 1234 -- sleep 10
Performance counter stats for process id '1234':
5,400,000,000 cycles
6,900,000,000 instructions # 1.28 insn per cycle
1,900,000,000 stalled-cycles-frontend
2,700,000,000 stalled-cycles-backend
10.001234567 seconds time elapsed
Ce que cela signifie : Des stalls backend élevés corrèlent souvent avec la latence/bande passante mémoire, des cache misses ou des limites de ressources du pipeline.
Décision : Si les stalls backend dominent, un CPU avec un meilleur sous-système mémoire (caches plus grand, plus de canaux mémoire) peut surpasser un CPU à fréquence plus élevée. Pensez aussi à NUMA et à la vitesse mémoire avant de réécrire la moitié du code.
Task 11: Identify top CPU consumers and whether they’re user vs kernel heavy
cr0x@server:~$ top -b -n 1 | head -n 15
top - 00:00:10 up 12 days, 3:12, 2 users, load average: 12.50, 11.20, 9.80
Tasks: 310 total, 8 running, 302 sleeping, 0 stopped, 0 zombie
%Cpu(s): 82.0 us, 10.0 sy, 0.0 ni, 8.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 128000.0 total, 22000.0 free, 53000.0 used, 53000.0 buff/cache
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 app 20 0 4567890 512000 25000 R 750.0 0.4 10:12.3 myservice
2345 root 20 0 0 0 0 S 80.0 0.0 1:20.1 ksoftirqd/3
Ce que cela signifie : Un sy (système) élevé plus des pics de ksoftirqd laisse penser que le réseau/traitement d’interruptions consomme du CPU. C’est toujours du CPU, mais la solution n’est pas « optimiser le parsing JSON ».
Décision : Si le CPU noyau est élevé, inspectez les interruptions, les réglages NIC, les taux de paquets, conntrack et les options d’offload TLS. Si le CPU utilisateur est élevé, profilez l’application.
Task 12: Check interrupt distribution (common on high packet rates)
cr0x@server:~$ cat /proc/interrupts | head -n 8
CPU0 CPU1 CPU2 CPU3
24: 1234567 1200345 98012 87000 IR-PCI-MSI eth0-TxRx-0
25: 98012 87000 1123456 1198765 IR-PCI-MSI eth0-TxRx-1
NMI: 1234 1234 1234 1234 Non-maskable interrupts
LOC: 98765432 98765000 98764900 98764800 Local timer interrupts
Ce que cela signifie : Si un CPU est noyé sous les interruptions NIC, vous verrez des hotspots et de la latence tail. Idéalement, les interruptions sont équilibrées entre les cœurs (ou fixées intentionnellement).
Décision : Si les interruptions sont déséquilibrées, activez/vérifiez irqbalance, ajustez les queues RSS ou épinglez les interruptions loin des threads critiques pour la latence.
Task 13: Measure scheduler pressure (PSI) to confirm contention
cr0x@server:~$ cat /proc/pressure/cpu
some avg10=0.50 avg60=0.80 avg300=0.75 total=123456789
full avg10=0.10 avg60=0.25 avg300=0.20 total=23456789
Ce que cela signifie : PSI indique le temps passé à attendre le CPU. « full » indique des tâches qui ne peuvent pas du tout s’exécuter faute de CPU. C’est extrêmement actionnable pour savoir « est-ce vraiment une contention CPU ? »
Décision : Si PSI « full » monte pendant les pics de latence, vous êtes à court de CPU ou throttlé. Ajoutez du CPU, réduisez la concurrence ou retirez le throttling. Si PSI est bas, arrêtez de blâmer le CPU et cherchez ailleurs.
Task 14: Confirm disk and filesystem aren’t smearing your CPU test
cr0x@server:~$ iostat -xz 1 3
Linux 6.1.0 (server) 01/12/2026 _x86_64_ (32 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
70.00 0.00 10.00 0.20 0.00 19.80
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %util await
nvme0n1 10.00 20.00 800.00 900.00 0.00 1.00 5.00 0.40
Ce que cela signifie : Un %util bas et un await faible suggèrent que le stockage n’est pas le goulot d’étranglement. Un iowait élevé n’est pas toujours un verdict « IO lent », mais cela vous donne une base.
Décision : Si l’utilisation disque et l’await augmentent pendant les « tests CPU », vous testez l’IO ou le comportement fsync. Isolez l’IO, migrez vers tmpfs pour des tests purement CPU, ou acceptez l’IO comme partie de la charge (souvent correct pour les bases de données).
Task 15: Run a controlled load test and capture latency percentiles
cr0x@server:~$ hey -z 60s -c 200 -q 50 http://127.0.0.1:8080/api/v1/search
Summary:
Total: 60.0012 secs
Slowest: 0.4123 secs
Fastest: 0.0102 secs
Average: 0.0451 secs
Requests/sec: 9700.12
Response time histogram:
0.010 [1] |
0.050 [520000] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
0.100 [55000] |■■■
0.200 [4000] |
0.400 [120] |
Latency distribution:
50% in 0.0400 secs
90% in 0.0800 secs
99% in 0.1500 secs
Ce que cela signifie : Cela vous donne une vue externe : ce que l’expérience client perçoit. Le p99 est votre « jauge de douleur client ».
Décision : Comparez les CPU à la même charge offerte et surveillez p99/p999. Si le CPU A vous donne 20% de RPS en plus mais double le p99, le CPU A est un piège pour les services sensibles à la latence.
Task 16: Profile hotspots without changing code (quick-and-dirty flamegraph input)
cr0x@server:~$ perf record -F 99 -p 1234 -g -- sleep 30
[ perf record: Woken up 8 times to write data ]
[ perf record: Captured and wrote 12.345 MB perf.data (12345 samples) ]
Ce que cela signifie : Vous avez collecté des piles d’appels. Vous pouvez maintenant identifier si le temps est passé en crypto, parsing JSON, allocation mémoire ou appels système du noyau.
Décision : Si les hotspots sont dans le réseau noyau, ajustez le noyau/NIC. Si les hotspots sont dans l’allocation/GC, ajustez le runtime ou réduisez les allocations. Si les hotspots sont dans un verrou, corrigez la concurrence. Tester le CPU sans profiler, c’est deviner avec de jolis graphiques.
Playbook de diagnostic rapide
Voici la version « Je suis d’astreinte et le graphique flambe ». Vous voulez décider vite si vous êtes lié CPU, throttlé ou si vous souffrez d’un autre goulot d’étranglement déguisé en CPU.
First: confirm user pain and whether it correlates with CPU pressure
- Vérifiez la latence p95/p99 (depuis la sortie du load test ou les métriques du service). Si la latence tail est plate, ne chassez pas le CPU juste parce que l’utilisation paraît élevée.
- Vérifiez PSI CPU (
/proc/pressure/cpu). Si « full » monte pendant les pics de latence, la contention/throttling CPU est réelle. - Vérifiez la run queue (
vmstat 1,uptime). Une run queue soutenue au-dessus du nombre de cœurs est un signal classique de saturation.
Second: rule out the top two CPU impostors
- Throttling : quotas de conteneur (
/sys/fs/cgroup/cpu.stat) et steal time cloud (mpstat). - NUMA/migrations : si vous êtes multi-socket ou avez des SLO stricts, vérifiez
numactl --hardwareetperf stat ... cpu-migrations.
Third: decide whether you need more CPU, different CPU, or different code
- Plus de CPU quand run queue + PSI sont élevés, et le profiling montre du calcul réel.
- Un autre CPU quand perf montre des stalls mémoire, des cache misses, ou un effondrement de fréquence. Vous pourriez avoir besoin de caches plus grands, plus de canaux mémoire ou d’un turbo all-core plus stable — pas seulement plus de cœurs.
- Un autre code quand des hotspots sont évidents et réparables (contention de verrous, tempêtes d’allocation, syscalls bavards).
Blague n°2 : Si votre p99 s’améliore seulement quand vous stoppez sa mesure, vous avez inventé la performance par observation — s’il vous plaît, ne le mettez pas en production.
Trois mini-histoires d’entreprise issues du terrain
Mini-histoire n°1 : L’incident causé par une mauvaise hypothèse (SMT « est toujours un gain gratuit »)
Une entreprise exécutait une API sensible à la latence qui faisait authentification, validation JSON et une petite requête BD. Ils sont passés d’une flotte plus petite à une génération CPU plus récente et ont vu d’excellents chiffres synthétiques. Quelqu’un a remarqué qu’on pouvait activer SMT partout et « obtenir 30% de CPU en plus » sans rien acheter. Le déploiement a commencé avec confiance et un tableau Excel de célébration.
En quelques heures, le p99 des requêtes de login a commencé à vaciller. Pas dramatiquement au début — juste assez pour déclencher leurs alertes SLO internes. Le taux d’erreur est resté bas, la latence moyenne semblait correcte, et l’utilisation CPU a même légèrement baissé parce que les requêtes « terminaient ». L’ingénieur d’astreinte a eu le pire genre de graphique : celui qui ne crie pas, il déçoit juste.
Ils ont chassé la base de données. Ils ont ajusté les pools de connexions. Ils ont même accusé le load balancer. Le vrai coupable était physiquement embarrassant : SMT a augmenté la contention sur des ressources d’exécution partagées et les caches pour un chemin d’authentification chargé en verrous et branchy. Sous des rafales, les threads supplémentaires ont augmenté les changements de contexte et empiré la latence tail.
La correction n’a pas été idéologique (« SMT mauvais »). Ils ont fait des tests contrôlés SMT on/off et ont découvert une séparation : les endpoints batch appréciaient SMT, le login non. Ils ont fini par désactiver SMT pour la couche critique en latence et le garder pour la couche batch. L’incident n’était pas « SMT a cassé la production ». L’incident était une mauvaise hypothèse : penser que les fonctionnalités CPU sont universellement bénéfiques.
À retenir : traitez SMT comme toute autre variable. Mesurez sous la concurrence réelle et surveillez la latence tail et les migrations. Les benchmarks orientés débit vous mentiront avec un aplomb sérieux.
Mini-histoire n°2 : L’optimisation qui a rebondi (la montée en CPU a déplacé le goulet et empiré le p99)
Une autre organisation avait un service marginalement CPU-bound : beaucoup de terminaison TLS plus une compression des réponses. Ils ont monté vers un CPU à fréquence plus élevée avec meilleure accélération crypto. Le RPS avant/après s’est amélioré. Les graphiques de revue d’upgrade semblaient propres. Tout le monde s’est relaxé.
Puis une semaine plus tard, le p99 a augmenté pendant les pics quotidiens normaux. Pas toujours. Pas de façon prévisible. Les nouveaux CPU étaient « plus rapides », pourtant les plaintes clients ont augmenté. L’équipe a réagi comme d’habitude : ajouter des pods, ajouter des nœuds, augmenter les limites d’autoscaling. Ça a aidé, mais le coût a grimpé et le problème n’a pas totalement disparu.
Le vrai problème : en rendant le front-end plus rapide, ils ont augmenté le débit de requêtes vers des caches en aval et un magasin clef-valeur partagé. Ce magasin avait une routine de compaction CPU-intensive qui était auparavant invisible parce que le front-end ne générait pas assez de pression. Maintenant il le pouvait. Le goulot s’était déplacé, et la latence tail a suivi le maillon le plus faible.
Ils ont corrigé cela en limitant la concurrence vers le magasin en aval, en ajoutant du backpressure et en ajustant les TTL des caches pour réduire les patterns de thundering-herd. La montée de CPU n’était pas mauvaise ; hypothèse que le CPU est une propriété locale l’était.
À retenir : les améliorations CPU changent la dynamique système. Quand vous testez des CPU, incluez le comportement des dépendances en aval ou isolez explicitement la dépendance. Si vous testez seulement un composant en isolation, vous faites une démo, pas de la planification de capacité.
Mini-histoire n°3 : La pratique ennuyeuse mais correcte qui a sauvé la mise (harnass reproductible + environnement épinglé)
Une équipe stockage préparait l’achat de nouveaux nœuds pour une plateforme d’analytics interne. Ils avaient deux options CPU : l’une avec plus de cœurs, l’autre avec moins mais des cœurs plus rapides et un cache plus grand. Les opinions étaient fortes, comme toujours quand le matériel est en jeu.
Au lieu de se disputer, ils ont construit un harnais terne et discipliné. Même image OS, même noyau, mêmes réglages BIOS, même baseline microcode. Ils ont verrouillé le gouverneur CPU, désactivé les services inutiles et lancé une phase de warm-up avant de mesurer. Ils ont capturé p50/p95/p99 pour la latence des requêtes, plus les compteurs perf et PSI. Chaque exécution était tagguée avec la révision git exacte et un timestamp. Personne ne pouvait « juste essayer un tweak de plus » sans l’enregistrer.
Ils ont trouvé quelque chose de contre-intuitif : le CPU « plus de cœurs » gagnait en débit brut, mais le CPU « cache plus grand » gagnait sur le p99 pour les requêtes lourdes de jointure. Les compteurs perf montraient des taux de cache miss et des stalls backend plus élevés sur l’option multi-cœurs. La conclusion n’était pas philosophique ; c’était des données : si ils voulaient une performance interactive stable, le CPU à cache plus grand était le choix le plus sûr.
Quand les achats ont demandé pourquoi ils choisissaient le SKU « moins dense en cœurs », ils ont présenté une synthèse d’une page avec des plots et des commandes reproductibles. Pas d’héroïsme. Juste de la compétence ennuyeuse. Cela a évité qu’une dispute à million de dollars se transforme en erreur à million de dollars.
À retenir : l’outil de perf le plus précieux est un harnais reproductible. Ce n’est pas glamour. C’est ce qui vous évite d’acheter des regrets.
Erreurs courantes : symptômes → cause racine → correctif
Cette section sert à diagnostiquer des modes de défaillance qui reviennent souvent dans de vrais tests CPU. Le motif importe : le symptôme est ce que vous voyez ; la cause racine est ce qui se passe réellement ; le correctif est ce que vous devriez faire ensuite.
1) Symptom: CPU is “low,” but p99 latency is high
- Cause racine : attentes IO, contention sur verrous, ou latence d’une dépendance en aval. Les moyennes d’utilisation CPU cachent les stalls.
- Correctif : Vérifiez
iostat -xz, vérifiez PSI mémoire/IO (si disponible), et profilez pour les verrous. Ajoutez les timings des dépendances à la sortie de vos tests.
2) Symptom: CPU is pegged, but throughput doesn’t increase with more clients
- Cause racine : verrou global, point de sérialisation, ou thread unique surchargé (p.ex. GC, boucle d’événements, logging).
- Correctif :
top -Hpour trouver les threads chauds ; utilisezperf record; réduisez la contention ; réarchitectez la section critique.
3) Symptom: Results vary wildly run-to-run
- Cause racine : mise à l’échelle de fréquence/turbo différente, throttling thermique, steal time, bruit d’arrière-plan, ou effets de warm-up.
- Correctif : Verrouillez le gouverneur, surveillez fréquence/températures, assurez-vous d’absence de steal, exécutez plus longtemps avec warm-up, et logguez les détails d’environnement.
4) Symptom: “Better CPU” wins on average latency but loses on p99
- Cause racine : jitter d’ordonnancement, contention SMT, migrations, ou thrash de cache sous rafale.
- Correctif : Vérifiez migrations, run queue et compteurs perf ; expérimentez avec SMT désactivé ; épinglez des cœurs pour les threads critiques en latence.
5) Symptom: CPU throttling shows up only in containers
- Cause racine : quota cgroup CPU trop bas, période trop courte, ou workload en rafales atteignant les bords du quota.
- Correctif : Augmentez les limites, considérez l’utilisation soignée des requests/limits CPU, ou évitez les quotas stricts pour les services critiques en latence.
6) Symptom: High system CPU and ksoftirqd spikes
- Cause racine : pression d’interruptions/softirq : taux de paquets élevé, paquets petits, conntrack, ou queueing NIC sous-optimal.
- Correctif : Équilibrez les interruptions, ajustez RSS/queues, revoyez les réglages conntrack, et considérez les offloads avec mesure.
7) Symptom: CPU looks saturated only on dual-socket systems
- Cause racine : mémoire distante NUMA ou locks cross-node ; allocations mémoire non locales aux cœurs exécutants.
- Correctif : Épinglez les processus avec
numactl, utilisez des allocateurs NUMA-aware, alignez IRQs et threads, et testez l’échelle par socket.
8) Symptom: You “optimize” by increasing threads and get worse performance
- Cause racine : overhead de context switch, contention sur verrous, thrash de cache. Plus de threads peut réduire l’efficacité CPU.
- Correctif : Réduisez la concurrence ; utilisez l’async quand approprié ; dimensionnez les pools de threads par rapport aux cœurs ; mesurez context switches et migrations.
Listes de vérification / plan étape par étape
Checklist A: A sane CPU comparison plan (bare metal or dedicated VMs)
- Choisissez des scénarios : 1–3 mixes de charge représentant la production (incluez un scénario « mauvais jour »).
- Gelez l’environnement : même OS, noyau, baseline microcode, même config service, mêmes versions des dépendances.
- Contrôlez le comportement CPU : enregistrez gouverneur/turbo ; évitez les différences thermiques ; assurez refroidissement et limites d’alimentation similaires.
- Warm-up : lancez une phase de warm-up (5–15 minutes selon caches/JIT/BD).
- Phase de mesure : durée fixe (10–30 minutes) avec charge offerte stable.
- Collectez les métriques : distribution de latence, débit, taux d’erreur, run queue, PSI, throttling cgroup, snapshot compteurs perf.
- Répétez : au moins 3 runs par scénario ; écartez les outliers évidents seulement avec une raison (pic de steal, événement de déploiement).
- Comparez au SLO : quel CPU atteint votre cible p99 au coût le plus bas et avec le risque opérationnel le plus faible ?
- Documentez le harnais : commandes, configs et un résumé d’une page « ce qui a changé ».
Checklist B: A quick “is it CPU or not?” plan
- Regardez d’abord la latence p99 et le taux d’erreur. Si les utilisateurs ne souffrent pas, n’optimisez pas à panique.
- Vérifiez PSI CPU et run queue. Si les deux sont bas, le CPU n’est pas votre ressource limitante.
- Vérifiez throttling cgroup et steal time. Si présents, corrigez l’environnement avant d’interpréter les résultats.
- Vérifiez le CPU système et les interruptions. Si le temps noyau est élevé, vous luttez sur le chemin noyau/NIC.
- Ensuite seulement profilez l’app. Ne profilez pas à l’aveugle ; apportez des preuves.
Checklist C: Deciding what to buy (or whether to tune instead)
- Si votre workload est sensible au cache/mémoire : priorisez des caches plus grands, une meilleure bande passante mémoire et la compatibilité NUMA plutôt que le boost single-core maximal.
- Si votre workload est embarrassingly parallel : les cœurs comptent, mais surveillez les rendements décroissants dûs aux locks et aux ressources partagées.
- Si votre workload est critique en latence : réduisez les sources de jitter (SMT, migrations, throttling), préférez une performance all-core cohérente, et mesurez le p99 sous rafale.
- Si vous êtes dans des conteneurs : traitez le throttling comme une contrainte de première classe ; un « CPU rapide » peut être rendu lent par la politique.
FAQ
1) Should I use synthetic benchmarks at all?
Utilisez-les comme garde-fous, pas comme décisionnaires. Ils aident à détecter des hôtes cassés (mauvais refroidissement, BIOS incorrect, turbo désactivé). Ils ne remplacent pas les tests de charge réels.
2) How many runs are “enough” for confidence?
Pour des environnements stables, trois exécutions par scénario est un minimum raisonnable. Si la variance est élevée, corriger la variance est le vrai travail ; davantage d’exécutions ne font que quantifier le chaos.
3) What’s the single most useful CPU metric besides utilization?
CPU Pressure Stall Information (PSI) est brutalement pratique. Il répond à : « Les tâches attendent-elles du temps CPU ? » Il corrèle bien avec la latence visible par l’utilisateur sous saturation.
4) Is iowait a reliable signal that I’m IO-bound?
C’est un indice, pas un verdict. iowait peut être bas même quand l’IO est votre goulot (IO asynchrone, threads bloqués ailleurs), et il peut être élevé à cause d’artéfacts d’ordonnancement. Associez-le à iostat -xz et aux timings applicatifs.
5) Should I disable SMT for production?
Parfois. Si votre workload est lourd en verrous, sensible au cache ou critique en p99, SMT peut augmenter le jitter. Si votre workload vise le débit et subit souvent des stalls, SMT peut être un gain. Testez les deux ; ne devinez pas.
6) Why does my CPU benchmark look great for 30 seconds and then gets worse?
Turbo et limites thermiques. Beaucoup de CPU boostent agressivement jusqu’à atteindre des contraintes soutenues de puissance/thermique. Exécutez assez longtemps pour atteindre l’état stable et surveillez fréquence/températures.
7) Can I do real-world CPU testing on shared cloud instances?
Vous pouvez faire des tests de charge, mais la comparaison CPU est risquée. Steal time, voisins bruyants et politiques turbo variables introduisent de la variance. Si nécessaire, utilisez des instances dédiées ou au moins enregistrez %steal et exécutez plus longtemps.
8) What if perf is restricted in my environment?
Dans ce cas, appuyez-vous davantage sur les percentiles de latence externes, PSI, run queue et le profiling côté application. Les compteurs perf sont excellents, mais pas obligatoires pour prendre une décision correcte.
9) How do I know if I’m memory bandwidth bound vs compute bound?
Cherchez une IPC basse et des stalls backend élevés sous charge, plus une sensibilité au pinning NUMA et à la vitesse mémoire. Si la performance s’améliore drastiquement en améliorant la localité, ce n’est pas « juste CPU ».
10) What’s a realistic success criterion for a CPU change?
Définissez-le comme une augmentation de capacité à SLO fixe : « À p99 < X ms, nous pouvons gérer Y% de débit en plus. » C’est le nombre sur lequel vous pouvez piloter une décision business.
Conclusion : prochaines étapes qui fonctionnent réellement
Les tests CPU en conditions réelles ne servent pas à gagner des disputes. Ils servent à acheter (ou ajuster) de la performance que vous pouvez défendre à 3h du matin, quand le cache est froid, le trafic en rafale et qu’un cron fait quelque chose de « utile ».
Faites ceci ensuite :
- Écrivez un scénario basé sur un SLO (charge offerte + objectif p99) et un scénario « mauvais jour ».
- Construisez un harnais reproductible : warm-up, mesure, enregistrez l’environnement, collectez latence + PSI + run queue + throttling.
- Exécutez trois fois sur votre CPU actuel et établissez une baseline fiable.
- Changez une variable (modèle CPU, SMT, quota) et réexécutez. Si vous changez cinq choses, vous n’apprenez rien.
- Décidez à douleur utilisateur égale : choisissez le CPU (ou le tuning) qui respecte le p99 avec le risque opérationnel le plus faible.
Si vous ne retenez qu’une chose : mesurez la charge, pas le matériel. Le matériel n’est que la scène. Votre logiciel est la pièce. Et la production est la critique qui ne dort jamais.