
Lent sans raison ? Il relit : le piège du cache KV
Votre modèle écrit à 54 jetons par seconde, le banc est content, et pourtant un agent de code attend des minutes avant son premier mot. Le coupable n'est presque jamais le modèle. C'est la première phase de chaque réponse, la lecture du prompt, et un réglage de cache qui la casse sans bruit.
Comment ces chiffres ont été obtenus. Mesurés sur nos propres machines (voir le protocole), en général en un seul passage, vitesses relevées par le serveur lui-même ; nous ne publions pas les journaux bruts. Ce sont des ordres de grandeur sur ce matériel, pas des garanties sur le vôtre. Les chiffres extérieurs sont accompagnés de leur source.
Ce que vous y gagnez. La réponse à « pourquoi mon agent local est si lent » en deux minutes de mesure, et un correctif d'un seul réglage : sur notre carte, un prompt d'agent de 23 400 jetons est passé de 18 minutes à 17 secondes.
1. Deux vitesses, pas une
Un modèle répond en deux phases. D'abord il lit tout le prompt (le traitement du prompt, ou prefill), puis il écrit la réponse jeton par jeton (la génération). La plupart des bancs et des démos de chat ne mesurent que la seconde. Un agent envoie un long prompt à chaque tour : c'est la première phase que vous attendez.
2. Mesurer la lecture, sur un long prompt
llama.cpp affiche les deux vitesses à la fin de chaque requête. Envoyez un long prompt (quelques milliers de jetons : collez un gros fichier et demandez un résumé), puis lisez le journal du serveur :
prompt eval time = 5830 ms / 7758 tokens (1330 tokens per second)
eval time = 3700 ms / 200 tokens ( 54 tokens per second)
La première ligne, c'est la lecture. Sur une carte récente avec flash attention, attendez des centaines à des milliers de jetons par seconde. Quelques dizaines, c'est un problème.
3. Le réglage qui casse tout
Pour gagner de la mémoire, llama.cpp peut compresser son cache clé/valeur : -ctk pour les clés, -ctv pour les valeurs. Compresser les valeurs en q4_0 ressemble à un gain gratuit. Ce n'en est pas un : flash attention se désarme en silence. Le serveur accepte -fa on, ne dit rien dans son journal, même en verbosité maximale, et lit 21 fois plus lentement.
Le gain était d'environ 33 Ko par jeton au lieu de 50 : 1,8 Go sur une fenêtre de 106k. Payé vingt et une fois le temps de lecture, c'est une mauvaise affaire dans tous les cas.
llama-server -m modele.gguf -fa on -ctk q8_0 -ctv q8_0 # ceci, et rien de plus bas pour V
4. Reconnaître la signature
Sans flash attention, la lecture ralentit à mesure qu'elle avance : chez nous 212 jetons par seconde sur les 2 048 premiers jetons, puis 82, 51, 37. Une vitesse qui chute avec la position dans le prompt, c'est une attention non optimisée. Une vitesse plate mais basse, c'est autre chose (mémoire, déchargement sur le processeur).
Pièges
L'autre cause de relecture, c'est un cache de prompt coupé. Avec --cache-ram 0, le serveur oublie le début de la conversation et le relit à chaque tour (voir le 177B sur deux cartes).
Claude Code sur un modèle local ? Pour un nom de modèle qu'il ne connaît pas, il suppose une fenêtre de 200 000 jetons et compacte trop tard ; et la compaction automatique ne s'arme qu'au-delà de 100 000. Donnez-lui CLAUDE_CODE_MAX_CONTEXT_TOKENS égal à la vraie fenêtre de votre serveur (voir Claude Code, deux façons).
Sources
Utile ? Dites-nous ce qu'il faut mesurer ensuite.