Retour au blog

DépannageGuide

Traitement du prompt llama.cpp 35x plus lent ? Corrigez le cache KV q5_1

Flash attention plus un cache KV q5_1 peuvent pousser l'attention sur le CPU. Comment le confirmer, le corriger avec q8_0, et quand une API est plus simple.

Par CheapTokens TeamPublié le 6 min de lecture

Terminal montrant le GPU à 0 pour cent et le CPU à 930 pour cent pendant le traitement du prompt llama.cpp
Sur cette page
  1. À quoi cela ressemble à l'écran
  2. Pourquoi un cache plus petit ralentit autant
  3. Vérifiez en deux minutes
  4. Correctif 1 : utilisez q8_0 (ou recompilez)
  5. Quand l'optimisation cesse d'en valoir la peine
  6. L'ordre à suivre

Votre modèle local répond bien, mais chaque prompt long reste maintenant bloqué une minute et demie pendant que le GPU ne fait rien. Si vous exécutez llama.cpp avec -fa on et un cache KV q5_1, c'est probablement la cause : la build CUDA par défaut n'a pas de noyau flash-attention pour ce type de cache, donc l'étape d'attention s'exécute discrètement sur le CPU. Passer le cache à q8_0 a fait passer un rapporteur de 38 à 1348 tokens/s sur le traitement du prompt.

Les chiffres ci-dessous proviennent de l'issue llama.cpp #28455 (ouverte le 5 septembre 2026, build 10791, vérifiée le 28 octobre 2026). Un rapporteur, une RTX 4080, un modèle 27B, donc lisez-les comme un exemple concret, pas comme une loi. Leur analyse de cause racine est aussi la leur : un mainteneur l'a qualifiée de « problème connu » et a fermé l'issue le lendemain.

À quoi cela ressemble à l'écran#

La configuration était ordinaire. Quelqu'un voulait un contexte de 131072 tokens sur une seule carte de 16 Go, il a donc réduit le cache KV à q5_1 :

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q5_1 --cache-type-v q5_1

Puis il a envoyé un prompt de 3,6K tokens. Le traitement du prompt tournait à 38,4 tokens/s et le décodage à 6,5 tokens/s. Faites la division : 3 600 ÷ 38,4 = 93,75, soit environ 94 secondes avant le premier mot. Avec q8_0, le même prompt tournait à 1348 tokens/s : 3 600 ÷ 1 348 = 2,67, moins de trois secondes.

L'indice est dans le gestionnaire des tâches. Pendant les exécutions lentes, le GPU restait proche de 0 % tandis que le CPU affichait environ 930 % (neuf ou dix cœurs occupés). Un GPU qui tourne au ralenti alors que vous « exécutez sur le GPU », voilà le symptôme à rechercher.

Vitesse de traitement du prompt avec cache KV q5_1 et q8_0
Prompt eval sur un prompt de 3,6K tokens, RTX 4080, issue llama.cpp #28455 (5 septembre 2026)

Pourquoi un cache plus petit ralentit autant#

Flash attention est un noyau GPU fusionné. La build CUDA par défaut ne le fournit que pour certaines paires de types de cache KV. Pour q4_0, q4_1, q5_0 et q5_1, il n'y a pas de noyau, donc llama.cpp ne s'arrête pas sur une erreur. Il déplace cette opération vers le CPU et continue. Tout fonctionne toujours, juste environ 35 fois plus lentement sur le traitement du prompt (1348 ÷ 38,4 = 35,1).

Ce « discrètement » est tout le problème. Rien dans la réponse ne vous l'indique. Le traitement du prompt chute le plus (environ 35×), et la génération ralentit aussi : 6,5 tokens/s contre 30,1 avec q8_0 dans la même issue.

Vérifiez en deux minutes#

Lancez llama-bench avec les mêmes types de cache que ceux utilisés dans le serveur, une fois par variante, et surveillez le GPU dans un autre terminal :

bash
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q8_0 -ctv q8_0 -p 512 -n 0
llama-bench -m model.gguf -ngl 99 -fa 1 -ctk q5_1 -ctv q5_1 -p 512 -n 0
nvidia-smi -l 1

Les résultats pp512 du rapporteur sur un Qwen 27B Q5_K_XL : 1511,92 tokens/s pour q8_0/q8_0, environ 197 pour q5_1/q5_1 sur la build par défaut. Si vos deux lignes montrent un tel écart et que nvidia-smi montre le GPU au ralenti pendant la seconde, vous l'avez trouvé.

Avant de corriger quoi que ce soit, associez ce que vous voyez à la cause probable :

Associer les paires

Associez chaque symptôme à sa cause

  • GPU à ~0 %, CPU à ~930 % pendant un prompt longL'attention s'exécute sur le CPU
  • Le décodage chute à ~6,5 tokens/s contre 30,1 avec q8_0La génération ralentit aussi, pas seulement le traitement du prompt
  • Les mêmes flags sont rapides avec q8_0Cette paire de types de cache n'a pas de noyau flash-attention GPU dans la build par défaut

Correctif 1 : utilisez q8_0 (ou recompilez)#

Le correctif le plus rapide consiste à changer deux flags et à garder tout le reste :

bash
llama-server -m model.gguf -ngl 99 -fa on -c 131072 -np 4 \
  --cache-type-k q8_0 --cache-type-v q8_0

Dans l'issue, cela donnait 1348 tokens/s sur le traitement du prompt et 30,1 tokens/s sur le décodage (contre 6,5). Le prix est la mémoire : un cache q8_0 est plus gros que q5_1, donc vérifiez que votre longueur de contexte tient toujours en VRAM.

Si vous devez conserver q5_1 pour des raisons de mémoire, recompilez avec toutes les combinaisons de cache flash-attention intégrées :

bash
cmake -B build -DGGML_CUDA=ON -DGGML_CUDA_FA_ALL_QUANTS=ON
cmake --build build --config Release -j

Cette build exécutait q5_1 à 1509,70 tokens/s sur pp512 et 1162 tokens/s sur le prompt de 3,6K tokens, avec un décodage à 38,5 tokens/s. Un commentateur de l'issue a fait remarquer que déplacer le travail entre backends pouvait modifier légèrement les résultats numériques et a suggéré une comparaison de perplexité entre les builds. Si les sorties exactes vous importent, faites-en une.

Comparer

Quel correctif convient à votre carte ?

Cache q8_0

Passez --cache-type-k et --cache-type-v à q8_0. Pas de recompilation. Utilise davantage de VRAM que q5_1, donc revérifiez la taille de contexte que vous pouvez faire tenir.

Recompiler avec FA_ALL_QUANTS

Conservez q5_1 et compilez avec -DGGML_CUDA_FA_ALL_QUANTS=ON. Même VRAM qu'avant, mais vous maintenez votre propre build et la compilation prend plus de temps.

La réponse « problème connu » du mainteneur ne dit pas si les versions actuelles de llama.cpp ont changé cela. La vérification honnête est la paire llama-bench ci-dessus : si les deux lignes sont proches, tout va bien.

Quand l'optimisation cesse d'en valoir la peine#

Disons que vous avez fait fonctionner le correctif, et que vous pesez maintenant le tour suivant : plus de flags, un second GPU, un quant plus petit. Si la tâche consiste en résumés, revues de code ou extraction sur des prompts de quelques milliers de tokens, demandez-vous ce qu'une requête vous coûte en temps et en effort, pas seulement en watts.

Voici une charge de travail énoncée à titre de comparaison. Disons qu'une tâche de résumé envoie 3 600 tokens d'entrée et en reçoit 500, 1 000 fois. Sur CheapTokens, cela représente environ $0.104 sur DeepSeek-V4-Flash (contexte de 380K, jusqu'à 32 768 tokens de sortie par réponse, un prix à toute heure) et environ $0.092 sur GLM-5.3-Flash (contexte de 390K). Le tableau compte l'entrée et la sortie ensemble, avec une colonne OpenRouter pour comparer :

Modèle Par requête 1 000 requêtes OpenRouter, 1 000 requêtes
GLM-5.3-Flash $0.000092 $0.092 $0.187
DeepSeek-V4-Flash $0.0001 $0.104 $0.249
MiniMax-M2.7 $0.00026 $0.255 $1.68

3 600 tokens d'entrée et 500 tokens de sortie par requête. Les prix par million de tokens sont actuels et évoluent avec nos tarifs ; la colonne OpenRouter indique des prix de référence pour la même charge de travail.

L'API ne se soucie pas de votre type de cache, de votre VRAM ou de vos flags de compilation ; il n'y a rien à basculer vers le CPU. Envoyez d'abord quelques-uns de vos vrais prompts et jugez vous-même les réponses. L'étape est petite : le point de terminaison parle le format OpenAI Chat Completions, donc la plupart des clients locaux n'ont besoin que d'une nouvelle URL de base.

bash
curl https://api.cheaptokens.dev/v1/chat/completions \
  -H "Authorization: Bearer $CHEAPTOKENS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "deepseek-ai/DeepSeek-V4-Flash-0731", "messages": [{"role": "user", "content": "Summarize the text below in 5 bullets: ..."}], "max_tokens": 500}'

La configuration complète est dans la documentation CheapTokens, et les prix actuels par modèle sont sur la page de tarification. Pour un examen plus approfondi du matériel face à l'API, voir Exécuter DeepSeek localement ou utiliser une API. Si vous voulez voir comment l'entrée et la sortie sont facturées séparément, tokens d'entrée vs de sortie le couvre.

L'ordre à suivre#

Remettre en ordre

Mettez les étapes de dépannage dans l'ordre

  1. Lancez llama-bench pour q8_0 et q5_1 et surveillez nvidia-smi
  2. Passez les deux types de cache à q8_0
  3. Vérifiez que la longueur de contexte tient toujours en VRAM
  4. Recompilez avec GGML_CUDA_FA_ALL_QUANTS=ON seulement si vous avez besoin de q5_1
  5. Comparez le même prompt sur une API si la configuration locale ne vaut toujours pas l'effort

Une mise en garde supplémentaire sur les sources : tout le diagnostic repose sur une seule issue publique et un seul GPU. Considérez 1348 et 38,4 comme ce qui s'est passé là-bas, et mesurez votre propre carte avant de faire confiance à l'un ou à l'autre.