Aller au contenu principal

Module 6 — Mesurer la latence et le débit

Un modèle qui répond « vite » dans une démo peut engorger un poste en production. La différence tient à des grandeurs mesurables que trop de projets ignorent jusqu'à la mise en service. Ce module fixe les deux grandeurs qui comptent, l'unique protocole qui les rend comparables, et les valeurs typiques à attendre sur un poste modeste.

Les deux grandeurs qui comptent

Temps au premier jeton (« time to first token » ou TTFT). Le délai entre l'envoi de la requête et l'apparition du premier caractère de la réponse. C'est la grandeur que l'utilisateur ressent comme « le modèle a compris ». Sous 300 ms, l'assistant paraît instantané ; entre 300 ms et 1 s, il paraît réfléchi ; au-delà, il paraît lent.

Jetons par seconde (« tokens per second » ou TPS). Le débit de génération une fois le premier jeton produit. Un TPS de 30 correspond à peu près à la vitesse de lecture d'un humain rapide ; à 60 TPS, la réponse défile plus vite qu'on ne peut la lire. Pour un résumé de trois lignes (~80 jetons), passer de 20 à 60 TPS fait passer la génération de 4 s à 1,3 s.

Ces deux grandeurs sont indépendantes : le TTFT dépend surtout de la longueur des entrées et de la vitesse de la passe avant sur le contexte (« prefill »), le TPS dépend de la vitesse d'un pas de décodage. Une mesure qui les confond — par exemple, un « temps total » sans distinction — est inutilisable pour diagnostiquer un problème.

L'effet caché de la taille de lot

Sur un serveur multi-utilisateurs, la taille de lot (« batch size ») change tout. Traiter huit requêtes en parallèle sur un GPU multiplie le débit total par un facteur 5 à 7, parce que la bande passante mémoire du GPU est mieux amortie. C'est la promesse du continuous batching utilisé par vLLM, TGI et ONNX Runtime.

Sur un poste local mono-utilisateur — notre fil rouge — cet effet n'existe pas : les requêtes arrivent une à une. La latence unitaire est donc la seule grandeur pertinente, et il ne sert à rien de comparer un chiffre de débit obtenu à batch 16 sur un H100 avec un chiffre de latence à batch 1 sur un portable. Beaucoup de billets de blog font cette confusion et publient des « TPS » impossibles à reproduire.

Un protocole de mesure honnête

Cinq règles suffisent à rendre une mesure reproductible.

Distribution d'entrées fixée. Nos 200 tickets d'évaluation, dans un ordre déterministe. Longueur d'entrée, distribution de longueur de sortie et gabarit de discussion identiques à ceux de la production visée.

Chauffe préalable. Les cinq premières requêtes sont écartées : le premier appel charge le modèle en mémoire, le second remplit les caches, les suivants stabilisent la fréquence CPU/GPU. Sans chauffe, la médiane est faussée par un pic initial.

Deux GPU différents. Mesurer sur le matériel cible et sur un matériel plus modeste. Un modèle qui répond en 400 ms sur RTX 4070 tourne à 900 ms sur GTX 1660, à 2,5 s sur CPU seul. Publier un seul chiffre sans le matériel associé n'a pas de sens.

Le 95ᵉ centile, pas la moyenne. Un modèle qui répond en 300 ms neuf fois sur dix, puis en 4 s sur la dixième requête, a une moyenne de 670 ms qui ment. Le 95ᵉ centile capture la latence de la mauvaise nouvelle, celle que les utilisateurs remarquent.

Le même moteur. Comparer llama.cpp à Ollama à ONNX Runtime sur le même modèle donne parfois des écarts de 30 %, parce que les paramètres par défaut diffèrent (nombre de fils, taille de bloc, précision des activations). Une comparaison honnête fixe le moteur ou publie la valeur pour chaque moteur.

Ce qu'on mesure vraiment sur le fil rouge

Notre modèle Ministral 3B quantifié en Q4_K_M donne, mesuré sur 200 tickets réels, avec la chauffe et les règles ci-dessus :

MatérielTTFT p95TPS médianLatence totale p95 (80 jetons)
CPU seul (i5 récent, 8 fils)620 ms127,3 s
GPU d'entrée de gamme (RTX 3050 6 Go)180 ms422,1 s
GPU milieu de gamme (RTX 4070 12 Go)90 ms781,1 s
MacBook Air M3 16 Go via MLX70 ms651,3 s

Deux enseignements. Sur CPU seul, un modèle 3B est utilisable pour un usage occasionnel, pas pour un flux continu d'agents de support. Sur GPU d'entrée de gamme à 200 euros, la latence tombe sous les deux secondes, ce qui rend l'assistant confortable en production. C'est cette configuration qui devient la recommandation matérielle du projet.

Le piège du contexte long

Deux mesures identiques peuvent cacher un comportement radicalement différent sur des entrées de 2 000, 8 000 ou 32 000 jetons. Le TTFT croît quadratiquement avec la longueur du contexte pour les modèles à attention complète, linéairement pour ceux à attention à fenêtre glissante. Sur un ticket de 300 jetons, le prefill dure 40 ms ; sur un fil de discussion complet de 8 000 jetons, il peut atteindre 3 s sur le même matériel.

Mesurer uniquement sur des requêtes courtes donne donc une image optimiste. Le protocole doit inclure des exemples représentatifs des longueurs réellement rencontrées, y compris les cas rares mais lourds.

Le chiffre publié n'est jamais celui que vous obtiendrez

Toute publication de vitesse d'inférence — billet de blog, fiche technique, communiqué — est mesurée dans des conditions optimales : batch précis, contexte court, matériel choisi pour flatter le résultat. Ces chiffres sont utiles comme borne supérieure, jamais comme prévision. Seule votre propre mesure sur votre propre tâche compte.

En résumé

  • Deux grandeurs séparées : TTFT (perception de réactivité) et TPS (vitesse de génération) ; les mesurer ensemble n'a pas de sens.
  • Le 95ᵉ centile remplace la moyenne, la chauffe écarte les premières requêtes, la taille de lot est fixée à 1 sur un poste mono-utilisateur.
  • Sur notre fil rouge, un modèle 3B quantifié tourne confortablement sur un GPU à 200 euros, marginalement sur CPU seul ; MLX sur Apple Silicon rivalise avec un GPU discret.
  • Le contexte long casse les latences ; mesurer sur des entrées de la taille réelle, pas sur des messages courts choisis.

Module suivant : affiner le petit modèle en LoRA sur nos données de tickets, pour gagner les derniers points d'exactitude sans passer au grand modèle.