Una empresa despliega un modelo local de 13B parámetros y lo activa para diez usuarios internos. La primera semana funciona sin problemas. Entonces llega un segundo equipo, añade otro caso de uso, el número de peticiones concurrentes salta a 20–30 y el tiempo de respuesta pasa de tres segundos a cuarenta. Alguien propone comprar una GPU más potente. Se compra. La respuesta baja a 25 segundos. El problema no es el hardware — el problema es lo que ocurre con la KV-cache y el batching bajo el capó.
El throughput y la latencia de la inferencia LLM no son únicamente una cuestión de hardware. Son una cuestión de arquitectura. La forma en que el framework de serving gestiona la memoria, cómo agrupa las peticiones en lotes, cómo comparte resultados intermedios — todo eso determina si extraes de una GPU el rendimiento que justifica su precio, o si estás pagando por una capacidad que nunca se aprovecha. Este artículo explica los mecanismos y muestra dónde están las palancas de mejora sin necesidad de comprar hardware adicional.
Dónde se pierde el rendimiento: anatomía de la inferencia LLM
Para entender dónde optimizar, primero hay que entender qué ocurre al generar texto. La inferencia LLM se divide en dos fases.
La fase de prefill procesa todo el prompt de entrada de una vez — es una operación paralelizable que carga la GPU como una multiplicación matricial. El resultado son los vectores de clave y valor (KV) para cada token de entrada, que se almacenan en memoria. El tiempo que requiere esta fase se denomina TTFT — Time to First Token. El TTFT es lo que determina cuándo el usuario ve el primer carácter de la respuesta.
La fase de decode genera los tokens uno a uno, donde cada nuevo token depende de todos los anteriores. Es una operación secuencial — no se puede paralelizar dentro de una misma petición. La velocidad de generación se mide en tokens por segundo (tokens/sec).
El insight clave: el prefill está limitado por cómputo (satura los núcleos de la GPU), el decode está limitado por ancho de banda de memoria (satura el ancho de banda de la RAM de la GPU). Estas dos fases tienen cuellos de botella distintos y requieren optimizaciones diferentes. La mayoría de las implementaciones ingenuas sacrifican el throughput en favor de la simplicidad.
Batching estático: por qué falla con cargas mixtas
El enfoque clásico de batching es sencillo: se espera a que se llene un lote de peticiones, se envían todas a la vez, el modelo las procesa en paralelo, se devuelven los resultados. Luego se espera al siguiente lote.
El problema surge cuando las peticiones tienen longitudes muy distintas — que es la realidad habitual. Una petición quiere una respuesta de una línea; otra genera 500 tokens. El batching estático espera a que la petición más larga del lote termine. Los núcleos de la GPU que podrían haber comenzado una nueva petición permanecen inactivos. El uso efectivo de la GPU cae al 20–40 % en cargas mixtas típicas.
En cargas de trabajo reales, esto significa que la GPU por la que se está pagando espera la mayor parte del tiempo. No espera datos, no espera red — espera a una sola petición lenta.
Continuous batching: relleno dinámico del lote
El continuous batching (batching continuo) resuelve este problema de forma diferente. En lugar de esperar al final del lote, cada token generado se convierte en una oportunidad: el servidor consulta la cola de peticiones pendientes y añade inmediatamente nuevas al lote en curso. La petición que acaba de terminar libera su hueco para una nueva — sin interrupciones.
El resultado es drástico: con el mismo hardware, el continuous batching alcanza típicamente 2–3× más throughput que el batching estático en cargas mixtas. No porque la GPU sea más rápida, sino porque los ciclos no utilizados se rellenan con nuevas peticiones.
vLLM, SGLang y TGI implementan continuous batching de forma nativa. Ollama no lo implementa en su totalidad — está optimizado para uso en escritorio de un solo usuario, no para cargas de producción multi-usuario. Para un equipo con una decena de usuarios concurrentes, la diferencia es notable: con ocho peticiones paralelas, vLLM es significativamente más eficiente que Ollama con el mismo hardware. Una comparación detallada de los frameworks de serving está disponible en el artículo vLLM vs SGLang vs Ollama.
KV-cache: dónde «desaparece» la VRAM con contextos largos
Cada token que el modelo procesa — ya sea en la fase de prefill o en la de decode — produce un vector de clave y un vector de valor para cada capa de atención. Estos vectores deben almacenarse para que no haya que recalcularlos al generar el siguiente token. Este almacenamiento se denomina KV-cache.
El tamaño de la KV-cache crece linealmente con la longitud del contexto. Para dar una referencia: un modelo de 70B parámetros con una ventana de contexto de 128K necesita decenas de gigabytes solo para la KV-cache — por encima de la VRAM que ya ocupa el propio modelo. Con cuatro peticiones paralelas a la misma longitud de contexto, esa cifra se multiplica por cuatro.
Los modelos modernos implementan mayoritariamente Grouped Query Attention (GQA), que reduce drásticamente el tamaño de la KV-cache frente a la atención multi-head clásica al hacer que grupos de cabezas «query» compartan los mismos vectores «key» y «value». La mayoría de las familias actuales (Llama, Qwen, Mistral) ya incluyen GQA — es el estándar hoy, no la excepción.
Otra palanca es la cuantización de la KV-cache: reducir la precisión de los vectores KV a INT8 o FP8 puede reducir su tamaño a la mitad con una pérdida mínima en la calidad de los resultados. Los frameworks de serving de producción admiten esto como optimización opcional.
Consecuencia práctica: si la VRAM no es suficiente y estás considerando contextos largos, la KV-cache es el primer lugar donde buscar la causa. Antes de comprar una GPU adicional, merece la pena medir cuánta VRAM ocupa realmente la KV-cache en tu carga de trabajo típica.
PagedAttention: paginación virtual para la KV-cache
La gestión ingenua de la KV-cache genera otro problema: reserva memoria para la longitud máxima posible del contexto de antemano, aunque la mayoría de las peticiones sean mucho más cortas. Imagina que asignas 128K slots porque el modelo técnicamente lo soporta, pero la petición promedio tiene 2000 tokens. La mayor parte de la memoria asignada permanece vacía — y por la fragmentación no se puede reciclar eficientemente.
PagedAttention, la innovación clave de vLLM, resuelve este problema tomando inspiración de los sistemas operativos. Al igual que el SO pagina la RAM en bloques físicos y los mapea de forma virtual, PagedAttention divide la KV-cache en bloques de tamaño fijo (páginas) que se asignan dinámicamente según los tokens que la petición genera realmente. La memoria se asigna bajo demanda, no de antemano.
El resultado: la fragmentación de la KV-cache cae del típico 60–80 % de desperdicio a menos del 4 %. Esto significa que la misma VRAM puede atender muchas más peticiones paralelas, o el mismo número de peticiones con un contexto más largo.
Para despliegues en producción, esta diferencia impacta directamente en cuántas GPU se necesitan — y por tanto en los costes del serving.
Latencia vs. throughput: no son lo mismo
Una de las confusiones conceptuales más frecuentes: optimizar el throughput y optimizar la latencia son objetivos parcialmente opuestos.
El throughput mide cuántos tokens (o peticiones) genera el servidor por segundo en agregado — interesa en cargas batch, procesamiento offline de documentos, APIs de alto volumen.
La latencia mide con qué rapidez recibe un usuario concreto su respuesta — TTFT y tiempo total hasta el final de la generación. Interesa en aplicaciones interactivas, chatbots y copilotos.
El problema aparece cuando se aplica el continuous batching de forma agresiva: el servidor puede retrasar deliberadamente el envío de la respuesta para tener tiempo de rellenar un lote mayor — mayor throughput, peor latencia para el individuo. La mayoría de los frameworks de producción tienen parámetros (--max-num-seqs, scheduler-delay-factor) con los que se ajusta este equilibrio según las prioridades.
Para aplicaciones interactivas, la estrategia correcta suele ser priorizar el TTFT — un usuario que ve los primeros tokens en dos segundos percibe el sistema como «rápido» aunque la generación completa tarde más. El streaming de la respuesta (SSE o WebSocket) mejora aún más esta percepción sin cambiar el throughput real.
Al comparar soluciones de serving: SGLang alcanza en cargas con muchos prefijos repetidos un TTFT aproximadamente un 20 % menor que vLLM con el mismo hardware, lo cual es una diferencia notable en aplicaciones interactivas. Para serving batch, la diferencia es menor.
Prefix caching y compartición de KV-cache
Cuando múltiples peticiones comienzan con el mismo prompt de sistema — algo habitual en aplicaciones de producción — cada petición recalcula el mismo prefijo desde cero. Eso es un desperdicio: calcular el prefill de los mismos 500 tokens del prompt de sistema 1000 veces al día.
El prefix caching (o prompt caching) resuelve este problema: el servidor almacena los resultados KV del prefijo y cuando llega una nueva petición con el mismo inicio, los lee desde la caché en lugar de recalcularlos. El efecto es doble — reduce la latencia (el TTFT para un prefijo cacheado es cercano a cero) y también los costes computacionales.
SGLang implementa esto mediante RadixAttention — una caché LRU de valores KV organizada en un árbol radix, donde se localiza y comparte automáticamente el prefijo común más largo entre peticiones. En cargas de trabajo con prefijos largos y repetidos (RAG con el mismo contexto, chatbot multi-turno con el mismo prompt de sistema), la mejora de throughput es medible.
En el lado de los proveedores cloud de API, se implementa una función análoga: el prompt caching automático reduce el coste de los tokens de entrada en prefijos repetidos en torno a un 50–90 %. Esto es relevante también para arquitecturas híbridas donde algunas peticiones van a la nube — estructurar correctamente el prompt (prompt de sistema constante al principio, contenido dinámico al final) puede reducir significativamente la factura. El artículo Prompt caching y costes lo aborda en detalle.
Cómo extraer más de una GPU sin comprar hardware adicional
Resumen de las palancas prácticas que vemos en despliegues reales — ordenadas por impacto típico:
- 1.Migra a un framework de serving de producción — si estás ejecutando
Ollamacon varios usuarios concurrentes, migrar avLLMoSGLanges el cambio más impactante que puedes hacer. El continuous batching y PagedAttention ya están integrados.
- 1.Configura la cuantización adecuada — Q4_K_M o AWQ 4-bit reduce significativamente el footprint de memoria del modelo frente a FP16 (típicamente a un tercio o cuarto), lo que libera VRAM para lotes más grandes, con una pérdida de calidad inferior al 5–8 %. Más sobre los formatos en el artículo Cuantización LLM (GGUF, AWQ, GPTQ).
- 1.Activa la cuantización de la KV-cache — la KV-cache en FP8 o INT8 reduce el footprint de memoria a la mitad con un impacto mínimo en los resultados. En
vLLMesto se controla con el parámetro--kv-cache-dtype.
- 1.Optimiza para tu contexto real — si tus peticiones utilizan en promedio solo el 20 % del contexto máximo configurado, reducir
--max-model-lenlibera VRAM para más peticiones paralelas.
- 1.Aprovecha el prefix caching — si el prompt de sistema o el contexto RAG es el mismo en las distintas peticiones, dirígelas a un framework con RadixAttention (SGLang) o activa el prefix caching en
vLLM.
- 1.Monitoriza el uso real de la GPU —
nvidia-smi dmony las métricas del framework de serving muestran dónde está el cuello de botella. Un uso bajo de la capacidad de cómputo de la GPU junto con alta latencia suele indicar un problema en la gestión de la KV-cache o un batching subóptimo.
Cuándo tiene sentido usar varias GPU
El escalado horizontal (tensor parallelism entre varias GPU) está justificado en dos situaciones: el modelo no cabe físicamente en una sola GPU ni siquiera tras la cuantización, o se han agotado todas las optimizaciones en una GPU y el throughput sigue siendo insuficiente.
El tensor parallelism (--tensor-parallel-size 2 en vLLM) divide las matrices del modelo entre las GPU — cada GPU procesa una parte del cómputo y los resultados se sincronizan. Escala casi linealmente para la fase de prefill, con menor eficiencia en el decode (por la sobrecarga de sincronización).
Para la mayoría de los despliegues empresariales con un modelo y decenas de usuarios concurrentes, una sola GPU bien configurada con el framework de serving adecuado es más eficiente y económicamente más aceptable que dos GPU con Ollama. Comprar hardware antes de optimizar el software es un error frecuente — y innecesariamente caro.
Para saber qué GPU elegir concretamente y cuánta VRAM necesitas para un modelo y carga determinados, te remitimos a la guía práctica del artículo Qué GPU elegir para inferencia LLM.
Preguntas frecuentes
¿Qué es el TTFT y por qué importa?
El TTFT (Time to First Token) es el tiempo que transcurre desde que se envía la petición hasta que el servidor empieza a hacer streaming de los primeros tokens de la respuesta. Para las aplicaciones interactivas (chatbots, copilotos), el TTFT es determinante para la percepción de velocidad — un usuario que recibe el primer token en 1–2 segundos percibe el sistema como responsivo aunque la respuesta completa tarde 10 segundos. Un contexto largo y la KV-cache llena alargan el TTFT porque la fase de prefill tarda más.
¿Cuál es la diferencia entre PagedAttention y continuous batching?
Son dos optimizaciones ortogonales que resuelven problemas distintos. PagedAttention resuelve la gestión de memoria — reduce la fragmentación de la KV-cache al asignarla dinámicamente en páginas en lugar de reservarla de antemano. El continuous batching resuelve la planificación de peticiones — en lugar de esperar al final del lote, añade nuevas peticiones al ciclo en curso de forma continua. Ambas funcionan de forma conjunta y están implementadas en frameworks de producción como vLLM.
¿Vale la pena el prefix caching en despliegues pequeños?
Sí, si tienes un prompt de sistema o contexto RAG consistente que se repite en las peticiones. Incluso con una decena de usuarios concurrentes, el prefix caching puede reducir el TTFT entre un 40–70 % para peticiones con un prefijo cacheado largo. En el lado de las API cloud, esto impacta directamente en los costes — un prompt correctamente estructurado con un prefijo constante puede ahorrar decenas de puntos porcentuales en tokens de entrada.
¿Cuándo tiene sentido migrar de Ollama a vLLM?
Cuando tienes más de un usuario concurrente en un entorno de producción. Ollama es excelente para el escritorio del desarrollador, el prototipado y el acceso local de un solo usuario. Con múltiples peticiones paralelas, el continuous batching y PagedAttention de vLLM proporcionan un throughput notablemente mayor por el mismo coste de hardware. La migración es relativamente sencilla — vLLM proporciona una API compatible con OpenAI, por lo que cambiar la URL y la clave suele ser suficiente.
¿Cómo afecta la longitud del contexto al throughput?
De forma lineal y significativa. Un contexto más largo implica una KV-cache mayor por petición, lo que reduce directamente el número de peticiones que caben simultáneamente en la VRAM disponible. Un modelo con una ventana de contexto de 1M necesita del orden de cien gigabytes de KV-cache para una sola petición larga — algo inalcanzable para las GPU de producción habituales. Para la mayoría de los casos de uso reales, RAG con un contexto más corto es económicamente mucho más ventajoso que una ventana de contexto larga rellena con un documento completo. El artículo Ventana de contexto: cuándo ayudan realmente 1M de tokens aborda en detalle cómo decidir entre RAG y contexto largo.
*MP Industrial Solutions ayuda a las empresas a diseñar una arquitectura de LLM serving adecuada a su carga y presupuesto — desde la elección del framework de serving hasta la configuración de la KV-cache y la cuantización. Si estás gestionando la ralentización de un despliegue existente o planificando un despliegue en producción, estaremos encantados de analizar los números concretos y proponer una optimización.*
