37 Servir y desplegar
Dónde estamos. Cierra la Parte VI. Ya tenemos el modelo entrenado (Parte IV), comprimido (Cap. 35) y con atención eficiente (Cap. 34). Falta lo último y más práctico: ponerlo en producción. Servir un LLM tiene su propia física —dos fases con cuellos de botella opuestos, una caché que limita cuántos usuarios caben, y una tensión constante entre rapidez para uno y rendimiento para todos—. Esto es el capítulo de los sistemas.
37.1 La idea en una frase
Servir bien un LLM consiste en mantener la GPU ocupada pese a que generar es intrínsecamente secuencial: el arte está en agrupar muchas peticiones y gestionar la caché KV, equilibrando throughput (tokens/seg totales) contra latencia (rapidez para cada usuario).
37.2 Conceptos clave y su papel en el transformer
Antes de entrar en detalle, definimos los términos de este capítulo y para qué sirve cada uno dentro de un transformer:
- Prefill. Definición: procesar todo el prompt de golpe (en paralelo). En el transformer: es compute-bound (satura la GPU) y marca el TTFT.
- Decode. Definición: generar la respuesta token a token. En el transformer: es bandwidth-bound (relee la caché KV cada paso) → el cuello de botella del serving.
- TTFT (time-to-first-token). Definición: cuánto tardas en ver la primera palabra. En el transformer: lo domina el prefill; clave en chat.
- TPOT / ITL. Definición: tiempo por cada token de salida. En el transformer: lo domina el decode; es la “velocidad” percibida.
- Throughput vs goodput. Definición: tokens/seg totales vs peticiones/seg que cumplen un SLO (objetivo de latencia). En el transformer: maximizar throughput a secas puede incumplir la latencia de cada usuario.
- Batching continuo. Definición: agrupar peticiones a nivel de iteración (no de lote fijo), metiendo y sacando peticiones cada paso. En el transformer: mantiene la GPU llena pese a que las respuestas acaban en momentos distintos.
- Caché KV (en serving). Definición: la memoria de claves/valores ya calculados (Cap. 20). En el transformer: crece con el lote y la longitud y compite con los pesos por la HBM → limita cuántas peticiones caben.
- Prefill desagregado / troceado. Definición: separar o trocear el prefill para que no estanque los decodes en curso. En el transformer: cumple a la vez los objetivos de TTFT y TPOT.
Con esto en mano, abrimos la caja del servidor.
37.3 Las dos fases de la inferencia (y por qué chocan)
Generar con un LLM tiene dos fases muy distintas, y entender que tienen cuellos de botella opuestos es la clave de todo el capítulo:
- Prefill: procesar el prompt entero. Como todos los tokens de entrada se conocen, es una multiplicación matriz-matriz masiva y paralela que satura la GPU → está limitada por cómputo (compute-bound).
- Decode: generar la respuesta un token cada vez. Cada token nuevo necesita releer los estados (K, V) de todos los anteriores → es una operación matriz-vector de baja intensidad que infrautiliza el cómputo y está limitada por ancho de banda (bandwidth-bound): lo que manda es traer datos de la HBM (pesos + caché KV), no calcular.
El decode es el cuello de botella del serving, y es justo donde la caché KV (Cap. 20) se relee en cada paso. De ahí la tensión central: meter más peticiones a la vez sube el throughput total, pero ralentiza el token-a-token de cada usuario.
🧩 Analogía — leer la pregunta vs escribir la respuesta. El prefill es leer la pregunta entera de un vistazo (rápido, en paralelo). El decode es escribir la respuesta palabra por palabra, releyendo tus notas en cada palabra (lento, secuencial). El servidor se atasca escribiendo, no leyendo.
37.4 Las métricas que importan
No hay un solo número. Servir se mide en varios ejes que tiran en direcciones distintas:
- Throughput: tokens de salida por segundo sumando todos los usuarios.
- TTFT (time-to-first-token): cuánto tarda el usuario en ver la primera palabra —lo domina el prefill—.
- TPOT / ITL (tiempo por token de salida / latencia entre tokens): la velocidad percibida —la domina el decode—.
- La latencia total ≈ TTFT + TPOT × (nº de tokens).
- Goodput: las peticiones/seg que el sistema sostiene cumpliendo un SLO (p. ej. P90 TTFT < 200 ms y TPOT < 50 ms). Es más honesto que el throughput a secas: puedes tener mucho throughput y poco goodput si los usuarios incumplen su objetivo de latencia (Zhong et al. 2024).
Por eso se optimiza distinto según el caso: un chat prioriza TTFT y TPOT bajos; un trabajo por lotes (offline) maximiza throughput bruto.
🧩 Analogía — el primer plato y el ritmo. El TTFT es cuánto tarda el camarero en traerte el primer plato; el TPOT es el intervalo entre platos. Un comensal impaciente quiere ambos cortos; un bufé (lotes) solo quiere servir el máximo de platos por hora a toda la sala.
37.5 Batching continuo: la gran victoria
El problema del lote estático: si juntas N peticiones y esperas a que terminen todas, el lote no se libera hasta la más larga —y como las longitudes de salida varían de forma impredecible, las peticiones ya acabadas mantienen slots ociosos ocupando la GPU—.
El batching continuo (o iteration-level, o in-flight batching) lo arregla programando a nivel de token/iteración (Yu et al. 2022): tras cada paso, saca las peticiones terminadas y mete nuevas, manteniendo la GPU saturada. Es el mayor salto de throughput del serving moderno (Orca lo combina con selective batching: la atención, con longitudes distintas por secuencia, se trata aparte). Lo implementan vLLM y TensorRT-LLM.
🧩 Analogía — el taxi compartido. El lote estático es una furgoneta que no arranca el siguiente viaje hasta vaciarse del todo. El batching continuo es un taxi compartido que recoge y deja pasajeros sin parar: en cuanto baja uno, sube otro, y el coche nunca va medio vacío.
Las cifras de aceleración del batching continuo (se han reportado hasta ~23× frente a lote estático ingenuo) son muy dependientes de la carga: ese máximo solo aparece con alta varianza en la longitud de las respuestas; con respuestas de longitud parecida, todos los sistemas convergen a ~1×. Trátalo como “hasta X× en las condiciones reportadas”, nunca como garantía.
37.6 Gestionar la caché KV al servir
La caché KV es la restricción vinculante del serving: crece linealmente con el tamaño del lote y la longitud de secuencia, y compite con los pesos por la HBM. Cuánta KV quepa decide cuántas peticiones puedes batchear —y, por tanto, tu throughput—.
Aquí entra PagedAttention / vLLM (Kwon et al. 2023) (su mecánica, en el Cap. 34). Su impacto en serving: al guardar la caché en bloques estilo memoria virtual, elimina la fragmentación (casi cero desperdicio) → caben lotes mucho mayores → más throughput (hasta 2-4× en las condiciones reportadas, a igual latencia). Y permite compartir prefijos: si muchas peticiones comparten el mismo system prompt o ejemplos few-shot, sus bloques de KV se comparten físicamente (automatic prefix caching) en vez de recomputarse.
37.7 Prefill desagregado y troceado (2024)
Un problema fino: prefill (compute-bound) y decode (bandwidth-bound) interfieren si conviven. Un prefill largo monopoliza la GPU y estanca los decodes en curso de otros usuarios → dispara su TPOT. Dos soluciones modernas:
- Desagregación — DistServe (Zhong et al. 2024): poner el prefill y el decode en GPUs/instancias separadas, cada fase optimizada por su lado, eliminando la interferencia (a costa de transferir la caché KV entre ellas). Optimiza goodput bajo SLOs de TTFT y TPOT (hasta 7,4× más peticiones en las condiciones reportadas).
- Prefill troceado — Sarathi-Serve (Agrawal et al. 2024): trocear el prefill largo en fragmentos e intercalarlos con los decodes en curso en el mismo lote, sin pausarlos (stall-free batching) → equilibra TTFT y TPOT en las mismas GPUs.
Son dos estrategias —separación espacial vs intercalado temporal— al mismo problema de interferencia.
37.8 Otras palancas (que ya conoces)
El serving compone casi todo lo anterior:
- Decodificación especulativa (Cap. 29) → baja la latencia (mismo resultado, más rápido).
- Cuantización (Cap. 35) → modelos más grandes caben, o más lote en la misma HBM.
- Paralelismo de tensor/pipeline (Cap. 25) → modelos que no caben en una GPU.
- Multi-LoRA — S-LoRA (Sheng et al. 2023): un modelo base + miles de adaptadores en memoria de host, intercambiados al vuelo, batcheando peticiones de adaptadores distintos juntas (Cap. 28).
37.9 Compilación y kernels (situar)
Una capa transversal de eficiencia: la compilación de grafo y la fusión de kernels juntan varias operaciones en un solo kernel optimizado → menos sobrecoste de lanzamiento y menos viajes a memoria. Herramientas: torch.compile (PyTorch), TensorRT-LLM (NVIDIA: kernels fusionados + in-flight batching + KV paginada + cuantización) y ONNX Runtime (fusión de operadores multiplataforma). No hace falta entrar al detalle: la idea es generar kernels que muevan menos datos, coherente con que el decode es bandwidth-bound.
37.10 El marco de decisión (honesto)
Desplegar es navegar un triángulo latencia–coste–throughput (con la calidad de fondo). En la práctica:
- Tienes un presupuesto de latencia y maximizas throughput dentro de él.
- El tamaño de lote es la palanca principal: más lote → más throughput, más latencia por usuario.
- El coste lo domina el decode + la memoria de KV-cache, así que casi todas las optimizaciones atacan exactamente eso.
Casi todos los “X× más rápido/barato” del serving (batching continuo, PagedAttention, DistServe, S-LoRA…) son muy específicos de la carga: dependen del baseline, la GPU y su interconexión, el tamaño del modelo y las longitudes de entrada/salida. Léelos como “hasta X× bajo las condiciones del paper”, y mide en tu propia carga antes de creer cualquier curva.
37.11 Puente con nuestro tema (breve y honesto)
El cuello de botella del decode es la caché KV (se relee en HBM cada paso), y esa caché es la restricción vinculante de cuántas peticiones caben en el lote. Nuestra ventana D_f derivada de γ (Cap. 20) acota cuánta KV hay que retener por petición → reducir la longitud efectiva de la caché significa más peticiones a la misma HBM, es decir, toca directamente la restricción de serving. (Honesto: es una observación propia del libro; las fuentes de serving no hablan de γ ni de D_f.)
tafagent calcula el presupuesto de KV a partir de γ (Cap. 20): cuánta caché necesita de verdad tu modelo a la longitud objetivo. Como esa caché es lo que limita el lote (y por tanto el throughput), conocerla de antemano te ayuda a dimensionar el servicio —cuántos usuarios concurrentes caben en tu GPU—.
37.12 Resumen
- Dos fases: prefill (compute-bound, marca TTFT) y decode (bandwidth-bound, marca TPOT) → el decode es el cuello de botella, y es donde se relee la caché KV.
- Métricas: throughput vs goodput (cumpliendo SLO); latencia ≈ TTFT + TPOT × tokens. Chat optimiza latencia; batch, throughput.
- Batching continuo (Yu et al. 2022): programar por iteración (meter/sacar peticiones cada paso) mantiene la GPU llena → gran salto de throughput.
- Caché KV: la restricción vinculante; PagedAttention (Kwon et al. 2023) casi cero desperdicio + compartir prefijos → más lote, más throughput.
- Prefill desagregado (DistServe) o troceado (Sarathi-Serve): evitan que un prefill largo estanque los decodes → cumplen TTFT y TPOT a la vez.
- Compone: especulativa (latencia), cuantización (capacidad), paralelismo (tamaño), multi-LoRA (S-LoRA); compilación/fusión de kernels.
- Honesto: los “X×” son específicos de la carga —mide en la tuya—.
Siguiente (Parte VII): dejamos la ingeniería y volvemos a la pregunta de fondo del libro —¿qué está pasando de verdad dentro?—: interpretabilidad mecanicista, nuestro audit de fórmulas (verificado/folclore/numerología), el mapa del paisaje 2026 y la ética.
37.13 Ejercicios
- Dos fases. ¿Por qué el prefill es compute-bound y el decode bandwidth-bound? ¿Cuál es el cuello de botella del serving y por qué?
- Métricas. Define TTFT y TPOT y di qué fase domina cada uno. ¿Por qué el goodput es más honesto que el throughput?
- Batching continuo. ¿Qué desperdicia el lote estático y cómo lo evita el continuo? ¿Por qué el “23×” no es una garantía?
- Caché KV. ¿Por qué la caché KV limita cuántas peticiones caben en el lote? ¿Qué dos cosas aporta PagedAttention al servir?
- Interferencia. ¿Cómo estanca un prefill largo a los decodes en curso, y cómo lo resuelven DistServe y Sarathi-Serve?
- Honestidad. ¿Por qué un “5× más rápido” de un paper de serving puede no cumplirse en tu despliegue?