# Cómo funciona realmente vLLM (y por qué lo necesitas)

> Qué es el KV cache, por qué desperdiciaba memoria de GPU y cómo vLLM aplicó la paginación de los sistemas operativos con PagedAttention.

- Autor: Alexander Zelada, Cloud Architect
- URL: https://alexanderzelada.com/vllm-pagedattention-kv-cache/
- Ruta: Infraestructura para IA (lección 2 de 5) — https://alexanderzelada.com/infraestructura-para-ia/
- Publicado: 2026-10-16
- Actualizado: 2026-10-16
- Idioma: español

vLLM nació porque los servidores de Vicuna colapsaron, y el problema no era la falta de GPUs sino cómo se gestionaba la memoria. Aquí verás qué es el mecanismo de atención, por qué el KV cache decide cuántos usuarios caben en una GPU y cómo PagedAttention aplica a esa memoria la paginación de los sistemas operativos.

[Vídeo en YouTube](https://www.youtube.com/watch?v=0_VJHCbnL04)

## Por qué colapsaron los servidores de Vicuna

En marzo de 2023, un grupo de investigadores de Berkeley, de la organización LMSYS, publicó Vicuna. Si no te suena, fue uno de los primeros modelos de código abierto construidos encima de LLaMA. Lo lanzaron para que cualquiera pudiera probarlo y se hizo viral de la noche a la mañana.

El resultado fue que los servidores colapsaron. Y el cuello de botella no era la falta de GPUs, sino cómo se estaba gestionando la memoria.

Piénsalo así: entrenar un modelo lo haces una vez, pero servirlo lo haces cada milisegundo, en tiempo real, mientras esté en producción.

### Los agentes multiplican los tokens

Con el auge de los agentes, esto se ha vuelto crítico. Antes, para una tarea concreta, como programar una función o montar una web, te bastaba con gastar 10.000 tokens. Ahora el agente escribe código, ejecuta los tests, falla, lee el error, corrige y vuelve a empezar. En un solo flujo puede consumir de 10 a 100 veces más tokens.

Cada paso de esa conversación acumula contexto y va llenando la memoria de la GPU, y eso con un único usuario. Si multiplicas ese bucle por miles de sesiones en paralelo, la VRAM no da abasto.

## FasterTransformer y Orca: los intentos anteriores

Este problema, servir a escala sin que la memoria se quede corta, ya se había intentado resolver antes por dos caminos distintos.

Por un lado, FasterTransformer, de NVIDIA, se centró en optimizar la velocidad del cálculo, es decir, la ejecución pura en la GPU, para sacar el máximo de tokens por segundo.

Por otro lado, Orca, de la Universidad Nacional de Seúl, se centró en el _scheduling_  a nivel de iteración. Organizaba las peticiones que iban entrando para no tener que esperar a que terminara un lote entero y no dejar la GPU parada.

Las dos soluciones funcionaban bien, pero ninguna había resuelto el problema de raíz, que era la gestión de la memoria.

## Cómo nació vLLM en Berkeley

Mientras tanto, en Berkeley, un equipo liderado por Woosuk Kwon trabajaba en otra dirección, en lo que hoy conocemos como vLLM. Y aquí hay un detalle importante: Kwon no venía del área de machine learning, venía de sistemas. De hecho, su director de tesis era Ion Stoica, cocreador de Apache Spark y cofundador de Databricks.

Al principio, la demo de Vicuna corría sobre el backend de Hugging Face Transformers, que es la librería estándar para investigar y prototipar, pero que no está pensada para servir tráfico masivo y concurrente en producción. Cuando las peticiones se dispararon, ese backend se convirtió en el cuello de botella.

Ahí es cuando LMSYS y el equipo de vLLM se juntan. A mediados de abril de 2023 ya habían integrado vLLM como el nuevo motor de inferencia de la demo, y en cuestión de semanas lo extendieron también a Koala y a LLaMA.

Lograron hasta 30 veces más throughput en las pruebas. LMSYS pudo mantener el tráfico con la mitad de las GPUs, con una media de 30.000 peticiones diarias y picos de 60.000. Meses después, en septiembre, publicaron el paper oficial.

## Qué es el mecanismo de atención

Para entender qué era lo que estaba colapsando esa memoria hay que bajar un nivel, a la arquitectura del modelo. Esta es la parte más técnica de la lección, así que voy paso a paso.

En inferencia, un modelo autorregresivo genera texto token a token. Produce uno, lo añade al contexto y, con toda esa secuencia, calcula el siguiente.

Para generar cada token nuevo, el modelo necesita calcular cuánta atención debe prestar a cada uno de los tokens anteriores, porque no todos pesan lo mismo. Es como cuando revisas un log en un servidor: no lees cada línea desde arriba, vas directo a buscar el error o el componente que falló. Eso es el mecanismo de atención.

Dentro de cada capa del Transformer, este cálculo se hace proyectando la información en tres vectores distintos: **Query**, **Key**  y **Value**.

- La **Query**  es la consulta que lanza el token que se está procesando en ese instante, es decir, qué contexto necesita encontrar.
- Las **Keys**  son las etiquetas de los tokens anteriores y describen qué información representa cada uno.
- Los **Values**  son el contenido real, la información semántica que aportan esos tokens anteriores.

### Un ejemplo: «el pod del microservicio se reinició»

Vamos a verlo con una frase típica de sistemas: _el pod del microservicio se reinició_.

Cuando el modelo llega a la palabra _reinició_, compara su vector Query con el vector Key de cada una de las palabras anteriores. Con _el_, la afinidad es prácticamente nula. Con _del_, tampoco. Con _se_, casi nada. Con _microservicio_  empieza a haber correlación. Y con _pod_, la coincidencia matemática es muy fuerte.

Esas afinidades pasan por una función **Softmax**, que las convierte en porcentajes que suman 100 entre todas las palabras. Digamos que _pod_  se lleva un 70 % de ese reparto, _microservicio_  un 20 % y el resto casi nada.

El modelo no descarta las otras palabras ni se queda solo con una. Lo que hace es una suma ponderada: multiplica el vector Value de cada palabra por su porcentaje y los suma todos. El resultado se queda sobre todo con el contenido del vector Value de _pod_, con un poco de _microservicio_  y casi nada del resto.

En resumen, la atención consiste en lanzar una Query, medir la afinidad con las Keys y hacer un promedio ponderado de los vectores Value.

## El problema: recalcular los mismos vectores una y otra vez

Ahora viene el problema real en producción. Los vectores Key y Value de los tokens anteriores nunca cambian: son exactamente los mismos en cada paso. Pero si no se guardan en una memoria intermedia, cada vez que el modelo genera un token nuevo, la GPU tiene que recalcular las proyecciones de toda la secuencia desde el inicio y a través de todas las capas.

Para generar la palabra número 100, recalcula las 99 anteriores. Para la 101, recalcula 100. Ese coste de cómputo crece de forma cuadrática: con 100 tokens ya estás repitiendo unas 5.000 operaciones redundantes, y con 1.000 tokens, cerca de medio millón. La GPU gasta sus ciclos de cálculo en recalcular una y otra vez exactamente los mismos vectores.

Si esta parte se te ha hecho pesada, es normal. A mí también me costó varias vueltas entenderla bien.

## El KV cache: guardar en vez de recalcular

Esa repetición ya sabíamos cómo arreglarla desde hace tiempo en informática. Se llama **memoización**  y la idea es clásica: si un cálculo no va a cambiar, no lo repitas, guárdalo en memoria.

Aquí pasa justo eso. El modelo es causal, es decir, cada token solo mira hacia atrás, nunca hacia delante. Por eso los vectores Key y Value de los tokens anteriores se calculan una sola vez y ya no se vuelven a recalcular.

El modelo los deja guardados directamente en la VRAM de la GPU, y eso es el **KV cache**. A partir de ahí, cada token nuevo solo tiene que calcular lo suyo: su propia Query para preguntar, y su Key y su Value, que se quedan guardados en esa caché para los tokens que vengan después.

El KV cache resolvió el problema de velocidad de los modelos de lenguaje. Pero ojo, no es gratis: es un _trade-off_. Antes repetías el cálculo en cada paso y no guardabas nada. Ahora ya no repites cálculo, pero lo guardas todo en memoria, y esa memoria no se libera hasta que la petición termina por completo.

El vector Query se usa una vez en el paso actual y se descarta. Los vectores Key y Value, en cambio, se van acumulando token tras token y turno tras turno.

## Cuánto ocupa el KV cache en una GPU

¿Y cuánto ocupa realmente todo esto? El paper de vLLM lo midió sobre OPT, el modelo abierto de Meta, en su versión de 13.000 millones de parámetros.

Un solo token en el KV cache ocupa unos 800 KB. Una sola petición con un contexto de 2.000 tokens ya llega a unos 1,6 GB de memoria. Y eso es para una sola petición de un único usuario.

En una A100 de 40 GB, los pesos del modelo ya ocupan 26 GB de entrada. Con los 14 GB que quedan libres, apenas te alcanza para unas 9 peticiones con el contexto completo, y hasta ahí llegas.

Es decir, lo que decide cuántos usuarios simultáneos caben en tu GPU no son los pesos del modelo, sino el tamaño del KV cache.

## La sobre-reserva estática y los tres tipos de desperdicio

Antes de seguir, una aclaración. El KV cache no lo inventó Kwon ni nació con vLLM. En 2023 ya llevaba años siendo la práctica estándar en la industria, y todo el mundo lo usaba. Lo que se preguntó Kwon fue otra cosa.

El KV cache crece con cada token nuevo, y el problema de base es que el servidor no sabe de antemano cuántos tokens va a escribir cada petición.

Antes de vLLM se hacía lo más simple y a la vez lo más ineficiente: la **sobre-reserva estática**. Se reservaba de una vez un bloque de memoria continuo para la longitud máxima posible de la secuencia. Cada casilla de ese bloque es un _slot_  para el Key y el Value de un token. Si el límite eran 2.048 tokens, se reservaban directamente esos 2.048 slots en la VRAM, aunque la respuesta final ocupara solo 200.

Así se perdían recursos por tres tipos de desperdicio clásicos de la memoria:

- **Memoria reservada.**  Son los slots que bloqueas por adelantado para tokens que se van a generar más adelante. Aunque al final se usen, mientras tanto ocupan un espacio que impide atender a otros usuarios en paralelo.
- **Fragmentación interna.**  Son los slots que reservaste hasta el límite de 2.048 tokens y que nunca se llegaron a escribir porque la respuesta terminó antes. Se quedan vacíos, asignados solo a esa sesión, y nadie más los puede usar.
- **Fragmentación externa.**  Como las peticiones terminan en momentos distintos, van quedando huecos libres separados entre sí por sesiones que siguen activas. Sumando todos esos huecos puedes tener espacio de sobra, pero si llega una petición nueva que exige un bloque continuo grande, la GPU no la puede admitir porque no tiene ese espacio contiguo.

El paper de vLLM le puso números a esto. En los sistemas de 2023, solo entre el 20 y el 38 % de la memoria reservada para el KV cache estaba guardando tokens de verdad. El resto era desperdicio.

## PagedAttention: la paginación del sistema operativo aplicada a la GPU

Si vienes del mundo de sistemas y del kernel, esto te sonará enseguida. Es el mismo problema que tenía la memoria física antes de que se inventara la memoria virtual.

Y esa fue la idea de Kwon. No inventó una matemática nueva. Fue a ver cómo lo habían resuelto los sistemas operativos hace décadas, que es con paginación. Al algoritmo que diseñó para gestionar esta memoria lo llamó **PagedAttention**, y es la pieza central de vLLM.

La idea es sencilla: la memoria del KV cache no tiene por qué ser continua físicamente. Divides la caché en bloques pequeños de tamaño fijo, que en vLLM son de 16 tokens por defecto, y los vas asignando en cualquier página libre de la VRAM a medida que la petición genera texto. Solo reservas memoria cuando de verdad hace falta.

Para conectar todo esto, cada secuencia mantiene su propia **tabla de bloques**, igual que la tabla de páginas de un sistema operativo. Para el modelo, la conversación es una lista lógica y continua: bloque 0, bloque 1, bloque 2. Y la tabla es la que traduce cada bloque lógico a una dirección física real en la VRAM.

**Qué guarda cada entrada de la tabla de bloques**

Cada entrada de la tabla anota cuántos slots de ese bloque están ocupados y a qué dirección física apunta. Así, el bloque lógico 0 puede estar en el bloque físico 7 de la memoria, el bloque 1 en el 3 y el bloque 2 en el 12.

Los datos del KV cache quedan dispersos y mezclados entre distintos usuarios, pero a la red neuronal le da exactamente igual.

Cuando la GPU calcula la atención, el kernel de PagedAttention (el programa que se ejecuta en la propia tarjeta) lee esos bloques dispersos al vuelo, siguiendo los punteros de la tabla, sin copiar ni reordenar datos en un búfer contiguo. Y si entran 10 usuarios a la vez, sus bloques se intercalan en cualquier hueco libre de la VRAM, lo que elimina por completo la fragmentación externa.

Y ojo, esto no cambia en nada la matemática de la atención del Transformer. Lo único que cambia es cómo se gestiona y se direcciona esa memoria.

El resultado fue que el desperdicio de memoria bajó por debajo del 4 %. Y como ahora caben muchas más peticiones concurrentes en la misma tarjeta, el throughput se multiplica entre 2 y 4 veces, con el mismo hardware, el mismo modelo y manteniendo la latencia.

Por eso el paper se presentó en SOSP, que es una de las conferencias más importantes de sistemas operativos, y no en un congreso de machine learning. En el fondo nunca fue un problema de inteligencia artificial, sino un problema clásico de gestión de memoria.

## vLLM hoy: Amazon, Red Hat y OpenShift AI

Hoy vLLM es el estándar de facto para servir modelos de código abierto en producción.

Amazon lo usa a escala global para Rufus, su asistente de compras. Y hay un detalle que dice mucho más que las métricas habituales: Amazon fabrica sus propios chips propios de IA, Trainium para entrenamiento e Inferentia para inferencia, y en lugar de crear un motor cerrado desde cero, optó por desarrollar el plugin oficial para vLLM.

Red Hat fue todavía más lejos. A finales de 2024 anunció la compra de Neural Magic, uno de los mayores contribuidores del núcleo de vLLM, un equipo salido del MIT especializado en optimización de bajo nivel. Poco después lanzó su propio servidor empresarial basado directamente en vLLM, y lo convirtió en el motor por defecto de soluciones como OpenShift AI.

## Automatic Prefix Caching: reutilizar el contexto entre peticiones

Todo esto conecta con los agentes del principio, que reenvían todo el contexto de la conversación a la GPU en cada paso del bucle.

El KV cache tradicional te ayuda dentro de una misma petición mientras la llamada está activa. Pero en cuanto esa petición termina, ese espacio de memoria se libera por completo. Si 2 segundos después llega otra petición con el mismo system prompt largo o con el mismo historial de conversación, los servidores tradicionales volvían a procesar todo ese tramo desde cero, y pagabas otra vez el coste de computación.

Para solucionarlo, el equipo de vLLM introdujo **Automatic Prefix Caching**. En lugar de borrar los bloques al terminar la petición, el motor los indexa por un hash de su contenido. Si detecta que una petición nueva comparte el mismo inicio o las mismas instrucciones del sistema, reutiliza directamente esos bloques de la VRAM sin volver a calcularlos.

En pruebas reales con prompts largos, de unos 10.000 tokens, el tiempo hasta obtener el primer token pasó de 4,3 segundos a 0,6 segundos. En los flujos con agentes, esto reduce mucho el coste y la latencia de cada iteración.

Lo más costoso al servir IA en producción no siempre es el tamaño del modelo, sino cómo gestionas los recursos en cada petición que entra al sistema. Kwon no modificó la arquitectura de la red neuronal ni esperó a que saliera una GPU con más memoria. Aplicó ingeniería de sistemas a la memoria que ya teníamos.

## Lo que hemos visto

- vLLM nació en Berkeley cuando la demo de Vicuna colapsó, y el problema era la gestión de la memoria, no la falta de GPUs.
- La atención compara la Query del token actual con las Keys de los anteriores y hace una suma ponderada de sus Values.
- El KV cache guarda las Keys y los Values para no recalcularlos, pero su tamaño es lo que decide cuántos usuarios caben en una GPU.
- PagedAttention divide el KV cache en bloques de tamaño fijo y los localiza con una tabla de bloques, igual que la paginación de un sistema operativo, y así el desperdicio baja por debajo del 4 %.
- Automatic Prefix Caching reutiliza los bloques de peticiones anteriores que empiezan igual, lo que ayuda mucho a los agentes.
