9 min de lectura
🇧🇷 Portugués | 🇺🇸 Leer en inglés
llama.cpp caché KV VRAM con Devstral 16 GB: de 0.33 a 19.3 tok/s
La máquina estaba funcionando con llama.cpp, Devstral y 16 GB de VRAM. El modelo cargó sin errores. La primera generación llegó con una respuesta correcta en portugués.
Y fue completamente inútil.
En este post, describo el hardware que armamos para correr un LLM localmente, explico por qué un modelo que cabe en el SSD puede paralizar la GPU, muestro cómo el cálculo del caché KV decidió qué configuración quedó en producción y por qué uno de los dos modelos disponibles no tiene una solución estructural en este hardware.
Síntoma medido: 0.33 tok/s
El 17 de septiembre de 2026, en la primera tarde de pruebas, el servidor cargó el modelo Qwen3-30B-A3B con un contexto de 8,192 tokens. La respuesta llegó en portugués, correcta, después de largos segundos.
Medí: 0.33 tok/s de generación. Más de tres segundos por token. Una respuesta de 400 tokens tardaría 20 minutos.
El monitor de la GPU marcaba aproximadamente un 0 % de utilización. El proceso estaba activo, el modelo cargado y la tarjeta gráfica descansando mientras la CPU esperaba.
La hipótesis equivocada
La suposición inicial era simple: un modelo de 12.9 GB en un disco, una GPU con 16 GB de VRAM. El cálculo cerraba. Con el archivo completo en la tarjeta, la GPU debería dominar la generación.
Estaba equivocado. Lo que no entró en el cálculo fue el caché KV.
Causa raíz: el cálculo del KV
Cada modelo de lenguaje con atención multicabeza guarda, para cada token en el contexto, los vectores K y V de todas las capas. El Qwen3-30B-A3B tiene 40 capas de transformador, 8 cabezas KV por capa, con una dimensión interna de 128. En fp16 (dos bytes por valor), el caché de una sola posición de contexto ocupa:
40 capas × 8 cabezas × 128 dimensiones × 2 (K y V) × 2 bytes = 163,840 bytes por token.
Con 8,192 tokens de contexto, el caché KV ocupa 1.31 GB. Los pesos del modelo ya usaban 12.9 GB de los 16,311 MiB disponibles. La suma sobrepasa el límite por unos 400 MB. El driver de NVIDIA derramó el exceso a la RAM del sistema sin ninguna advertencia de error.
El problema no es el tamaño del modelo. Es el caché KV.
El ancho de banda de la VRAM de una RTX 5060 Ti es de aproximadamente 448 GB/s. La RAM DDR4 a 3200 MT/s en dual channel alcanza aproximadamente 40 GB/s en el mejor de los casos reales. Cuando el caché KV queda en la RAM, la GPU necesita buscar todos los vectores de atención a través de la interfaz de memoria del sistema con cada token generado. El resultado directo: GPU a aproximadamente 0 % y 0.33 tok/s de generación, con la RAM como cuello de botella.
La vía de datos que nadie mencionó: PCIe
Existe un segundo cuello de botella que no aparece en las especificaciones del modelo: la interfaz PCIe.
El servidor usa PCIe 4.0 x8, que ofrece aproximadamente 16 GB/s de ancho de banda bidireccional. Cuando el caché KV está en la RAM y la GPU necesita acceder a él en cada capa de atención, la ruta es: la GPU solicita los datos por la interfaz PCIe, la CPU los busca en la RAM y los devuelve por la misma interfaz. Con 448 GB/s de ancho de banda interno y 16 GB/s de entrada, la GPU queda limitada por el puerto de entrada, no por el procesamiento interno.
El log de llama.cpp registra el número de tensor splits cuando el modelo no cabe por completo en la GPU:
graph splits (tensors moved to host): N
Con el KV en la VRAM: graph splits: 0. Con el KV en la RAM: el valor aumenta con el tamaño del contexto. Este campo es el indicador más directo de que el cuello de botella es de transferencia, no de computación.
La cura: ctx 4096
Al reducir el contexto a 4,096 tokens, el caché KV pasó a 655 MB. Con los 12.9 GB de los pesos, el total quedó en 13.6 GB, por debajo de los 16,311 MiB disponibles. El servidor asignó 16,002 MiB, con un margen de 309 MB.
El log de inicio lo confirmó:
INFO [server_context_init_load_model] llama_new_context_with_model:
KV self size = 640.00 MiB, K (f16): 320.00 MiB, V (f16): 320.00 MiB
graph splits (tensors moved to host): 0
Ningún tensor fue al host. Todo el cómputo pasó a ocurrir en la GPU.
La prueba contundente: 19.3 tok/s
Con el mismo hardware, el mismo modelo, el mismo prompt y solo el cambio de contexto de 8,192 a 4,096:
37.2 tok/s de prompt y 19.3 tok/s de generación. GPU al 99 %.
La diferencia entre 0.33 tok/s y 19.3 tok/s, en el mismo hardware, provino de hacer que el caché KV cupiera dentro de la VRAM.
Cómo calcular el KV para cualquier modelo
La fórmula es directa. Para cualquier modelo con atención multicabeza:
bytes_por_token = n_layers × n_kv_heads × head_dim × 2 (K e V) × bytes_por_valor
En fp16: bytes_por_valor = 2. En q8_0: bytes_por_valor = 1. En q4_0: bytes_por_valor = 0,5.
El resultado, multiplicado por el ctx máximo deseado, es lo que ocupará el caché KV. Ese número se suma a los pesos del modelo para determinar si todo cabe en la VRAM.
Para el Devstral Small 2 24B (40 capas, 8 cabezas KV, dimensión 128), en fp16, el cálculo es idéntico: 160 KB por token. En q8_0, la mitad: 80 KB por token.
El caso sin salida: Devstral en este hardware
El Devstral Small 2 24B tiene pesos de 14.6 GB en Q4_K_M. La VRAM disponible es de 16,311 MiB (17.1 GB). Quedan 1.5 GB para el caché KV.
Con 160 KB por token en fp16 y 1.5 GB disponibles, el contexto máximo posible dentro de la VRAM es de aproximadamente 9,375 tokens. Este valor ignora la memoria de contexto de CUDA, los búferes de activación y la sobrecarga del servidor: en la práctica, nada sube a este contexto sin derramarse a la RAM.
La solución que funciona: KV cuantizado en q8_0, que reduce el caché a 80 KB por token. Con ctx 12,288 y q8_0, el KV ocupa 983 MB, y el total (pesos + KV) queda en aproximadamente 15.6 GB, dentro de los 16,311 MiB. Es exactamente lo que hace la config C3P, y lo que las pruebas del 18/09/2026 confirmaron: 15,097 MiB asignados, 26.3 tok/s con dos llamadas en cola.
Devstral solo funciona en este hardware con el KV cuantizado. No es una limitación de configuración, es un cálculo de memoria.
Las especificaciones de la máquina
Para contextualizar: Ryzen 7 5700X (8 núcleos, 16 hilos), 64 GB de DDR4 a 3200 MT/s en dual channel, SSD WD Green SN3000 de 1 TB, Windows 10 Pro. GPU RTX 5060 Ti de 16 GB (16,311 MiB disponibles) con driver 616.92 y límite de potencia de 180 W. Interfaz PCIe 4.0 x8 (aproximadamente 16 GB/s bidireccional).
El servidor llama.cpp corre en el build 11024. Un script de PowerShell lee un archivo de configuración en el arranque y reinicia el proceso en 5 segundos si se cae. El acceso es por VPN privada.
La máquina tenía poco más de un día cuando comenzaron estas pruebas: Windows instalado el 16/09 a las 20:49, carpeta de trabajo creada el 17/09 a las 9:50, primera descarga de llama.cpp el 17/09 a las 10:04.
Lo que costó
Medio día de pruebas para descubrir que el cálculo que rompió la primera configuración no eran los pesos del modelo, sino los 160 KB de caché por token en el contexto. La documentación de llama.cpp menciona los parámetros, pero no hace la aritmética de lo que sucede cuando la suma excede la VRAM.
El segundo costo fue entender que el ancho de banda de la RAM, y no solo el tamaño de la VRAM, define la velocidad de generación cuando cualquier parte del caché KV se escapa al sistema. La diferencia de 448 GB/s a 40 GB/s no es marginal: es un factor de 11 en el techo teórico de lectura secuencial, y aparece como tiempo de espera de la CPU en el monitor de la GPU.
Los modelos en disco son: Devstral Small 2 24B Q4_K_M (13.3 GB en disco, 14.6 GB tras cargarse) y Qwen3-30B-A3B UD-Q3_K_XL (12.9 GB). Con el cálculo del KV para ctx 4096, Qwen3 cabe con holgura. Devstral solo cabe con el KV cuantizado en q8_0. Este trade-off fue el inicio de las pruebas con las siguientes configuraciones.
En el próximo post: tres llamadas simultáneas que devolvieron HTTP 500 al mismo tiempo, y cómo pasamos de 5.8 tok/s a 25.7 tok/s cambiando solo la forma en que el servidor distribuye el caché KV entre los slots.
Suscríbete al boletín para seguir la serie.
Notas
[1] Medición del 17/09/2026, supervisor.log y salida directa de llama-server. Hardware descrito por lectura del sistema el 18/09/2026.
[2] Cálculo de KV: Qwen3-30B-A3B y Devstral Small 2 24B, ambos con 40 capas, 8 cabezas KV, dimensión 128, fp16 (2 bytes). Fuente: estructura de los modelos confirmada el 17/09/2026.
[3] Ancho de banda: RTX 5060 Ti (~448 GB/s) y DDR4 3200 MT/s dual channel (~40 GB/s). PCIe 4.0 x8 (~16 GB/s). Valores aproximados de especificaciones públicas; no se corrió ningún benchmark de ancho de banda en esta máquina.
[4] Config C3P: ctx 12,288, KV q8_0, flash-attn on, 1 slot. 15,097 MiB asignados, 25.7 tok/s en llamada aislada, 26.3 tok/s en 2 llamadas en cola. Medición del 18/09/2026.
Newsletter
Una decisión técnica por post. Nada más.
Doble confirmación. Sin spam. Cancela cuando quieras.
Más posts en el Diario de la Máquina de LLM →
Newsletter
Uma decisão técnica por post. Nada mais.
Bastidores reais de quem constrói empresa operada por IA. Sem hype.
Dupla confirmação. Sem spam. Cancele quando quiser. Dados usados exclusivamente para esta newsletter (LGPD).