volver al blog

El KV cache: por qué el primer token tarda y los demás vuelan

llmkv-cachelatencia

La pregunta que casi nadie se hace

Lanzas una consulta con un prompt de 40.000 tokens y el modelo se queda pensando casi un segundo antes de escribir nada. Luego, de golpe, empieza a soltar palabras a toda velocidad, sin despeinarse. ¿Por qué esa pausa inicial? ¿Y por qué, una vez arranca, cada token siguiente es tan barato?

La respuesta es una sola pieza de ingeniería: el KV cache. Entenderla cambia la forma en que diseñas prompts, eliges modelos y presupuestas latencia. No es un detalle académico; es la razón de la mitad de las decisiones de tu capa de inferencia.

Generar texto son dos fases, no una

Un LLM no escribe la respuesta de un tirón. La produce token a token, y para cada token nuevo tiene que “mirar” todo lo anterior mediante el mecanismo de atención. Ese proceso se parte en dos fases con costes muy distintos.

La primera es el prefill: el modelo lee tu prompt entero de una vez y calcula la representación interna de cada token. Aquí es donde se va la pausa inicial. Si el prompt tiene 40.000 tokens, el modelo procesa 40.000 posiciones antes de producir la primera palabra. Esa espera tiene nombre: time to first token (TTFT).

La segunda es el decode: una vez arrancado, el modelo genera un token, lo añade al final y repite. Cada paso produce exactamente una palabra. Y aquí está la sorpresa: cada uno de esos pasos es rapidísimo comparado con el prefill.

La pregunta obvia es por qué. Si para el token 41.000 hay que volver a mirar los 40.999 anteriores, ¿no debería ser cada vez más lento?

El truco: guardar en vez de recalcular

Lo sería, si el modelo recalculara todo cada vez. No lo hace. Ahí entra el cache.

En la atención, cada token se convierte en tres vectores: una query, una key y un value (de ahí la K y la V). Para generar la palabra siguiente, la query del token actual se compara con las keys de todos los tokens previos, y esas comparaciones pesan los values. La clave está en que las keys y los values de los tokens ya procesados no cambian nunca. El token 500 tiene la misma K y la misma V lo generes cuando lo generes.

Así que el modelo los calcula una vez y los guarda. Eso es el KV cache: una tabla en memoria de GPU con las keys y values de todo lo visto hasta ahora. En cada paso de decode solo calcula la K y la V del token nuevo, las añade a la tabla y reutiliza el resto.

# Pseudocódigo del bucle de decode. Lo esencial es lo que NO se recalcula.
cache_k, cache_v = prefill(prompt)   # se paga una vez: procesa todo el prompt
token = primer_token

while token != FIN:
    # solo se computa la K y la V del último token, no de los 40.000 previos
    k, v = proyectar(token)
    cache_k.append(k)                # la tabla crece de uno en uno
    cache_v.append(v)

    # la atención reutiliza todo lo cacheado; no recalcula nada anterior
    logits = atender(query(token), cache_k, cache_v)
    token = muestrear(logits)
    yield token

Sin cache, generar N tokens sería un coste cuadrático: cada palabra reprocesa toda la secuencia. Con cache, el decode es lineal. Por eso el primer token tarda y los demás vuelan: el trabajo duro ya está hecho y guardado.

Lo que el cache te cobra: memoria

No hay comida gratis. El KV cache ahorra cómputo a cambio de ocupar memoria de GPU, y esa memoria crece con cada token de contexto. Un contexto largo no solo tarda más en el prefill: además reserva una tabla enorme mientras dura la generación.

Ese es el verdadero techo de muchos despliegues. No es que el modelo no “quepa”: es que el KV cache de contextos largos, multiplicado por las peticiones concurrentes, se come la VRAM. Cuando lees que un motor de inferencia sirve “hasta X peticiones en paralelo”, casi siempre el límite lo pone el cache, no los pesos del modelo.

Por qué esto explica tu factura y tu latencia

Junta las dos fases y de repente muchas cosas encajan.

FaseQué procesaCoste dominanteQué lo empeora
PrefillTodo el prompt de una vezLatencia inicial (TTFT)Prompts largos
DecodeUn token por pasoTiempo total de salidaRespuestas largas

Un prompt gigante penaliza el arranque; una respuesta larga penaliza el total. Son palancas distintas y se optimizan distinto. Si tu problema es que la respuesta tarda en empezar, recorta el prompt o usa streaming para tapar la espera. Si el problema es que tarda en terminar, limita los tokens de salida o elige un modelo más rápido en decode.

Y aquí conecta con algo que quizá ya uses: el prompt caching que ofrecen APIs como las de Anthropic u OpenAI es, en el fondo, reutilizar el KV cache del prefill entre peticiones. Si mil llamadas comparten el mismo prefijo de sistema, no tiene sentido pagar su prefill mil veces. Se cachea una vez y las siguientes empiezan más rápido y más baratas. Es la misma idea, elevada del token a la petición.

Cómo jugarlo a tu favor

Cuatro decisiones prácticas que salen directas de entender el cache:

  • Pon lo estable al principio del prompt. Instrucciones de sistema y contexto fijo primero; lo variable, al final. Así el prefijo se puede cachear entre llamadas y solo repagas la cola que cambia.
  • Vigila el contexto, no por el modelo sino por la memoria. Meter 100.000 tokens “porque caben” dispara el TTFT y la VRAM. Recupera solo lo relevante (un buen RAG hace justo esto) en lugar de volcarlo todo.
  • Mide TTFT y tokens por segundo por separado. Un solo número de “latencia” esconde las dos fases. Si no las separas, optimizas a ciegas.
  • Aprovecha el prompt caching de tu proveedor siempre que repitas prefijos largos. Es el ahorro más fácil que existe y mucha gente lo deja sobre la mesa.

En resumen

El primer token tarda porque el modelo procesa todo el prompt de golpe; los siguientes vuelan porque guarda las keys y values de lo ya visto y no los vuelve a calcular. Ese intercambio —cómputo por memoria— es el KV cache, y explica desde el TTFT hasta cuántas peticiones concurrentes aguanta tu GPU. No es magia ni un parámetro oculto: es contabilidad de recursos. Y en cuanto la ves, la latencia de tu LLM deja de ser un misterio y pasa a ser algo que puedes diseñar.