English · Español
04 — Inferencia distribuida: TP para servir y prefill/decode desagregados¶
🇪🇸 Inferencia no es entrenamiento. No hay backward, no hay gradientes, no hay optimizador. Lo que sí hay: KV cache que crece por token, peticiones concurrentes con shapes distintos, y el famoso desbalance prefill (compute-bound) vs decode (memory-bound). La paralelización para servir tiene reglas distintas.
El entrenamiento distribuido y la inferencia distribuida tienen cuellos de botella diferentes. Esta página mapea las diferencias y cubre dos patrones: inferencia tensor parallel (la que tocamos en el lab 02) y prefill/decode desagregados (la frontera actual; cobertura conceptual solamente).
Por qué la inferencia es distinta del entrenamiento¶
En entrenamiento:
- Forward + backward + optimizador por step.
- Cada step tiene la misma shape (después de hacer padding al máximo).
- Puedes meter muchas secuencias en un batch → alta intensidad aritmética → compute-bound.
- La comunicación es una vez por step (all-reduce de DDP) o varias veces (ZeRO/TP), pero predecible.
En inferencia:
- Solo forward. Sin gradientes, sin estado del optimizador, sin momentum de Adam.
- Las peticiones llegan con longitudes de prompt distintas y longitudes de salida distintas → difícil hacer batch perfecto.
- Prefill (procesar el prompt) es compute-bound (matmuls grandes matriz-matriz).
- Decode (un token cada vez después del prefill) es memory-bound — cargar una fila de pesos, hacer un matmul matriz-vector, repetir.
- Las dos fases tienen perfiles de hardware opuestos. Una sola GPU ejecutando ambas está sobre- e infrautilizada en momentos distintos.
Consecuencias:
- Los clústeres de inferencia necesitan objetivos de optimización distintos. El rendimiento (throughput) por GPU importa menos que la latencia por petición (TTFT, ITL).
- Las estrategias de sharding que funcionan en entrenamiento pueden no funcionar en inferencia, y viceversa.
- El mismo tamaño de modelo que requiere TP en 16 GPUs para entrenar puede caber en 1 GPU para inferencia (sin gradientes, sin estado del optimizador — reducción de memoria 4×).
Inferencia tensor parallel¶
El esquema TP de theory/02-parallelism-flavors.md también funciona para inferencia: hacer shard de los pesos de cada capa, ejecutar forward con all-reduces intra-capa.
Qué cambia respecto al entrenamiento¶
- Sin backward → no hace falta el segundo lote de all-reduces.
- El KV cache se hace shard en el mismo eje que las proyecciones Q/K/V — cada worker TP guarda sus
n_heads / Nheads de KV. - El all-reduce ocurre en el residual stream tras la proyección de salida de attention y tras la down-projection del MLP — mismo patrón que en entrenamiento, la mitad del volumen.
¿Cuándo merece la pena la inferencia TP?¶
| Situación | ¿Usar inferencia TP? |
|---|---|
| El modelo cabe en una GPU, un solo usuario | No. GPU única es más simple y rápida (sin comm). |
| El modelo cabe en una GPU, muchos usuarios | Quizá. TP recorta latencia por petición a cambio de más comm por token. |
| El modelo es demasiado grande para una GPU | Sí. TP es la opción estándar. |
| Requisito de TTFT ajustado | TP puede ayudar — divide el cómputo de prefill entre workers. |
| Requisito ajustado de tokens/seg/$ | Una GPU + continuous batching suele ganar (sin comm). |
Para el tutor de gramática: el modelo cabe en una calculadora, así que productivamente la inferencia TP es un desperdicio. El lab 02 la ejecuta igualmente porque la única manera de ver el patrón all-reduce es escribir el all-reduce. Resultado esperado: 2-GPU TP es más lento que 1-GPU para este modelo diminuto. Esa es la lección. El volumen de comm cruza al volumen de cómputo; nunca desplegarías así.
El escenario prospectivo: un tutor de gramática hipotético de 600k-formas (5 lenguas × 20 verbos × 5 tiempos × 3 personas × paráfrasis) con \(d_{\text{model}} = 4096\) necesitaría TP-sharding de la tabla de embeddings solo para caber. Ese es el punto en el que el patrón del lab 02 se vuelve económicamente correcto.
Prefill/decode desagregados¶
Un patrón frontera para servir (Splitwise, DistServe, Mooncake): workers separados se encargan de prefill vs decode, en lugar de un solo worker haciendo ambos.
Por qué desagregar¶
Prefill y decode tienen hardware óptimo distinto:
- Prefill: compute-bound. Quiere FLOPs/$ altos. Una A100 o H100 con SM count alto va estupenda. Sensible a latencia (esto es el TTFT).
- Decode: memory-bound. Quiere ancho de banda de memoria/$ alto. La H100 tiene ambos; una Hopper o incluso una Gaudi pueden valer. También sensible a latencia (esto es el ITL).
En un clúster compartido único, las instancias de prefill ocasionalmente quedan ociosas (no llegan prompts nuevos) mientras las de decode trabajan a tope (muchos usuarios concurrentes generando). La aritmética para un clúster co-localizado dice que aprovisionas para el pico de cualquiera — derrochador.
Desagregar significa: aprovisionar un pool pequeño de prefill dimensionado para la tasa de llegada de prompts, y un pool más grande de decode dimensionado para la generación concurrente. El KV cache se transfiere del worker de prefill al worker de decode una vez por petición (RDMA o NVLink). La transferencia es un coste único amortizado sobre todo el decode.
Esquema del patrón¶
Cliente → Router → Pool de prefill (pequeño, ajustado a cómputo)
│ (hand-off del KV cache, RDMA)
▼
Pool de decode (más grande, ajustado a ancho de banda)
│ (stream de tokens)
▼
Cliente (streaming)
El router decide cuándo reenviar; la transferencia del KV cache es el nuevo componente de latencia a medir (sumado al TTFT).
Por qué no entra en este currículo¶
Implementar prefill/decode desagregados requiere:
- Dos pools de GPU escalados por separado (≥ 4 GPUs).
- Interconexión con capacidad RDMA para la transferencia de KV.
- Un router de peticiones que conozca el ciclo de vida de la petición.
Coste total del sistema: $5+/hr. Presupuesto de la Fase 35: $5 en total. La cuenta dice leer papers, no implementar. El lab 03 incluye una lectura anotada breve del paper de DistServe.
Speculative decoding¶
Una optimización distinta, mencionada aquí porque a menudo se despliega junto con TP / inferencia desagregada.
Un modelo draft pequeño propone los siguientes \(k\) tokens de forma barata; el modelo target (real) verifica los \(k\) en un solo forward pass y acepta el prefijo correcto más largo. Resultado: en lugar de un token por forward, obtienes 2–5 tokens por forward de media.
Para el tutor de gramática: el modelo draft tendría que ser aún más diminuto que el tutor ya diminuto. Probablemente un modelo de n-gramas de la Fase 14. Conceptualmente factible, en la práctica no merece el coste de implementación.
La Fase 36 cubre Medusa / EAGLE / Lookahead con más profundidad (esas son variantes arquitectónicas de speculative decoding).
Checklist de inferencia distribuida¶
Al diseñar un setup de inferencia distribuida:
- ¿Cabe el modelo en una GPU? Si sí, empieza con GPU única.
- ¿Eres sensible a la latencia (TTFT/ITL)? Si sí, considera TP dentro de un nodo.
- ¿Eres sensible al throughput (tokens/seg/$)? Primero GPU única + continuous batching; añade TP solo si la memoria fuerza.
- ¿Tu KV cache es > 50% de la memoria? Mira PagedAttention (vLLM) antes de añadir más GPUs.
- ¿Están desbalanceadas las cargas de prefill y decode? Prefill/decode desagregados — pero solo a escala suficiente.
Para el tutor de gramática: la respuesta es "no, no, no, no, no" — una GPU (o CPU) basta. El lab 02 viola la checklist intencionalmente para enseñar.
Lo que esta fase NO cubre¶
- Implementar prefill/decode desagregados. Solo lectura.
- Implementación de speculative decoding (Medusa, EAGLE, Lookahead). Territorio de la Fase 36; aquí solo vocabulario.
- Routing de inferencia cross-region / cross-AZ. Territorio de production-ops; Fase 38.
- Autoscaling de inferencia por tokens/seg. Fase 38.
- Offloading de KV cache entre workers (pools de memoria CXL, etc.). Frontera; solo vocabulario.
Siguiente: lab/00-cloud-budget-and-tooling.md.