Pourquoi les benchmarks synthétiques mentent (et comment les démasquer)

Cet article vous a aidé ?

Vous avez lancé fio, obtenu un chiffre héroïque, envoyé ça aux achats, et six semaines plus tard la production donne l’impression de patauger dans du sirop.
Maintenant tout le monde regarde « le stockage » comme s’il s’agissait d’un méchant animé.

Les benchmarks synthétiques n’ont pas seulement échoué à prédire la réalité — ils vous ont activement convaincu du contraire.
Ce n’est pas une malchance. C’est un mode de défaillance. Et vous pouvez apprendre à le repérer rapidement, avant la mise en production.

Ce que sont les benchmarks synthétiques (et ce qu’ils supposent en silence)

Un benchmark synthétique est une charge conçue : vous choisissez le mélange lecture/écriture, la taille des blocs, la profondeur de file d’attente, le nombre de threads, l’aléa, la durée et la taille du jeu de données.
C’est un cobaye de laboratoire. Utile. Mais ce n’est pas vos utilisateurs.

Le mensonge n’est pas que les outils synthétiques sont « faux ». Le mensonge, c’est qu’ils sont incomplets par défaut, et que les gens traitent la sortie comme un bon de commande.
La performance du stockage est une propriété système : matériel, firmware, noyau, système de fichiers, pilotes, multipath, réseau, virtualisation et le voisin bruyant dont vous n’aviez pas connaissance.

Le contrat du benchmark que vous n’avez pas lu

Chaque résultat synthétique vient avec des hypothèses non dites. Quand vous lancez « 4k randread QD=64 », vous affirmez :

  • Que votre charge réelle lit en blocs de 4k (ou du moins se comporte de façon similaire au niveau IO).
  • Que votre charge peut maintenir une profondeur de file d’attente de 64 sans blocages en amont.
  • Que votre appli tolère la même distribution de latences, pas seulement la même moyenne.
  • Que vos caches (page cache, cache contrôleur, CDN, cache applicatif) se comportent de la même façon.
  • Que votre chemin IO est identique : même système de fichiers, mêmes options de montage, même chiffrement/compression, mêmes réglages de réplication.

En production, ces hypothèses se font écraser par la réalité. Le benchmark affiche quand même un joli nombre. C’est la partie dangereuse.

Voici le modèle mental qui vous garde honnête : les benchmarks ne mesurent pas la « vitesse du disque ». Ils mesurent un pipeline dans un ensemble très spécifique de conditions.
Changez les conditions, changez l’histoire.

Blague n°1 : les benchmarks synthétiques sont comme des CV — tout le monde a l’air incroyable jusqu’à ce que vous vérifiiez les références et découvriez que « expert en Kubernetes » voulait dire « a ouvert le dashboard une fois ».

Comment les benchmarks synthétiques mentent : mécanismes courants

Les benchmarks « mentent » d’une manière prévisible. Si vous apprenez les schémas, vous pouvez les attraper tôt — parfois juste en regardant le graphique et en posant une question un peu brutale.

1) Cache, cache, cache (et le benchmark qui n’a jamais touché le disque)

Le classique : votre benchmark relit les mêmes données en boucle, et le page cache de l’OS sert tout depuis la RAM.
Ou le cache en écriture d’un contrôleur RAID absorbe les écritures puis les envoie au disque pendant que votre benchmark jubile.

Les charges réelles ne bénéficient généralement pas d’un cache chaud infini. Elles subissent de la localité partielle, des tempêtes d’éviction et des « c’était rapide jusqu’à 09:03 ».
Les benchmarks qui ne dimensionnent pas le jeu de données au-delà du cache ne mesurent que la bande passante mémoire avec des étapes en plus.

2) Fantaisie de la profondeur de file d’attente : QD=64 n’est pas « plus réaliste », c’est « un univers différent »

Une profondeur de file élevée gonfle le débit sur les périphériques qui peuvent réordonner et paralléliser en interne. NVMe adore ça. SATA tolère. Les réseaux parfois font semblant.
Mais les applications ne génèrent peut‑être jamais ce type de concurrence car elles bloquent sur des verrous, des points de commit, des allers-retours RPC ou le CPU.

Si votre appli est mono‑thread ou sérialisée par un fsync WAL de base de données, les résultats QD=64 sont de la trivia.
Vous devez benchmarker à la profondeur de file que votre système peut soutenir de bout en bout.

3) Théâtre de la taille de bloc : « IOPS » sans taille de bloc n’a pas de sens

Le débit séquentiel 1M et les IOPS aléatoires 4k sont deux disciplines différentes. Un système peut exceller dans l’un et être ridicule dans l’autre.
Certains vendeurs aiment montrer la métrique qui les met en valeur. Les ingénieurs aussi, soyons honnêtes.

Si votre charge est « beaucoup de petites lectures de métadonnées » et que vous testez « lecture séquentielle 128k », vous n’avez pas benchmarké votre charge. Vous avez benchmarké vos espoirs.

4) Les moyennes de latence sont de la fan fiction

Les moyennes cachent ce qui déclenche votre astreinte : la latence tail (p95, p99, p99.9) et les pics de latence.
Beaucoup de tests synthétiques rapportent une moyenne qui semble correcte tandis que le p99 est un cauchemar.

Ce qui compte dépend du système. Pour un système de mise en file, le p99 peut provoquer des retries, des timeouts et des défaillances en cascade.
Pour une base de données, quelques longs fsync peuvent bloquer des commits et créer des hordes thundering.

5) « Direct IO » vs IO tamponné : choisissez mal, mesurez le mauvais sous‑système

L’IO tamponné inclut le comportement du page cache, le writeback et les seuils de pages sales. Le Direct IO contourne le page cache et change les contraintes d’alignement.
En production, c’est souvent un mélange : les bases utilisent Direct IO pour les fichiers de données mais s’appuient sur l’IO tamponné ailleurs ; des services peuvent tamponner des lectures sans le vouloir.

Si vous testez avec --direct=1 et que votre charge réalise surtout des lectures tamponnées, vous sous‑estimerez l’effet du cache. Si vous testez tamponné mais attendez un comportement direct, vous surpromettez.
Choisissez le mode IO qui correspond au chemin réel de votre application, pas la préférence du testeur.

6) Système de fichiers et options de montage : le multiplicateur invisible

Ext4 avec data=ordered se comporte différemment de XFS sous pression metadata. ZFS a son propre univers (ARC, recordsize, comportement sync).
Les options de montage comme noatime peuvent supprimer des écritures metadata. Des options comme barriers, mode journal ou discard peuvent déplacer la latence.

Les benchmarks synthétiques tournent sur un système de fichiers vide avec une localité parfaite. La production est fragmentée, a des millions d’inodes et subit une charge concurrente.

7) L’état du périphérique compte : les SSD tout neufs mentent aussi

Beaucoup de SSD donnent de meilleurs résultats quand ils sont vides à cause du cache SLC et du flash propre. En écriture aléatoire steady‑state, la GC et le wear leveling interviennent.
Un « test rapide » peut montrer une fantaisie qui s’effondre après des heures ou des jours de churn réel.

8) Compactage, checksum, chiffrement, réplication : le travail réel coûte des cycles

Les piles de stockage effectuent souvent du travail supplémentaire : checksums, compression, chiffrement au repos, déduplication, réplication, codage d’effacement, snapshots.
Les benchmarks synthétiques qui n’activent pas les mêmes fonctions ne testent pas votre système. Ils testent un autre système.

9) Virtualisation et voisins : vous avez bench marqué l’hôte ; la production vit dans l’immeuble

Sur une infra partagée, vous ne possédez pas le contrôleur, le cache, l’uplink réseau ou le « quelqu’un qui fait une sauvegarde » planifié.
Votre benchmark a peut‑être tourné la nuit, seul. La production tourne à midi, avec mille voisins.

10) Le benchmark devient la charge (et tout s’optimise pour lui)

Les ingénieurs règlent des sysctls et l’ordonnanceur IO jusqu’à ce que fio soit parfait, puis déploient. Le benchmark devient le test d’acceptation.
Le système est maintenant optimisé pour le pattern synthétique, parfois au détriment des charges mixtes ou de la latence.

Blague n°2 : si vous optimisez un système jusqu’à ce que le benchmark soit parfait, félicitations — vous avez réussi à entraîner votre stockage à obtenir un très bon score à un examen standardisé.

Faits et histoire intéressants (oui, ça compte)

  • Les IOPS sont devenus à la mode parce que les HDD étaient mauvais en IO aléatoire. Les discussions professionnelles initiales s’obsédaient sur « combien de lectures 4k aléatoires » parce que les plateaux étaient le goulot.
  • La latence moyenne était historiquement rapportée parce que c’était facile. La latence tail est devenue grand public plus tard, quand les systèmes distribués à grande échelle ont fait du p99 un enjeu de fiabilité.
  • Le cache en écriture d’un contrôleur RAID peut rendre les petits tests d’écriture surnaturels. Souvent c’est une DRAM avec alimentation de secours — rapide jusqu’à ce qu’il faille vider sur les disques.
  • Les SSD ont des modes « rafale courte » (p.ex. cache SLC) qui faussent les courts benchmarks. Beaucoup d’appareils sont conçus pour être flatteurs sur des tests courts et des traces grand public.
  • Le page cache de Linux peut satisfaire des lectures sans toucher au stockage du tout. À moins d’utiliser Direct IO ou de dimensionner correctement le jeu de données, vous mesurez la RAM.
  • Les ordonnanceurs IO ont évolué parce que différents médias nécessitaient des files d’attente différentes. Ce qui aidait les disques rotatifs (réordonner les seeks) peut être sans effet ou nuisible sur NVMe.
  • « fsync() rend les choses réelles » n’est vrai qu’en partie. Les périphériques et contrôleurs peuvent acquitter les écritures avant persistance à moins que barriers, réglages de cache et protection contre perte d’alimentation soient alignés.
  • Les disques cloud annoncent souvent débit/IOPS séparément de la latence. Vous pouvez atteindre des objectifs IOPS tout en ratant des SLO parce que c’est la latence tail que ressentent les utilisateurs.
  • Certaines suites de benchmarks sont devenues des outils d’achat. Lorsqu’un nombre détermine un achat, les vendeurs optimisent pour ce nombre, parfois sans améliorer les charges réelles.

Une idée utile paraphrasée de W. Edwards Deming : quand une métrique devient un objectif, elle cesse d’être une bonne métrique. La version stockage est simple :
si « fio IOPS » est votre but, vous obtiendrez fio IOPS — que votre base de données cesse ou non de rencontrer des timeouts.

Trois mini‑histoires du monde corporate

Mini‑histoire n°1 : L’incident causé par une mauvaise hypothèse

Une entreprise SaaS de taille moyenne est passée du NVMe local à une plateforme de blocs réseau.
Le plan de migration a été validé parce qu’un test synthétique montrait « IOPS similaires » et « plus haut débit ». Le diaporama était impeccable.
La première semaine après la bascule, la latence côté client a augmenté chaque matin. Pas une panne totale, juste un ralentissement progressif : timeouts, retries et un backlog de files en croissance.

L’équipe d’astreinte a regardé CPU et mémoire — OK. Réseau ? OK. Le tableau de bord stockage affichait des IOPS sous la limite annoncée.
« Donc ce n’est pas le stockage », a dit quelqu’un, ce qui est une phrase qui a mis fin à beaucoup de carrières heureuses.
Ils supposaient que la capacité IOPS impliquait stabilité de latence.

La différence cachée était les écritures fsync‑intensives. L’application utilisait une base avec une durabilité stricte au commit.
Le benchmark synthétique était principalement des lectures aléatoires avec une profonde file d’attente et de gros batches. En production c’était beaucoup de petites écritures synchrones avec une concurrence modeste.
Sous la charge du matin, la latence tail du système de stockage a grimpé, et la latence de commit s’est amplifiée en latence de requête.

La correction n’était pas « plus d’IOPS ». C’était l’alignement de la charge : mesurer la latence d’écriture synchrone à la concurrence réelle produite par la base.
Ils ont finalement ajusté le type de volume et affiné les réglages de commit de la base, et ils ont arrêté d’approuver des changements de stockage sur la base d’un seul profil fio.

La leçon était douloureusement simple : on ne raisonne pas à partir d’un mauvais benchmark.
Les incidents de stockage sont souvent causés par un décalage entre les conditions mesurées et les conditions de production, pas par un composant qui « ralentit ».

Mini‑histoire n°2 : L’optimisation qui s’est retournée contre eux

Une équipe plateforme données avait un traitement batch nocturne. Ils étaient fiers de leur discipline de benchmark : chaque nouveau type de nœud passait la même suite synthétique.
Un ingénieur a remarqué que changer l’ordonnanceur IO et augmenter la profondeur de file faisait sauter les chiffres du benchmark.
Ils ont déployé ces réglages sur toute la flotte et ont déclaré victoire.

Deux semaines plus tard, les requêtes interactives de jour ont commencé à montrer de la gigue.
Rien de catastrophique, juste assez de latence tail pour rendre les tableaux de bord désagréables et l’astreinte grognonne.
Le réglage avait optimisé le débit sous forte concurrence, mais avait altéré l’équité en conditions de charge mixte.

Le vrai problème était la contention : l’ordonnanceur et les réglages de queue permettaient au batch IO de dominer les tranches de temps du périphérique.
Les benchmarks synthétiques n’incluaient pas une charge concurrente sensible à la latence, donc ils n’ont jamais montré la famine.
La production, si.

Le retour arrière sur les réglages a amélioré immédiatement le p99 des requêtes, tandis que le débit batch a légèrement diminué.
L’équipe a appris à benchmarker des scénarios « mode mixte » : un job poussant le débit pendant qu’un autre sonde la latence.
Ils ont aussi appris qu’un réglage qui embellit un benchmark peut être une taxe sur l’expérience utilisateur.

Mini‑histoire n°3 : La pratique ennuyeuse mais correcte qui a sauvé la situation

Une équipe services financiers gérait une plateforme de stockage avec un contrôle strict des changements. Ils n’étaient pas glamour à ce sujet.
Avant toute mise à jour, ils capturaient une baseline : versions firmware des périphériques, réglages de queue, options de montage du système de fichiers et un petit ensemble de tests représentatifs de la charge.
Ils stockaient les résultats avec timestamps et versions du noyau. C’était ennuyeux. C’était aussi une machine à remonter le temps.

Après une mise à jour de noyau de routine, ils ont constaté une augmentation subtile de la latence write p99. Les utilisateurs l’ont à peine remarqué — jusqu’à la charge de fin de mois.
Parce qu’ils avaient des baselines, ils ont pu dire « cette dérive a commencé exactement après la mise à jour », pas « le stockage est étrange ces derniers temps ».
Cela a restreint la recherche aux changements de la pile IO, pas à un vague « peut‑être que le matériel lâche ».

Ils ont utilisé leurs jobs fio de référence plus des métriques applicatives pour confirmer la régression.
Puis ils ont comparé les réglages de la couche bloc et trouvé qu’un défaut avait changé dans leur environnement (comportement de queueing et sélection d’ordonnanceur différent sur le nouveau noyau).
Ils ont épinglé le comportement précédent et planifié un suivi contrôlé pour tester correctement les nouveaux defaults.

La fin de mois s’est déroulée sans drame. Personne n’a fêté. C’est le but.
La pratique qui les a sauvés n’était pas un benchmark magique ; c’était une mesure répétable, l’attribution de changements et le refus de « tuner à l’aveugle ».

Méthode de diagnostic rapide : que vérifier en premier/deuxième/troisième

Quand « le stockage est lent », vous avez besoin d’un entonnoir rapide. Pas d’un projet de benchmark d’une semaine. Voici l’ordre qui trouve généralement la vérité rapidement.

Premier : prouvez s’il s’agit de latence, débit ou saturation

  • Regardez la latence tail (p95/p99), pas seulement la moyenne.
  • Vérifiez l’utilisation : le périphérique est‑il à 100 % ? La file augmente‑t‑elle ?
  • Corrélez avec la charge : la concurrence a‑t‑elle changé ? Une sauvegarde a‑t‑elle démarré ? Un compactage a‑t‑il débuté ?

Second : localisez la couche goulot

  • Application : verrous, pauses GC, exhaustion des pools de connexion, fréquence des fsync.
  • Système de fichiers : pression du journal, tempêtes metadata, fragmentation, options de montage.
  • Couche bloc : profondeur de queue, ordonnanceur, comportement de fusion, throttling.
  • Périphérique : cliff d’écriture steady‑state SSD, quirks firmware, throttling thermique.
  • Réseau / fabric stockage : retransmissions, congestion, multipath instable.
  • Virtualisation/environnement partagé : voisin bruyant, CPU steal de l’hôte, politiques de throttling.

Troisième : reproduisez en sécurité avec un micro‑test représentatif

  • Choisissez un test qui correspond à votre taille IO, mélange lecture/écriture, comportement sync et concurrence.
  • Exécutez‑le à côté de la charge production si possible (prudemment), ou rejouez des traces dans un clone de staging.
  • Validez avec les métriques système et applicatives. Si elles divergent, faites confiance à l’application.

Ce playbook fonctionne parce qu’il est sceptique : il suppose que le symptôme peut venir en amont, en aval, ou être auto‑infligé par la mythologie du benchmarking.

Tâches pratiques : commandes, ce que la sortie signifie, et la décision que vous prenez

Voici les tâches que j’exécute réellement quand quelqu’un me file un résultat de benchmark ou un ticket « stockage lent ».
Chaque tâche inclut : commande, sortie d’exemple, ce que ça signifie et la décision qu’elle guide.

Task 1: Identify the block devices and their topology

cr0x@server:~$ lsblk -o NAME,MODEL,SIZE,ROTA,TYPE,MOUNTPOINTS
NAME        MODEL             SIZE ROTA TYPE MOUNTPOINTS
nvme0n1     Samsung SSD      1.8T    0 disk 
├─nvme0n1p1                  512M    0 part /boot
└─nvme0n1p2                  1.8T    0 part /
sda         ST8000NM0045     7.3T    1 disk 
└─sda1                        7.3T    1 part /mnt/archive

Signification : ROTA=0 indique du solide ; ROTA=1 indique du rotatif. Le modèle donne un indice sur le contrôleur et la classe.
Savoir si vous êtes sur NVMe vs SATA vs HDD change ce à quoi ressemble le « bon » et quel comportement de queue est pertinent.

Décision : Choisissez des profils de benchmark adaptés à la classe de périphérique. N’utilisez pas des profondeurs NVMe sur un HDD unique et dites que c’est du « matériel injuste ».

Task 2: Check filesystem and mount options (the silent performance knobs)

cr0x@server:~$ findmnt -no SOURCE,TARGET,FSTYPE,OPTIONS /
/dev/nvme0n1p2 / ext4 rw,relatime,errors=remount-ro

Signification : Les options de montage indiquent si vous payez des taxes metadata (atime), quel est votre comportement de journalisation, et si discard est activé.

Décision : Si votre environnement de benchmark utilise des options de montage différentes de la production, arrêtez. Alignez‑les, puis retestez.

Task 3: Confirm you’re not accidentally benchmarking RAM via page cache

cr0x@server:~$ grep -E 'MemTotal|MemAvailable|Cached' /proc/meminfo
MemTotal:       263824032 kB
MemAvailable:   221443104 kB
Cached:         78455232 kB

Signification : Une grande valeur Cached plus un jeu de données plus petit que la RAM signifie souvent que les lectures viendront du cache.

Décision : Dimensionnez le jeu de données au‑delà de la RAM (ou utilisez Direct IO). Si vous ne pouvez pas, indiquez explicitement que vous avez mesuré une performance mise en cache, pas du disque.

Task 4: Check current writeback pressure and dirty page thresholds

cr0x@server:~$ sysctl vm.dirty_background_ratio vm.dirty_ratio
vm.dirty_background_ratio = 10
vm.dirty_ratio = 20

Signification : Ces paramètres contrôlent la quantité de données sales pouvant s’accumuler avant que le writeback n’impose du throttling.
Les benchmarks qui écrivent en tampon peuvent sembler excellents jusqu’à ce que le kernel décide qu’il est temps de flush, puis la latence pique.

Décision : Si vous voyez des cliffs périodiques de latence dans des tests d’écriture tamponnée, reproduisez avec Direct IO ou ajustez la durée du test et surveillez le comportement de writeback.

Task 5: See if the device is saturated (utilization and queue depth)

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

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          12.10    0.00    4.20    6.50    0.00   77.20

Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz  aqu-sz  %util
nvme0n1         820.0  52480.0     0.0   0.0    2.10    64.0    610.0  78144.0     0.0   0.0    8.40   128.1    5.10  92.00

Signification : %util proche de 100% suggère une saturation. aqu-sz indique l’arriéré de la file. r_await/w_await montrent la latence incluant le temps en file.

Décision : Si saturé, il vous faut soit plus de périphériques, une meilleure parallélisation, ou moins de travail par IO (choices de compression, batching, caching). Si non saturé mais latence élevée, cherchez firmware, throttling ou blocages en amont.

Task 6: Measure per-process IO behavior (who’s actually doing it)

cr0x@server:~$ pidstat -d 1 3
Linux 6.5.0 (server) 	01/12/2026 	_x86_64_	(64 CPU)

# Time   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
12:00:01 999     18422      0.00  41280.00      0.00      45  postgres
12:00:01 0        2211      0.00   5120.00      0.00       6  rsync

Signification : Vous pouvez distinguer « le stockage est lent » de « un seul processus fait beaucoup »,.
iodelay est un indicateur approximatif du temps passé en attente IO.

Décision : Si un job en arrière‑plan domine, planifiez‑le autrement ou limitez‑le. Si le service principal attend, concentrez‑vous sur la latence et le chemin de durabilité.

Task 7: Confirm IO scheduler and queue settings (especially after kernel updates)

cr0x@server:~$ cat /sys/block/nvme0n1/queue/scheduler
[none] mq-deadline kyber bfq

Signification : L’ordonnanceur sélectionné est entre crochets. NVMe utilise souvent none efficacement ; d’autres périphériques peuvent bénéficier de mq-deadline sous charges mixtes.

Décision : Ne paramétrez pas aveuglément pour le débit du benchmark. Si vous avez des charges sensibles à la latence, testez sous contention et choisissez l’ordonnanceur qui préserve la latence tail.

Task 8: Spot device throttling or errors in kernel logs

cr0x@server:~$ dmesg -T | tail -n 12
[Mon Jan 12 11:58:10 2026] nvme nvme0: failed command: WRITE, cmdid 123 qid 4
[Mon Jan 12 11:58:10 2026] nvme nvme0: status: { DNR }
[Mon Jan 12 11:58:11 2026] EXT4-fs (nvme0n1p2): warning: mounting fs with errors, running e2fsck is recommended

Signification : Les chiffres du benchmark sont sans objet si le périphérique génère des erreurs ou si le système de fichiers est compromis.
Les pics de latence peuvent être des retries d’erreurs, des resets ou des modes dégradés.

Décision : Arrêtez les tests de performance. Stabilisez le système : vérifiez SMART/NVMe logs, câblage/contrôleur, et corrigez les erreurs du système de fichiers.

Task 9: Run a direct IO latency-focused fio test (more honest for many DB paths)

cr0x@server:~$ fio --name=lat4k --filename=/mnt/testfile --size=8G --direct=1 --rw=randread --bs=4k --iodepth=4 --numjobs=4 --time_based --runtime=60 --group_reporting
lat4k: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=psync, iodepth=4
...
  read: IOPS=42000, BW=164MiB/s (172MB/s)(9840MiB/60001msec)
    slat (nsec): min=1800, max=21000, avg=5400.10, stdev=1100.30
    clat (usec): min=45, max=9200, avg=90.20, stdev=40.10
     lat (usec): min=49, max=9210, avg=96.10, stdev=40.50
    clat percentiles (usec):
     |  1.00th=[   55],  5.00th=[   62], 10.00th=[   68], 50.00th=[   84]
     | 90.00th=[  112], 95.00th=[  135], 99.00th=[  240], 99.90th=[ 1200]

Signification : Les IOPS sont correctes, mais ce sont les percentiles qui racontent la vraie histoire. Un p99.9 à 1,2ms peut être acceptable — ou casser un SLO si votre chemin de requête empile plusieurs IO.

Décision : Si la latence tail est élevée, réduisez la contention, baissez la profondeur de queue, examinez la GC/le throttling, ou migrez vers une classe de périphérique avec une meilleure latence steady‑state.

Task 10: Test sequential throughput with realistic block size and concurrency

cr0x@server:~$ fio --name=seqread --filename=/mnt/testfile --size=16G --direct=1 --rw=read --bs=1M --iodepth=8 --numjobs=2 --time_based --runtime=60 --group_reporting
seqread: (g=0): rw=read, bs=(R) 1024KiB-1024KiB, (W) 1024KiB-1024KiB, (T) 1024KiB-1024KiB, ioengine=libaio, iodepth=8
...
  read: IOPS=980, BW=980MiB/s (1027MB/s)(58800MiB/60001msec)

Signification : Utile pour les scans en masse, sauvegardes, log shipping. Mais ça ne prédit pas la performance IO aléatoire petite taille.

Décision : Utilisez‑le pour planifier la capacité des opérations bulk et des fenêtres de maintenance, pas pour justifier un SLO de latence base de données.

Task 11: Verify discard/TRIM settings (can create latency spikes)

cr0x@server:~$ findmnt -no TARGET,OPTIONS /mnt/test
/mnt/test rw,relatime,discard

Signification : Le discard continu peut ajouter un surcoût sur certains périphériques. Certains environnements préfèrent un fstrim périodique.

Décision : Si vous voyez des pics de latence périodiques alignés avec des discards, passez au trim planifié et retestez.

Task 12: Check NVMe SMART and media health (steady-state matters)

cr0x@server:~$ sudo nvme smart-log /dev/nvme0
Smart Log for NVME device:nvme0 namespace-id:ffffffff
critical_warning                    : 0x00
temperature                         : 47 C
available_spare                     : 100%
percentage_used                     : 3%
data_units_read                     : 12,345,678
data_units_written                  : 9,876,543
media_errors                        : 0
num_err_log_entries                 : 0

Signification : La température et les compteurs d’erreurs peuvent expliquer le throttling et les retries. percentage_used donne un indicateur d’usure approximatif.

Décision : Si la température est élevée ou des erreurs apparaissent, réparez le matériel / la circulation d’air avant d’accuser la charge ou un « mauvais réglage ».

Task 13: See whether your “random” test is actually hitting the same blocks (locality check)

cr0x@server:~$ fio --name=randcheck --filename=/mnt/testfile --size=64G --direct=1 --rw=randread --bs=4k --iodepth=1 --numjobs=1 --runtime=30 --time_based --group_reporting --randrepeat=0
randcheck: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=psync, iodepth=1
...
  read: IOPS=8200, BW=32.0MiB/s (33.6MB/s)(960MiB/30001msec)

Signification : --randrepeat=0 évite de répéter la même séquence aléatoire entre runs. Une plus grande --size aide à vaincre le cache.

Décision : Si la performance s’effondre quand vous augmentez le jeu de données, votre résultat antérieur était probablement gonflé par le cache.

Task 14: Confirm persistence semantics (write cache setting at the device level)

cr0x@server:~$ sudo hdparm -W /dev/sda
/dev/sda:
 write-caching =  1 (on)

Signification : Le write cache peut améliorer la vitesse mais change la durabilité sauf si vous avez une protection contre perte d’alim et des barriers corrects.

Décision : Pour des systèmes exigeant une durabilité stricte, assurez‑vous que la pile est correctement configurée et testez avec des patterns fsync/sync — sinon vous mesurez des écritures « acquittées », pas persistantes.

Task 15: Measure file-level latency from the filesystem perspective

cr0x@server:~$ strace -T -e trace=fsync -p 18422 -s 0
strace: Process 18422 attached
fsync(57)                                = 0 <0.012341>
fsync(57)                                = 0 <0.089120>
fsync(57)                                = 0 <0.007882>

Signification : Ces timings sont la réalité de l’application. Si fsync prend parfois 90ms, votre rapport stockage « moyenne 1ms » est hors sujet.

Décision : Si la latence fsync est épisodique, cherchez des tempêtes de writeback, GC périphérique, contention ou problèmes du chemin de durabilité (barriers, flush cache, stockage réseau).

Erreurs courantes : symptôme → cause racine → correctif

1) Symptom: “Benchmark says 500k IOPS, but the app times out”

Root cause: Queue depth and concurrency in the benchmark exceed what the app can sustain; tail latency is the real limiter.

Fix: Benchmark at realistic concurrency (threads, iodepth). Track p95/p99. Validate with app-level timing (fsync, query latency).

2) Symptom: “Reads are insanely fast, then suddenly slower after a few minutes”

Root cause: Page cache warming or controller cache absorbing; dataset too small or repeated.

Fix: Use direct IO or dataset larger than RAM; disable randrepeat; run longer tests and watch cache hit ratios where possible.

3) Symptom: “Writes look great in a 30-second test, terrible after an hour”

Root cause: SSD SLC cache / short-term buffering hides steady-state garbage collection and write amplification.

Fix: Precondition the device (fill and sustain writes), run long time-based tests, and measure steady-state latency percentiles.

4) Symptom: “Throughput is high, but interactive latency is awful during batch jobs”

Root cause: Fairness/priority issues: scheduler, queueing, or lack of IO isolation. Batch dominates device time.

Fix: Test mixed workloads; apply cgroup IO controls or workload scheduling; choose scheduler for latency fairness, not peak throughput.

5) Symptom: “Same benchmark on two ‘identical’ nodes differs massively”

Root cause: Firmware differences, PCIe slot/link width differences, thermal throttling, background scrubs, or filesystem state.

Fix: Inventory firmware/kernel settings; verify link speed/width; check temperatures; confirm background tasks; align filesystem/mount options.

6) Symptom: “Random write IOPS is fine, but fsync is slow”

Root cause: Benchmark isn’t issuing sync writes; durability path (cache flush, barriers, journal) is the bottleneck.

Fix: Use fio with --fsync=1 or sync engines; measure fsync directly in the app; validate controller write cache and power-loss protection assumptions.

7) Symptom: “Storage looks idle, yet latency is high”

Root cause: Upstream stalls (locks, CPU throttling, network retransmits) or intermittent device resets/retries.

Fix: Correlate app wait reasons; check dmesg; verify network stats if remote; inspect per-process IO waits and CPU steal time.

Listes de contrôle / plan pas à pas pour un benchmarking honnête

Plan pas à pas : de « beaux chiffres » à « résultats exploitables »

  1. Écrivez la question. Choisissez‑vous du matériel ? Validez‑vous une migration ? Déboguez‑vous une régression de latence ? Différentes questions exigent des tests différents.
  2. Extrayez les caractéristiques de la charge. Distribution des tailles IO, mélange lecture/écriture, comportement sync, concurrence, taille du working set, rafales, et latence tail acceptable.
  3. Alignez la pile. Même système de fichiers, mêmes options de montage, chiffrement/compression, réplication, noyau et pilotes que la production. Pas de « à peu près ».
  4. Décidez de ce que vous allez rapporter. Incluez toujours la taille de bloc, la profondeur de queue, numjobs, direct vs buffered, durée, taille du dataset et percentiles.
  5. Éliminez la vitesse factice. Jeu de données plus grand que les caches, --randrepeat=0, exécutez assez longtemps pour atteindre l’état stable, et évitez de vous vanter des « 10 premières secondes ».
  6. Mesurez à la fois signaux système et applicatifs. iostat/pidstat/histogrammes de latence plus latence des requêtes applicatives et taux d’erreur.
  7. Testez des charges mixtes. Un job de throughput plus une sonde de latence. La production n’est pas un seul job fio tournant seul à minuit.
  8. Exécutez au moins trois fois. Si les résultats varient énormément, votre système est instable ou votre méthode est mauvaise. Dans les deux cas, ne livrez pas.
  9. Faites une baseline et stockez les artefacts. Conservez les fichiers de job fio, versions du noyau/firmware, sysctls et sorties brutes. Le vous du futur en aura besoin pendant un incident.
  10. Translatez les résultats en décisions. « p99 fsync < 5ms à 200 commits/s » est une décision. « 1M IOPS » est un poster.

Checklist de revue de benchmark (utilisez‑la pour attraper les mensonges d’un rapport externe)

  • Le rapport inclut‑il la taille de bloc, le mix lecture/écriture, l’iodepth, numjobs, la durée et la taille du dataset ?
  • Inclut‑il p95/p99/p99.9 de latence, pas seulement la moyenne ?
  • Le dataset était‑il plus grand que la RAM et le cache contrôleur ?
  • Le Direct IO a‑t‑il été utilisé de façon appropriée pour la charge cible ?
  • Le périphérique a‑t‑il été préconditionné pour des écritures steady‑state ?
  • Le système de fichiers était‑il rempli/fragmenté comme en production, ou s’agissait‑il d’un volume tout neuf ?
  • Y avait‑il une charge concurrente, ou le benchmark a‑t‑il tourné en isolation ?
  • Y a‑t‑il des logs noyau montrant erreurs/retries pendant le run ?
  • Les résultats s’alignent‑ils avec les timings applicatifs ?

Ce qu’il faut éviter (opinions tranchées, apprises à la dure)

  • N’acceptez pas un benchmark à nombre unique. Aucun chiffre IOPS unique ne survit au contact avec des charges réelles.
  • Ne touchez pas aux réglages en vous basant uniquement sur un test synthétique. Le tuning change le comportement sous contention ; si vous n’avez pas testé la contention, vous n’avez pas testé le risque.
  • Ne benchmarkez pas sur un système avec des tâches d’arrière‑plan inconnues. Scrubs, rebuilds, sauvegardes et indexations produiront une « variance mystérieuse ». Ce n’est pas mystérieux.
  • N’ignorez pas la latence tail. C’est littéralement ce que ressentent les utilisateurs et ce que les systèmes distribués amplifient.

FAQ

1) Les benchmarks synthétiques sont‑ils inutiles ?

Non. Ils sont excellents pour des comparaisons contrôlées et pour isoler des variables. Ils sont inutiles quand on les traite comme une promesse du comportement en production sans aligner la charge, la pile et la contention.

2) Quelle est la raison principale pour laquelle les résultats de benchmark ne correspondent pas à la production ?

Mismatch entre cache et concurrence. Le benchmark tourne souvent avec un dataset qui tient dans le cache et une profondeur de file que l’application ne peut pas soutenir.
Le résultat est un débit gonflé et une latence tail cachée.

3) Dois‑je toujours utiliser Direct IO avec fio ?

Si votre charge cible utilise Direct IO (beaucoup de bases de données le font pour les fichiers de données), oui. Si votre charge dépend du page cache (beaucoup de services web le font), testez aussi l’IO tamponné.
La réponse honnête est : testez le chemin que vous exécutez réellement.

4) Pourquoi un iodepth plus élevé augmente les IOPS mais parfois dégrade la latence ?

La profondeur de file augmente le parallélisme et les opportunités de réordonnancement, ce qui booste le débit. Mais elle augmente aussi le délai de mise en file, surtout en saturation.
La latence tail est l’endroit où la douleur apparaît.

5) Mon vendeur m’a donné des chiffres de benchmark. Comment les valider rapidement ?

Relancez un ensemble minimal : un test de latence 4k aléatoire avec iodepth/numjobs réalistes, un test d’écriture synchrone 4k axé fsync, et un test de débit séquentiel.
Assurez‑vous que la taille du dataset dépasse le cache, et rapportez les percentiles.

6) Que signifie « steady state » pour le benchmarking SSD ?

Cela signifie la performance après que l’appareil a été suffisamment écrit pour que la garbage collection et le wear leveling soient actifs.
Les tests courts sur un périphérique propre mesurent souvent le comportement de cache de rafale, pas la performance à long terme.

7) Comment benchmarker honnêtement un stockage distribué (attache réseau) ?

Incluez le réseau et la pile cliente. Mesurez les retransmissions, le CPU et la latence tail.
Exécutez les tests depuis plusieurs clients simultanément, car les systèmes distribués se comportent souvent différemment sous une charge fan‑in que sous un seul client.

8) Pourquoi mes résultats fio diffèrent entre exécutions ?

Causes courantes : cache, tâches en arrière‑plan, throttling thermique, comportement GC du périphérique et état du système de fichiers (fragmentation, espace libre).
Si la variance est élevée, considérez‑la comme un signal : votre environnement n’est pas contrôlé, ou votre stockage est instable.

9) Quelles métriques dois‑je mettre dans un rapport de benchmark pour éviter les abus ?

Incluez : définition de la charge (rw mix, bs, iodepth, numjobs, direct/buffered), taille du dataset, durée, débit/IOPS, percentiles de latence et contexte système (noyau, système de fichiers, options de montage).
Si l’un de ces éléments manque, quelqu’un va « utilement » mal interpréter le résultat.

Conclusion : prochaines étapes qui tiennent en production

Les benchmarks synthétiques ne mentent pas parce qu’ils sont malveillants. Ils mentent parce qu’ils sont étroits, et les gens sont optimistes.
Les systèmes de production ne sont pas optimistes. Ils sont occupés, concurrents, désordonnés et pleins de travaux d’arrière‑plan que vous avez oubliés.

Prochaines étapes qui aident vraiment :

  • Choisissez 3–5 profils de benchmark qui correspondent à votre charge réelle (incluant les écritures sync si la durabilité vous importe).
  • Exigez des percentiles de latence et la taille du dataset dans chaque rapport de performance.
  • Faites une baseline avant les changements, et conservez les artefacts pour attribuer les régressions à un changement précis.
  • Testez des charges mixtes, pas seulement « fio seul sur un volume vide ».
  • En cas de doute, faites confiance aux timings applicatifs plutôt qu’au tableau de bord stockage.

L’objectif n’est pas d’interdire les benchmarks synthétiques. L’objectif est d’empêcher qu’ils valident des décisions qu’ils n’ont pas mesurées.
Les benchmarks sont des outils. La production est l’examen. Agissez en conséquence.

← Précédent
WordPress lent : identifier le goulot d’étranglement étape par étape (serveur, plugins ou base de données)
Suivant →
CSS pour contenu Markdown : des valeurs par défaut sensées qui ne cassent pas la production

Laisser un commentaire