Volver al blog

Solución de problemasGuía

¿El procesamiento de prompts de llama.cpp es 35 veces más lento? Arregla la caché KV q5_1

Flash attention más una caché KV q5_1 pueden llevar la atención a la CPU. Cómo confirmarlo, arreglarlo con q8_0 y cuándo una API es más sencilla.

Por CheapTokens TeamPublicado el 6 min de lectura

Terminal mostrando la GPU al 0 por ciento y la CPU al 930 por ciento durante el procesamiento de prompts de llama.cpp
En esta página
  1. Cómo se ve en pantalla
  2. Por qué una caché más pequeña lo hace mucho más lento
  3. Compruébalo en dos minutos
  4. Solución 1: usa q8_0 (o recompila)
  5. Cuándo dejar de ajustar
  6. El orden en que trabajar

Tu modelo local responde bien, pero ahora cada prompt largo se queda ahí durante minuto y medio mientras la GPU no hace nada. Si ejecutas llama.cpp con -fa on y una caché KV q5_1, probablemente esa sea la causa: la compilación CUDA por defecto no tiene kernel de flash-attention para ese tipo de caché, así que el paso de atención se ejecuta silenciosamente en la CPU. Cambiar la caché a q8_0 llevó a un usuario de 38 a 1348 tokens/s en el procesamiento de prompts.

Las cifras a continuación provienen del issue #28455 de llama.cpp (abierto el 5 de septiembre de 2026, build 10791, comprobado el 28 de octubre de 2026). Un usuario, una RTX 4080, un modelo de 27B, así que léelas como un ejemplo práctico, no como una ley. Su análisis de causa raíz también es suyo: un mantenedor lo calificó como "un problema conocido" y cerró el issue al día siguiente.

Cómo se ve en pantalla#

La configuración era normal. Alguien quería un contexto de 131072 tokens en una sola tarjeta de 16 GB, así que redujo la caché KV a 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

Luego enviaron un prompt de 3.6K tokens. El procesamiento de prompts se ejecutó a 38.4 tokens/s y la decodificación a 6.5 tokens/s. Haz la división: 3,600 ÷ 38.4 = 93.75, así que unos 94 segundos antes de la primera palabra. Con q8_0 el mismo prompt se ejecutó a 1348 tokens/s: 3,600 ÷ 1,348 = 2.67, menos de tres segundos.

La pista está en el administrador de tareas. Durante las ejecuciones lentas la GPU se mantuvo cerca del 0% mientras la CPU mostraba alrededor del 930% (nueve o diez núcleos ocupados). Una GPU que está inactiva mientras "ejecutas en la GPU" es el síntoma que debes buscar.

Velocidad de procesamiento de prompts con caché KV q5_1 y q8_0
Evaluación de prompt con un prompt de 3.6K tokens, RTX 4080, issue #28455 de llama.cpp (5 de septiembre de 2026)

Por qué una caché más pequeña lo hace mucho más lento#

Flash attention es un kernel GPU fusionado. La compilación CUDA por defecto solo lo incluye para algunos pares de tipos de caché KV. Para q4_0, q4_1, q5_0 y q5_1 no hay kernel, así que llama.cpp no se detiene con un error. Mueve esa operación a la CPU y continúa. Todo sigue funcionando, solo que unas 35 veces más lento en el procesamiento de prompts (1348 ÷ 38.4 = 35.1).

Ese "silenciosamente" es todo el problema. Nada en la respuesta te lo indica. El procesamiento de prompts es lo que más cae (unas 35×), y la generación también se ralentiza: 6.5 tokens/s frente a 30.1 con q8_0 en el mismo issue.

Compruébalo en dos minutos#

Ejecuta llama-bench con los mismos tipos de caché que usas en el servidor, una vez por variante, y observa la GPU en otra 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

Los resultados de pp512 del usuario en un Qwen 27B Q5_K_XL: 1511.92 tokens/s para q8_0/q8_0, unos 197 para q5_1/q5_1 en la compilación por defecto. Si tus dos líneas muestran una diferencia así y nvidia-smi muestra la GPU inactiva durante la segunda, lo has encontrado.

Antes de arreglar nada, relaciona lo que ves con la causa probable:

Emparejar los pares

Relaciona cada síntoma con su causa

  • GPU al ~0%, CPU al ~930% durante un prompt largoLa atención se está ejecutando en la CPU
  • La decodificación cae a ~6.5 tokens/s frente a 30.1 con q8_0La generación también se ralentiza, no solo el procesamiento de prompts
  • Los mismos flags son rápidos con q8_0Este par de tipos de caché no tiene kernel de flash-attention en GPU en la compilación por defecto

Solución 1: usa q8_0 (o recompila)#

La solución más rápida es cambiar dos flags y mantener todo lo demás:

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

En el issue esto dio 1348 tokens/s en el procesamiento de prompts y 30.1 tokens/s en la decodificación (frente a 6.5). El precio es la memoria: una caché q8_0 es más grande que q5_1, así que comprueba que tu longitud de contexto sigue cabiendo en la VRAM.

Si debes mantener q5_1 por razones de memoria, recompila con todas las combinaciones de caché de flash-attention incluidas:

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

Esa compilación ejecutó q5_1 a 1509.70 tokens/s en pp512 y 1162 tokens/s en el prompt de 3.6K tokens, con decodificación a 38.5 tokens/s. Un comentarista del issue señaló que mover el trabajo entre backends podría cambiar ligeramente los resultados numéricos y sugirió una comparación de perplejidad entre compilaciones. Si te importan las salidas exactas, haz una.

Comparar

¿Qué solución encaja con tu tarjeta?

Caché q8_0

Cambia --cache-type-k y --cache-type-v a q8_0. Sin recompilar. Usa más VRAM que q5_1, así que vuelve a comprobar el tamaño de contexto que puedes ajustar.

Recompilar con FA_ALL_QUANTS

Mantén q5_1 y compila con -DGGML_CUDA_FA_ALL_QUANTS=ON. La misma VRAM que antes, pero mantienes tu propia compilación y tarda más en compilar.

La respuesta del mantenedor "problema conocido" no dice si las versiones actuales de llama.cpp han cambiado esto. La comprobación honesta es el par de llama-bench de arriba: si ambas líneas están cerca, estás bien.

Cuándo dejar de ajustar#

Supongamos que conseguiste que la solución funcione y ahora estás sopesando la siguiente ronda: más flags, una segunda GPU, un quant más pequeño. Si el trabajo son resúmenes, revisión de código o extracción con prompts de unos miles de tokens, pregúntate cuánto te cuesta una solicitud en tiempo y esfuerzo, no solo en vatios.

Aquí tienes una carga de trabajo declarada para comparar. Supongamos que un trabajo de resumen envía 3,600 tokens de entrada y recibe 500 de vuelta, 1,000 veces. En CheapTokens eso es aproximadamente $0.104 en DeepSeek-V4-Flash (contexto de 380K, hasta 32,768 tokens de salida por respuesta, un precio a cualquier hora) y aproximadamente $0.092 en GLM-5.3-Flash (contexto de 390K). La tabla cuenta entrada y salida juntas, con una columna de OpenRouter para comparar:

Modelo Por solicitud 1000 solicitudes OpenRouter, 1000 solicitudes
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

3600 tokens de entrada y 500 tokens de salida por solicitud. Los precios por 1M de tokens son actuales y cambian junto con nuestras tarifas; la columna de OpenRouter muestra precios de referencia para la misma carga de trabajo.

A la API no le importa tu tipo de caché, tu VRAM ni tus flags de compilación; no hay nada que recurra a la CPU. Envía primero unos cuantos de tus prompts reales y juzga tú mismo las respuestas. El paso es pequeño: el endpoint habla el formato OpenAI Chat Completions, así que la mayoría de los clientes locales solo necesitan una nueva URL 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 configuración completa está en la documentación de CheapTokens, y los precios actuales por modelo están en la página de precios. Para una mirada más larga a hardware frente a API, consulta Ejecutar DeepSeek localmente o usar una API. Si quieres ver cómo se factura la entrada y la salida por separado, tokens de entrada vs salida lo cubre.

El orden en que trabajar#

Poner en orden

Ordena los pasos de solución de problemas

  1. Ejecuta llama-bench para q8_0 y q5_1 y observa nvidia-smi
  2. Cambia ambos tipos de caché a q8_0
  3. Comprueba que la longitud de contexto sigue cabiendo en la VRAM
  4. Recompila con GGML_CUDA_FA_ALL_QUANTS=ON solo si necesitas q5_1
  5. Compara el mismo prompt en una API si la configuración local sigue sin merecer el esfuerzo

Una advertencia más sobre las fuentes: todo el diagnóstico se basa en un issue público y una GPU. Toma 1348 y 38.4 como lo que ocurrió allí, y mide tu propia tarjeta antes de confiar en cualquiera de los dos.