KV cache: la memoria de trabajo que sostiene la inferencia LLM

TL;DR

El KV cache es la memoria de trabajo que un modelo de lenguaje mantiene durante una conversación. Sin él, cada token nuevo obligaría a recalcular toda la conversación desde el principio, con un coste cuadrático en la longitud del texto. Con él, el coste es lineal pero a cambio el cache vive en VRAM y crece con cada token. En la práctica, no es el modelo lo que limita cuánto contexto puedes servir: es el KV cache. Para una RTX 4090 con Llama 3 8B, cabe el modelo en 16 GB y queda apenas espacio para ~64 K tokens de cache totales (sumando todas las sesiones simultáneas). Entender este número es la diferencia entre anunciar “contexto de 128 K” y servirlo de verdad bajo carga.

La analogía: el orador con amnesia

Imagina que asistes a una conferencia técnica de dos horas. El ponente, cada vez que va a decir una frase nueva, rebobina mentalmente toda la charla desde el inicio, recompone el hilo, y solo entonces continúa. Su próxima frase requiere rememorar la anterior; la siguiente, las dos anteriores; al cabo de una hora, cada palabra nueva le cuesta una hora de recapitulación. Una conferencia así sería materialmente imposible.

Ahora imagina al mismo ponente con un cuaderno donde apunta, mientras habla, las dos o tres ideas clave de cada frase: sujeto, objeto, vínculo con lo anterior. Antes de cada frase nueva, ojea el cuaderno y sigue. Su próxima palabra sólo cuesta una ojeada al cuaderno, no rebobinar la charla entera.

Ese cuaderno, en un transformer, se llama KV cache. Sin él, los modelos de lenguaje conversacionales serían inviables. Con él, son productos comerciales. Pero el cuaderno pesa: y entender cuánto, dónde y por qué, es lo que separa una infraestructura de inferencia que funciona de una que se cae a la tercera sesión concurrente.

El mecanismo en sí (en cristiano)

Un transformer genera texto un token cada vez. Para decidir el siguiente token, el modelo aplica un mecanismo llamado atención sobre todos los tokens previos: pregunta “¿qué partes del contexto anterior son relevantes para predecir lo que viene ahora?”.

Internamente, cada token de entrada se proyecta a tres vectores:

  • Q (Query): “qué estoy buscando”
  • K (Key): “qué oferta este token”
  • V (Value): “qué información lleva este token”

La atención del token actual contra el contexto se calcula multiplicando su Q contra las K de todos los tokens previos, normalizando con softmax, y ponderando las V correspondientes. Resultado: una representación contextualizada del token actual.

Cálculo de atención para el token NQ (token N)"qué busco"K (tokens 1..N)del cacheV (tokens 1..N)del cacheQ·Kᵀ → softmax× Vrepresentación del token N

Aquí está la clave: para predecir el token N, sólo necesito Q nuevo (el del token N) y K, V de todos los tokens anteriores. Las K y V de los tokens 1..N-1 no han cambiado desde la iteración anterior. Recalcularlas sería tirar trabajo.

El KV cache es exactamente eso: la memoria que guarda K y V de cada token ya procesado, en cada capa del modelo, para no recalcularlos.

Por qué existe: el coste cuadrático sin él

Generar un texto de N tokens implica N pasos. En el paso i, se calcula la atención sobre i tokens anteriores. Sin cache, en cada paso recomputas las K, V de los i-1 tokens anteriores más las del nuevo. La cuenta total de cómputos de atención crece como:

Σ i (i=1..N)  =  N·(N+1) / 2  ≈  N² / 2

Con KV cache, sólo procesas el token nuevo en cada paso: coste lineal en N.

Cómputo acumulado para generar N tokenstokens generados (N)cómputo relativo025%50%75%100%01K2K3K4Kcon KV cache (lineal)sin KV cache (cuadrático)

Los números concretos son demoledores:

Tokens generadosSin KV cache (operaciones)Con KV cacheRatio
1288 25612864×
1 024524 8001 024512×
4 0968 390 6564 0962 048×
32 768536 887 29632 76816 384×

A los 32 K tokens, el cache te ahorra cuatro órdenes de magnitud de cómputo. No es una optimización: es lo que hace que la inferencia conversacional sea posible.

El precio: cuánto pesa la mochila

El KV cache se paga en VRAM. La fórmula, por secuencia, es:

KV_size  =  2  ·  n_layers  ·  n_kv_heads  ·  head_dim  ·  context_len  ·  bytes_per_param
            ↑
        K y V

Por token (sin el context_len), es una constante propia del modelo. Veamos números reales:

Modelon_layersn_kv_headshead_dimBytes/token (BF16)GB a 8 K ctxGB a 32 KGB a 128 K
Llama 3 8B (MHA hipotético)3232128524 2884.0016.0064.00
Llama 3 8B (GQA real)328128131 0721.004.0016.00
Llama 3 70B (GQA)808128327 6802.5010.0040.00
Qwen3 8B (GQA)368128147 4561.124.5018.00
Mistral 7B (GQA)328128131 0721.004.0016.00

Dos lecturas inmediatas:

  1. Sin GQA, no hay 128 K que valga. Un Llama 3 8B con atención multi-head clásica necesitaría 64 GB sólo de KV cache para una única secuencia con 128 K tokens. Es decir, no cabe en ninguna GPU consumer. Por eso Meta, Mistral y compañía adoptaron Grouped Query Attention.
  2. El KV cache puede ser mayor que el modelo. Llama 3 8B BF16 ocupa ~16 GB. Con 128 K de contexto, su cache son otros 16 GB. Una sola sesión empata al modelo en VRAM.
KV cache (GB) vs longitud de contexto (1 secuencia, BF16)010203040 GB08K32K64K128K≈ VRAM libre tras cargar 8B en una 4090Llama 3 8BQwen3 8BLlama 3 70B

La línea roja punteada marca la VRAM realista disponible en una RTX 4090 después de cargar el modelo. Cualquier modelo cuya curva cruza esa línea no podrá servir ese contexto sin estrategias adicionales (cuantización del cache, offload, particionado).

La inferencia es memory-bound, no compute-bound

Hay un equívoco común: pensar que “GPU rápida = inferencia rápida”. En el régimen donde realmente operan los servicios de inferencia con KV cache, lo que se mide es el ancho de banda de memoria. Cada token nuevo exige leer las K y V de todos los tokens anteriores desde HBM. El cómputo es modesto; el movimiento de datos, masivo.

Por eso, una H100 SXM (3.35 TB/s de HBM3) puede ser 2–3× más rápida que una A100 (1.55–2 TB/s) sin que la frecuencia ni el número de cores expliquen del todo la diferencia. Lo explica el ancho de banda.

Y por eso, también, las ofertas de “GPU baratas con mucha VRAM pero HBM lenta” (algunas variantes con GDDR6 o LPDDR5) decepcionan en inferencia con contextos largos: tienen sitio para guardar el cache pero les cuesta una eternidad releerlo.

Trucos para que el cuaderno sea más fino

Tres técnicas, en orden cronológico, han ido aplanando el tamaño del KV cache:

Multi-Head Attention (MHA). El planteamiento original del transformer (Vaswani et al., 2017). Cada cabeza de atención tiene su propia K y V. Caro en cache pero teóricamente máximo en expresividad. Es lo que tenían los modelos hasta ~2023.

Multi-Query Attention (MQA). Una sola K y V compartida por todas las cabezas. Reduce el cache n_heads veces. Funciona razonablemente pero degrada calidad de generación en algunos benchmarks.

Grouped Query Attention (GQA). El término medio que ha ganado. Las cabezas se agrupan: en Llama 3 8B, 32 cabezas de query comparten K, V en grupos de 4 → 8 grupos de KV. Reduce el cache 4× respecto a MHA con casi idéntica calidad. Es el estándar de facto desde 2024.

Multi-Head Latent Attention (MLA). La innovación de DeepSeek-V2/V3: en vez de almacenar K, V por cabeza, comprime el estado en un vector latente más pequeño y proyecta a K, V en el momento. El cache puede llegar a 70 bytes/token, dos órdenes de magnitud menos que GQA. Es la razón principal por la que DeepSeek-V3 (671 B parámetros, 37 B activos) es servible en infraestructura abordable.

KB de cache por token (Llama 3 8B equivalente, BF16)MHA (32 KV heads)512 KBGQA (8 KV heads)128 KBMQA (1 KV head)16 KBMLA (DeepSeek-V3)~0.5 KB (real V3)

Nota: la barra de MLA es ilustrativa con valores típicos publicados por DeepSeek; la implementación exacta depende del tamaño latente. Lo importante es el orden de magnitud.

A esto se suma una cuarta técnica ortogonal: cuantizar el cache a FP8, INT8 o incluso INT4. vLLM y TensorRT-LLM ya lo soportan en producción. Pasar de BF16 (2 bytes) a FP8 (1 byte) divide el cache por dos con coste pequeño en calidad. Pasar a INT4, por cuatro, con coste algo mayor.

El siguiente dragón: la fragmentación

Hasta aquí hemos hablado del cache como si fuera un bloque contiguo. En la práctica, un servidor de inferencia atiende decenas de sesiones simultáneas, cada una con su propio cache que crece a un ritmo distinto. La asignación naïve —reservar el máximo posible por sesión— desperdicia entre el 60 % y el 80 % de la VRAM según el paper original de PagedAttention.

Asignación naïve (contigua)PagedAttention (bloques)sesión Asesión Bsesión Csesión D→ ~70 % de VRAM reservada y vacía→ < 4 % desperdicio (paper vLLM)

PagedAttention —la idea de Kwon et al. (2023) que dio origen a vLLM— resuelve esto pidiendo prestada una técnica de los sistemas operativos: dividir la VRAM en bloques pequeños (típicamente de 16 tokens) y mantener una tabla de páginas lógicas → físicas por sesión. Una sesión ya no reserva un bloque contiguo enorme: crece un bloque cada vez, y los bloques pueden estar dispersos por la VRAM. Resultado: ocupación efectiva del 90 % en lugar del 30 %, y por tanto 2–4× más throughput agregado en el mismo hardware.

PagedAttention merece artículo propio. Lo dejo apuntado para el siguiente.

Dos escenarios: workstation vs cluster

Bajemos a casos concretos y comparemos las dos puntas del espectro de hardware on-premise: una workstation con una GPU consumer frente a un cluster de GPUs de datacenter. Mismas matemáticas, dos órdenes de magnitud de diferencia en lo que pueden servir.

Escenario A — Una RTX 4090 (24 GB, Ada Lovelace)

Configuración típica con Qwen3-8B BF16:

Modelo BF16:                    ~16 GB
Activations + overhead:          ~2 GB
VRAM disponible para KV cache:   ~6 GB (con margen)

Con 144 KB/token (Qwen3-8B GQA), eso son ~43 K tokens totales de cache distribuidos entre todas las sesiones simultáneas. En la práctica:

ConcurrenciaContexto máximo por sesión
132 768
48 192
84 096
162 048

Si necesitas anunciar “soportamos 32 K de contexto” con concurrencia 4+, hay que cuantizar el cache (FP8 baja a 72 KB/token, duplica capacidad) o subir el modelo de gama (un 4B con GQA y cache cuantizado holgaría).

Con tensor parallel = 5 y Llama 3 70B BF16:

Modelo BF16:                   ~140 GB (28 GB/GPU)
Overhead vLLM por GPU:           ~2 GB
VRAM libre para KV por GPU:     ~50 GB → ~250 GB agregados

Con 320 KB/token (Llama 3 70B GQA), eso son ~800 K tokens totales de cache. Mucho margen para servir contextos largos con concurrencia alta:

ConcurrenciaContexto máximo por sesión
4200 000
1650 000
6412 500

Para DeepSeek-V3 671 B con MLA: la economía cambia radicalmente porque el cache es ~100× más fino. Lo que limita ya no es el cache sino la VRAM del propio modelo (cuantizado FP8 son ~671 GB → no cabe en 5×H100, hace falta cluster mayor o FP4).

A y B, lado a lado

MétricaEscenario A (1×4090)Escenario B (5×H100 SXM)
VRAM total24 GB400 GB (≈ 17×)
Modelo servible (BF16)Hasta ~8 BHasta ~70 B
KV cache disponible~6 GB~250 GB (≈ 42×)
Tokens totales de cache~43 K~800 K (≈ 19×)
Ancho de banda HBM1.0 TB/s (GDDR6X)3.35 TB/s/GPU
Coste indicativo~2 K €~250 K € (≈ 125×)

La asimetría interesante: pasar de A a B cuesta unas 125× más, pero sólo da ~19× más cache total. Lo que el cluster compra de verdad no es proporcionalmente más contexto: es modelos un orden de magnitud más grandes servibles a la vez (8 B → 70 B), ancho de banda HBM 3× mayor por GPU y concurrencia muy alta sin degradar latencia. Si tu carga real es “modelos pequeños con mucho contexto y baja concurrencia”, la 4090 cuantizada rinde mucho más cerca del cluster de lo que el precio sugiere. Si es “modelo grande, baja latencia, muchos usuarios”, el salto a HBM3 + NVLink no se sustituye con más 4090s.

Implicaciones operativas

Tres observaciones para llevarse:

Primero, el contexto máximo anunciado por un modelo no es el que puedes servir en tu hardware. Llama 3 8B “soporta” 128 K, pero en una 4090 con 4 sesiones simultáneas tu contexto efectivo son ~8 K. Es trivial comprobarlo antes de comprometerse a un SLA.

Segundo, cuantizar el KV cache es de las optimizaciones con mejor relación coste/beneficio. No toca los pesos, no afecta a la reproducibilidad de las salidas y duplica capacidad. vLLM lo soporta vía --kv-cache-dtype fp8.

Tercero, si los SLA dictan contextos largos con muchos usuarios concurrentes, GQA es necesario pero no suficiente. A medio plazo, hay que mirar modelos con MLA o variantes de attention con compresión.

Lo que no hemos cubierto (próximos artículos)

  • PagedAttention y su implementación en vLLM: bloques, tabla de páginas, evicción.
  • Prefix caching: cuando varias peticiones comparten el system prompt, no hace falta recomputar las K, V de la parte común.
  • Speculative decoding y su interacción con el cache.
  • Cache offloading: mover bloques fríos a RAM o a NVMe, técnica clave para contextos > 1 M.

Referencias

  • Vaswani et al., Attention Is All You Need (NeurIPS 2017) — paper fundacional del transformer.
  • Ainslie et al., GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints (EMNLP 2023).
  • Kwon et al., Efficient Memory Management for Large Language Model Serving with PagedAttention (SOSP 2023) — paper original de vLLM.
  • DeepSeek-AI, DeepSeek-V2 Technical Report (2024) — introducción de Multi-Head Latent Attention.
  • Documentación oficial de vLLM: https://docs.vllm.ai/.
  • Llama 3 model card (Meta): especificaciones GQA, n_layers, n_kv_heads.