SSD Nodes Learn 🎉 VPS desde $5.50/mes
Guías Matt ConnorPor Matt Connor

Prefill frente a decode: por qué el token uno tarda

Prefill limita el tiempo hasta el primer token y decode los tokens por segundo. Mida ambas fases por separado en su propia GPU para detectar el cuello de botella.

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 todo el prompt una vez en cada capa. Un prompt de 2,000 tokens proporciona 2,000 filas de trabajo para 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 se muestra nada al lector hasta que termina ese procesamiento, por lo que el tiempo de prefill y el tiempo hasta el primer token (TTFT) son prácticamente la misma medición.

El coste de prefill aumenta con la longitud del prompt. La parte lineal corresponde al trabajo de las multiplicaciones de matrices en 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 el TTFT.

Puede observarlo en un minuto. Envíe a su servidor un prompt de 200 tokens y después otro de 2,000 tokens. Solicite el mismo número de tokens de salida en ambos casos. El TTFT aumenta considerablemente. La velocidad de streaming después del primer token apenas cambia.

¿Por qué el decode está limitado por el ancho de banda de memoria?

El decode genera un token por paso. Para generar ese único token, la GPU debe leer de 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 memoria el modelo completo. Por eso, el bus de memoria marca el ritmo y las unidades de cómputo permanecen inactivas.

Esto hace que el límite superior de la velocidad de decode en un único flujo sea una operación aritmética que puede calcularse sobre el papel. Divida el ancho de banda de memoria entre los bytes que ocupan los pesos.

ChartPublished memory bandwidth and the decode ceiling it implies for a 16 GB model
The data behind this chart
[
  {
    "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 de especificación publicada por cada fabricante. La columna de límite superior 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 sea resulta útil porque indica si debe corregir la pila de serving o el hardware.

Lea 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 cerca de 54. Una H100 SXM, con un ancho de banda publicado de 3350 GB/s, se sitúa cerca de 209.

Por esto, la cuantización también es la palanca individual más importante para aumentar la velocidad de decode. Almacene el mismo modelo con 8 bits en lugar de 16 y reducirá a la mitad los bytes leídos por token, por lo que el límite superior se duplicará aproximadamente. No ha añadido capacidad de cómputo. Ha movido menos datos de 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 se la mostrará. prompt_eval_count y prompt_eval_duration corresponden al prellenado: el número de tokens del prompt y el tiempo empleado en procesarlo. eval_count y eval_duration corresponden a la decodificación. Las duraciones están expresadas en nanosegundos, por lo que la velocidad de decodificación es eval_count / eval_duration * 1e9 y la velocidad de prellenado es prompt_eval_count / prompt_eval_duration * 1e9. En una misma solicitud, la tasa de prellenado suele ser mucho mayor que la tasa de decodificación. El resto de esta guía explica 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 lo que, 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 esta separación como 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.

Con 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 mediante percentiles. Es la única forma de ver cómo compiten ambas fases. Antes de ajustar nada, obtenga una línea base limpia: el método de medición de tokens por segundo en un LLM local proporciona una que se conserva tras reiniciar.

¿Por qué un prompt de sistema largo retrasa el primer token, pero no la velocidad de streaming?

Porque el prompt de sistema requiere trabajo de prefill, igual que el resto del prompt. 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 caché KV, y la fase de decodificación las lee junto con todo lo demás. Por tanto, 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 KV adicionales se vuelven a leer en cada paso de decodificación, por lo que un prompt muy largo también ralentiza un poco la decodificación. La sección siguiente trata este punto.

La solución es evitar recalcular el mismo prefijo. Un servidor con almacenamiento en caché de prefijos conserva la caché KV 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; compruebe vllm serve --help en su versión, porque el valor predeterminado ha cambiado entre versiones. Esta caché KV en la GPU es distinta de la caché de prompts por la que un proveedor de API le factura, y conviene leer la diferencia entre una caché KV 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 motivos, ambos relacionados con la caché KV.

El primero 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 cada cabeza (hidden_size dividido por num_attention_heads) y por los bytes por elemento. El 2 inicial cuenta una clave y un valor.

En una configuración habitual de 8 billion parámetros, con 32 capas, 8 cabezas de clave y valor mediante GQA (grouped query attention), dimensión de cabeza 128 y 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. Por tanto, una conversación de 8,000 tokens utiliza aproximadamente 1 GB de caché KV por solicitud.

El segundo es la capacidad. Ese 1 GB es memoria que no puede utilizarse para los pesos ni para el contexto de otro usuario. El servidor dimensiona su grupo de KV una sola vez durante el arranque, en vLLM mediante --gpu-memory-utilization, y las solicitudes nuevas esperan cuando el grupo está lleno. El aumento de vllm:num_requests_waiting mientras vllm:kv_cache_usage_perc permanece 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 adelante en lugar de ponerla en cola. El usuario lo percibe como una pausa en mitad del flujo.

Un contexto largo tiene dos costes: más trabajo de prefill al principio y más memoria que leer por token durante el resto de la respuesta.

¿Por qué el procesamiento por lotes mejora el rendimiento y empeora la latencia de cola?

Como la decodificación está limitada 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 lote, por lo que el rendimiento total aumenta casi de forma lineal con el tamaño del lote, hasta que se agota el grupo de KV o el lote crece lo suficiente como para volver a estar limitado por la capacidad de cálculo. El procesamiento continuo por lotes reconstruye el lote en cada paso. Así, una solicitud terminada sale y otra en cola entra sin esperar a las solicitudes vecinas.

El coste se refleja en los percentiles. El siguiente token de cada usuario debe esperar ahora a la parte más lenta de un paso compartido. Por eso p50, la mediana, sigue siendo aceptable, mientras p99, el 1 % de solicitudes más lentas, aumenta. Los usuarios perciben p99 porque corresponde a la pausa en mitad de una frase.

El prellenado lo acentúa. Cuando llega un prompt grande durante la generación, ocupa el dispositivo durante un paso largo y todos los flujos activos sufren una interrupción. El prellenado fragmentado reduce este efecto al dividir el prompt largo en partes e intercalar cada una en los lotes de decodificación. En agosto de 2026, el motor vLLM V1 lo hace de forma predeterminada y expone este equilibrio mediante --max-num-batched-tokens. La documentación de ajuste de vLLM indica claramente el compromiso: los valores más pequeños, alrededor de 2048, ofrecen una mejor latencia entre tokens (ITL), porque menos operaciones de prellenado interrumpen la decodificación; los valores más grandes ofrecen un mejor TTFT, porque caben más tokens de prellenado en un solo lote. Ese indicador representa el equilibrio entre prellenado y decodificación mediante un número que se puede ajustar. El punto en el que p99 deja de ser aceptable depende de la capacidad, y cuántos usuarios simultáneos puede atender un LLM autoalojado permite determinarlo 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 no es bueno

Cuando el TTFT es demasiado alto:

  • Acorte el prompt. El coste del prefill depende de los tokens del prompt, y el system prompt se procesa en cada solicitud.
  • Active el almacenamiento en caché del prefijo para que un prefijo repetido se procese una sola vez en lugar de hacerlo en cada solicitud.
  • Aumente --max-num-batched-tokens para procesar más tokens de prefill en cada paso.
  • Compruebe la cola antes de atribuir el problema al modelo. Un valor de vllm:num_requests_waiting superior a cero significa que la solicitud todavía no había empezado, por lo que el problema es de capacidad.

Cuando los tokens por segundo son demasiado bajos:

  • Cuantice los pesos. Menos bytes por peso implican menos bytes leídos por token.
  • Compare el ancho de banda de memoria publicado para su tarjeta con el gráfico anterior y compruebe cuánto se aproxima al límite.
  • Reduzca --max-num-batched-tokens para que los prefills interrumpan el decode con menos frecuencia.
  • 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 es importante, porque Ollama y vLLM planifican el prefill y el decode de forma diferente, y un ajuste que ayuda a uno puede no tener ningún efecto 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 siguientes se transmiten rápido?

La espera corresponde al prellenado y la transmisión 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 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?

Sí, ligeramente, y por un motivo diferente del TTFT. Cada paso de decodificación lee 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 unos 128 KiB por token, de modo que un contexto de 8,000 tokens supone aproximadamente 1 GB que se consulta en cada paso. El efecto principal de un prompt largo sigue produciéndose en el TTFT, no en la velocidad de transmisión.

¿Qué especificación de la GPU permite predecir la velocidad de decodificación?

El ancho de banda de memoria. Divida el ancho de banda publicado entre el tamaño de los pesos en memoria para obtener el límite teórico de una única secuencia. Una tarjeta con más capacidad de cómputo, pero con el mismo ancho de banda, no transmitirá más rápido. Por eso cuantizar a 8 bits duplica aproximadamente la velocidad de decodificación: reduce a la mitad los bytes leídos por token sin modificar el cómputo.

¿Por qué aumenta el rendimiento cuando añado usuarios, pero cada usuario percibe más lentitud?

Una única 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, por lo que la latencia por usuario aumenta al mismo tiempo. Supervise la latencia p99 entre tokens, no el valor de rendimiento agregado, y compruebe vllm:num_requests_waiting para determinar si las peticiones están en cola en lugar de ejecutándose.