Prefill vs decode: por qué se retrasa el primer token
Prefill limita el tiempo hasta el primer token por cómputo; decode limita los tokens por segundo por ancho de banda. Mide ambas fases por separado en tu equipo.
Prefill frente a decode, en un párrafo
Prefill frente a decode es la distinción que explica la mayoría de las dudas sobre la latencia de un LLM (modelo de lenguaje grande) autoalojado. Prefill lee todo el prompt en una sola pasada y está limitado por la capacidad de cómputo. Decode genera la respuesta un token cada vez y está limitado por el ancho de banda de memoria. El tiempo hasta el primer token corresponde a prefill. Los tokens por segundo corresponden a decode.
Ambas fases se ejecutan en la misma GPU (unidad de procesamiento gráfico), con los mismos pesos y dentro del mismo proceso, por lo que es natural tratarlas como una sola carga de trabajo. Se comportan como dos programas diferentes que comparten un dispositivo. Si las separa, muchos resultados aparentemente confusos dejan de serlo.
¿Por qué el prefill está limitado por el cómputo?
Prefill procesa el prompt completo una vez por cada capa. Un prompt de 2,000 tokens proporciona 2,000 filas de trabajo a cada multiplicación de matrices, por lo que la GPU realiza muchas operaciones aritméticas por cada byte de pesos que carga. Esta relación, operaciones aritméticas por byte transferido, se denomina intensidad aritmética, y prefill tiene una intensidad alta. El dispositivo funciona cerca de su límite de cómputo y el bus de memoria tiene capacidad disponible.
Prefill produce dos elementos: la caché KV (los tensores de claves y valores) para cada token del prompt y el primer token de salida. No llega nada al lector hasta que termina ese procesamiento, por lo que, en un servidor sin carga, prefill representa la mayor parte del tiempo hasta el primer token (TTFT). Según la definición de la guía de evaluación comparativa de NVIDIA, TTFT también incluye la espera en la cola de solicitudes y la latencia de red. Por eso, en un servidor con carga, la espera en la cola puede ser tan importante como el prompt.
El coste de prefill aumenta con la longitud del prompt. La parte lineal corresponde al trabajo de multiplicación de matrices de cada capa. La parte cuadrática corresponde a la atención, en la que cada token atiende a todos los tokens anteriores, y empieza a ser relevante con contextos largos. Por tanto, duplicar el prompt como mínimo duplica la parte de prefill de TTFT.
Puede observarlo en un minuto. Envíe un prompt de 200 tokens a su servidor y después otro de 2,000 tokens. Solicite el mismo número de tokens de salida en ambos casos. TTFT aumenta de forma considerable. La velocidad de generación después del primer token apenas cambia.
¿Por qué el ancho de banda de memoria limita el decode?
El decode produce un token por paso. Para producir ese token, la GPU debe leer de la memoria todos los pesos del modelo, usar cada peso en un par de operaciones y descartarlo. La intensidad aritmética es cercana a 1, por lo que las unidades de cómputo pasan la mayor parte del tiempo esperando.
El decode es lento porque cada token requiere leer de la memoria el modelo completo. Por eso, el bus de memoria marca el ritmo y las unidades de cómputo permanecen inactivas.
Por tanto, el límite de velocidad del decode en un solo flujo se calcula con una operación sencilla. Divide el ancho de banda de memoria entre los bytes que ocupan los pesos.
Los datos de este gráfico
[
{
"device": "CPU, dual channel DDR5-5600",
"mem_bandwidth_gb_s": 90,
"decode_ceiling_tok_s": 6
},
{
"device": "NVIDIA A10G",
"mem_bandwidth_gb_s": 600,
"decode_ceiling_tok_s": 38
},
{
"device": "NVIDIA L40S",
"mem_bandwidth_gb_s": 864,
"decode_ceiling_tok_s": 54
},
{
"device": "NVIDIA RTX 4090",
"mem_bandwidth_gb_s": 1008,
"decode_ceiling_tok_s": 63
},
{
"device": "NVIDIA A100 80GB SXM",
"mem_bandwidth_gb_s": 2039,
"decode_ceiling_tok_s": 127
},
{
"device": "NVIDIA H100 SXM",
"mem_bandwidth_gb_s": 3350,
"decode_ceiling_tok_s": 209
}
]La columna de ancho de banda contiene la cifra publicada por cada fabricante. La columna de límite contiene esa cifra dividida entre 16 GB, el tamaño de un modelo de 8 billion parámetros almacenado con una precisión de 16 bits. Es un cálculo aritmético, no el resultado de una prueba de rendimiento. La velocidad medida será inferior. Saber cuánto menor es resulta útil porque indica si debes corregir la pila de serving o el hardware.
Lee las 6 filas en orden y el patrón queda claro. Una CPU con DDR5 de doble canal mueve aproximadamente 90 GB/s, lo que limita el decode a cerca de 6 tokens por segundo para ese modelo. Una L40S alcanza aproximadamente 54. Una H100 SXM, con un ancho de banda publicado de 3350 GB/s, se sitúa cerca de 209.
Esta es también la razón por la que la cuantización es la palanca individual más eficaz para aumentar la velocidad de decode. Almacena los mismos pesos con 8 bits en lugar de 16 y reduces a la mitad los bytes de pesos leídos por token, por lo que el límite aproximadamente se duplica. Sin embargo, el límite no es el resultado final. La caché KV se sigue leyendo según su propio tamaño, y algunos formatos de 8 bits añaden trabajo por token, como la cuantización dinámica de activaciones por token que vLLM documenta para uno de sus esquemas FP8. Por eso, la mejora medida suele quedar por debajo de 2x. La palanca funciona por la misma razón por la que cambia el límite: mueves menos datos en memoria.
¿Cómo mido cada fase en mi propio servidor?
Ollama devuelve la separación en el cuerpo de la respuesta. Solicite una finalización sin streaming y lea los contadores.
curl -s http://localhost:11434/api/generate -d '{
"model": "llama3.2",
"prompt": "Explain memory bandwidth in two sentences.",
"stream": false
}' | jq '{prompt_eval_count, prompt_eval_duration, eval_count, eval_duration}'Use una etiqueta de modelo que haya descargado realmente. ollama list le mostrará las etiquetas disponibles. prompt_eval_count y prompt_eval_duration corresponden al prefill: el recuento de tokens del prompt y el tiempo empleado en procesarlo. eval_count y eval_duration corresponden al decode. Las duraciones están expresadas en nanosegundos, por lo que la velocidad de decode es eval_count / eval_duration * 1e9 y la velocidad de prefill es prompt_eval_count / prompt_eval_duration * 1e9. La referencia de la API de Ollama indica que prompt_eval_duration sólo incluye los tokens del prompt que no están en caché. Por tanto, calcule la velocidad de prefill a partir de un prompt que el servidor no haya procesado justo antes. En una misma petición, la velocidad de prefill debería ser mucho mayor que la velocidad de decode. El resto de esta explicación describe la causa de esa diferencia.
En un servidor compatible con OpenAI, como vLLM, curl puede medir el tiempo hasta el primer byte.
curl -N -s -o /dev/null \
-w 'pretransfer %{time_pretransfer}s first_byte %{time_starttransfer}s\n' \
http://localhost:8000/v1/completions \
-H 'Content-Type: application/json' \
-d '{"model": "meta-llama/Llama-3.1-8B-Instruct", "prompt": "Explain memory bandwidth.", "max_tokens": 128, "stream": true}'time_starttransfer es el momento en que llegó el primer byte del cuerpo de la respuesta. Por tanto, junto con "stream": true, representa el TTFT más el establecimiento de la conexión. Reste time_pretransfer para eliminar el coste de establecimiento. Ejecútelo dos veces y conserve el segundo resultado, porque la primera llamada puede incluir la carga en frío del modelo.
vLLM también publica la separación mediante métricas de Prometheus en /metrics. Ejecute curl -s http://localhost:8000/metrics | grep -E 'time_to_first_token|inter_token_latency' para obtener los histogramas vllm:time_to_first_token_seconds y vllm:inter_token_latency_seconds. Añada vllm:num_requests_running y vllm:num_requests_waiting para la profundidad de la cola, y vllm:kv_cache_usage_perc para la presión de la caché. Esos cinco nombres constituyen todo el panel.
Bajo carga, vllm bench serve --model <name> --num-prompts 200 --request-rate 4 genera carga en el servidor en ejecución e informa del tiempo hasta el primer token y de la latencia por token de salida con percentiles. Es la forma más directa de ver cómo compiten las dos fases en la pila que ya ejecuta. NVIDIA's GenAI-Perf y LLMPerf miden las mismas cantidades si prefiere una herramienta independiente. Antes de ajustar nada, establezca una línea base limpia. El método de medir tokens por segundo en un LLM local proporciona una medición que se mantiene después de reiniciar.
¿Por qué un prompt de sistema largo retrasa el primer token, pero no la velocidad de streaming?
Porque el prompt de sistema se procesa durante el prefill. No hace nada más. Se procesa una vez, en el mismo paso que el resto del prompt, antes de que aparezca el primer token. Después de ese paso, sólo existe como entradas de la KV cache, y el decode las lee junto con todo lo demás. Por tanto, sin prefix caching, un prompt de sistema de 3,000 tokens aumenta el TTFT en todas las solicitudes, pero apenas cambia los tokens por segundo.
Apenas, no exactamente. Esas entradas adicionales de la KV cache se vuelven a leer en cada paso de decode, por lo que un prompt muy largo también ralentiza un poco el decode. La siguiente sección explica este efecto.
La solución es evitar que se vuelva a calcular el mismo prefijo. Un servidor con prefix caching conserva la KV cache de un prefijo compartido y la reutiliza. Así, la segunda solicitud que contiene el mismo prompt de sistema omite por completo esa parte del prefill. vLLM denomina a esta función automatic prefix caching; consulte vllm serve --help para su versión, porque el valor predeterminado ha cambiado entre releases. Esta KV cache almacenada en la GPU es distinta de la caché de prompts por la que cobra un proveedor de API. Conviene leer la diferencia entre una KV cache y una caché de prompts antes de ajustar cualquiera de las dos.
¿Por qué la decodificación se ralentiza a medida que se llena el contexto?
Hay dos razones. Ambas están relacionadas con la caché KV.
La primera es el ancho de banda. En cada paso de decodificación, la atención lee las claves y los valores de todos los tokens anteriores. Los pesos representan un coste fijo por token. La caché KV crece con el contexto. Puede calcular su tamaño a partir de config.json del modelo: los bytes por token equivalen a 2 multiplicado por num_hidden_layers, por num_key_value_heads, por la dimensión de la cabeza y por los bytes por elemento. Use head_dim cuando aparezca en la configuración, y hidden_size dividido por num_attention_heads sólo cuando no aparezca, porque algunos modelos definen ambos valores de forma independiente. Mistral NeMo, por ejemplo, tiene un ancho de 5,120 y 32 cabezas, pero la dimensión de cabeza es 128, no 160. El 2 inicial cuenta una clave y un valor.
En una arquitectura común de 8 billion de parámetros, con 32 capas, 8 cabezas de clave y valor mediante GQA (grouped query attention), una dimensión de cabeza de 128 y una precisión de 16 bits, el cálculo es 2 x 32 x 8 x 128 x 2 = 131,072 bytes, aproximadamente 128 KiB por token. Una conversación de 8,000 tokens contiene, por tanto, aproximadamente 1 GB de caché KV por solicitud.
La segunda es la capacidad. Ese 1 GB es memoria que no puede contener los pesos ni el contexto de otro usuario. El servidor dimensiona su grupo de caché KV una sola vez durante el arranque, en vLLM mediante --gpu-memory-utilization, y cuando el grupo se llena, las solicitudes nuevas esperan. Que vllm:num_requests_waiting aumente mientras vllm:kv_cache_usage_perc se mantiene cerca de 1 es la señal exacta de ese estado. Algunas pilas interrumpen una solicitud en ejecución y vuelven a calcular su caché más tarde en lugar de ponerla en cola. El usuario lo percibe como una pausa en mitad del flujo.
Un contexto largo tiene un coste doble: más trabajo de prefill al principio y más memoria leída por token durante el resto de la respuesta.
¿Por qué el batching mejora el rendimiento y empeora la latencia de cola?
Porque el decode está limitado por el ancho de banda, las solicitudes adicionales tienen un coste computacional reducido. Una lectura de los pesos puede producir un token para cada secuencia del batch, por lo que el rendimiento total aumenta casi linealmente con el tamaño del batch hasta que se agota el pool de KV o el batch crece lo suficiente como para volver a estar limitado por el cómputo. El batching continuo reconstruye el batch en cada paso, de modo que una solicitud terminada sale y otra en cola entra sin esperar a las solicitudes vecinas.
El coste aparece en los percentiles. El siguiente token de cada usuario ahora espera la parte más lenta de un paso compartido, por lo que p50, la mediana, sigue siendo aceptable mientras p99, el 1% de solicitudes más lentas, aumenta. El p99 es lo que perciben los usuarios, porque corresponde a la pausa en mitad de una frase.
El prefill lo hace más evidente. Cuando llega un prompt grande durante el procesamiento, ocupa el dispositivo durante un paso largo y todos los usuarios que están recibiendo tokens ven una interrupción. El prefill por bloques elimina gran parte de este efecto al dividir un prompt largo en partes y mezclar cada parte con los batches de decode. En octubre de 2026, el motor vLLM V1 lo hace de forma predeterminada y expone el equilibrio mediante --max-num-batched-tokens. La documentación de ajuste de vLLM explica claramente el compromiso: los valores más pequeños, alrededor de 2048, ofrecen una mejor latencia entre tokens (ITL) porque menos operaciones de prefill interrumpen los decodes, mientras que los valores más grandes ofrecen un mejor TTFT porque caben más tokens de prefill en un solo batch. Ese único flag expone el equilibrio entre prefill y decode como un número que se puede ajustar. El punto en el que el p99 deja de ser aceptable depende de la capacidad, y cuántos usuarios simultáneos puede atender un LLM autoalojado permite analizarlo con las mismas métricas.
¿Por qué una GPU más grande a veces no cambia nada?
Porque «más grande» suele significar más capacidad de cómputo, y el decode no necesita más cómputo.
Compare dos filas de la tabla anterior. La A100 80GB ofrece un ancho de banda publicado de 2039 GB/s, frente a los 864 GB/s de la L40S, y el techo de decode sigue exactamente la misma relación: 127 tokens por segundo frente a 54. La RTX 4090 es una tarjeta muy rápida según la mayoría de las métricas, y sus 1008 GB/s sitúan su techo en 63. Independientemente de las demás diferencias entre dos tarjetas, el decode de un único flujo sigue la línea del ancho de banda indicada en la hoja de especificaciones.
Por tanto, hay dos formas de acelerar el decode: leer menos bytes por token (cuantizar los pesos o ejecutar un modelo más pequeño) o comprar más ancho de banda. El prefill es el caso contrario. Necesita capacidad de cómputo, por lo que una tarjeta más rápida realmente reduce el TTFT con prompts largos. Si el problema es que el primer token tarda cuatro segundos, un hardware mejor puede solucionarlo. Si el problema es que el texto aparece lentamente, probablemente no.
¿Debe ejecutar prefill y decode en workers separados?
Las grandes pilas de serving hacen exactamente esto. La técnica se denomina desagregación de prefill y decode. Un grupo de workers ejecuta sólo prefill y un segundo grupo ejecuta sólo decode. La caché KV que construye el primer grupo se transfiere al segundo mediante una interconexión rápida. Funciona porque cada fase requiere hardware y planificación diferentes. Prefill necesita capacidad de cómputo y lotes grandes de tokens. Decode necesita ancho de banda y muchas secuencias simultáneas. Separarlas permite escalar cada grupo por separado y evita que un prompt enorme bloquee todos los streams activos.
En un solo VPS (servidor privado virtual) con una GPU, casi nunca compensa. Dividiría un único dispositivo contra sí mismo y convertiría un puntero en una transferencia de red de gigabytes de caché. La técnica resulta útil cuando hay suficientes aceleradores para dedicar máquinas completas a cada fase y suficiente tráfico estable para mantener ocupados ambos grupos. Por debajo de ese nivel, chunked prefill ofrece casi el mismo aislamiento con un solo flag.
Qué cambiar cuando el valor es malo
Cuando el TTFT es demasiado alto:
- Acorte el prompt. El coste del prefill depende de los tokens del prompt y, sin el almacenamiento en caché del prefijo, el system prompt se procesa en cada solicitud.
- Active el almacenamiento en caché del prefijo para que un prefijo repetido se procese una vez en lugar de en cada solicitud.
- Aumente
--max-num-batched-tokenspara procesar más trabajo de prefill en cada paso. - Compruebe la cola antes de culpar al modelo. Un valor de
vllm:num_requests_waitingsuperior a cero indica que la solicitud todavía no había comenzado, lo que señala un problema de capacidad.
Cuando los tokens por segundo son demasiado bajos:
- Cuantice los pesos. Menos bytes por peso significa menos bytes leídos por token.
- Compare el ancho de banda de memoria publicado de su tarjeta con el gráfico anterior y compruebe qué tan cerca está del límite.
- Reduzca
--max-num-batched-tokenspara que el prefill interrumpa con menos frecuencia el decode. - Compruebe la longitud del contexto. Una conversación que ha crecido hasta miles de tokens lee una caché KV mucho mayor en cada paso.
El runtime también importa aquí, porque Ollama y vLLM programan el prefill y el decode de forma diferente, y un ajuste que ayuda a uno puede no hacer nada en el otro. Mida primero, en ambas fases, y después cambie una sola cosa.
FAQ
¿Por qué el primer token tarda segundos, pero los demás se generan rápido?
La espera corresponde al prellenado y la generación progresiva corresponde a la decodificación. El prellenado procesa todo el prompt en una única pasada limitada por la capacidad de cómputo antes de generar cualquier salida, por lo que su coste aumenta con la longitud del prompt. Después, la decodificación genera un token por paso a una velocidad determinada por el ancho de banda de memoria, que es casi independiente de la longitud del prompt. La causa habitual es incluir un prompt de sistema largo en cada petición. La caché de prefijos elimina el coste de procesar de nuevo esa parte.
¿Un prompt más largo reduce los tokens por segundo?
Un poco, y por un motivo distinto del TTFT. En cada paso de decodificación se leen las claves y los valores de todos los tokens anteriores, por lo que una caché KV más grande implica leer más bytes por token. En una configuración habitual de 8 billion parameters, la caché ocupa aproximadamente 128 KiB por token, así que un contexto de 8,000 tokens implica leer aproximadamente 1 GB en cada paso. El efecto principal de un prompt largo sigue produciéndose en el TTFT, no en la velocidad de generación progresiva.
¿Qué especificación de la GPU permite predecir la velocidad de decodificación?
El ancho de banda de memoria. Si divide el ancho de banda publicado entre el tamaño de los pesos en memoria, obtiene el límite teórico para un único flujo. Una tarjeta con más capacidad de cómputo, pero con el mismo ancho de banda, no generará tokens más rápido. Por eso cuantizar los pesos a 8 bits casi duplica el límite de decodificación: reduce a la mitad los bytes de pesos que se leen por token. La mejora medida suele ser menor porque la caché KV sigue leyéndose con su propio tamaño y algunos formatos de 8 bits añaden trabajo por token, como cuantizar las activaciones.
¿Por qué aumenta el rendimiento cuando añado usuarios, pero cada usuario parece ir más lento?
Una lectura de los pesos sirve para generar un token de cada secuencia del lote, por lo que el total de tokens por segundo aumenta con el tamaño del lote. Cada token individual debe esperar ahora a un paso compartido, así que la latencia por usuario aumenta al mismo tiempo. Supervise la latencia entre tokens p99, no el rendimiento agregado, y compruebe vllm:num_requests_waiting para determinar si las peticiones están en cola en lugar de ejecutándose.