Saltar al contenido
Angular•3 min de lectura

KV cache que no cabe: offloading en vLLM, del archivo al objeto

Tus agentes multi-turn reenvían el historial completo en cada turno, y prefix caching los salva mientras las sesiones caben en la GPU. El problema llega cuando el número de conversaciones crece: la HBM se llena, vLLM expulsa KV cache vieja, y el próximo turno de esa sesión paga un recompute completo del prefill. Offloading es la respuesta: guardar esa KV expulsada en memoria de CPU, disco u otros nodos, y recargarla en vez de recomputarla. El framework de tiered offloading existe desde vLLM v0.22 y salió con guía oficial este septiembre. Fuente: blog de vLLM, 10 de septiembre de 2026.

La escalera de memoria

El diseño es host-céntrico: toda la KV pasa por la RAM del host. Al expulsar de la GPU, la copia a CPU es un DMA rápido y libera la HBM de inmediato; desde la RAM, los chunks bajan en cascada a los tiers secundarios. Al recargar, se consulta primero la RAM (que es un cache LRU de verdad, no un staging buffer) y después los tiers en orden: filesystem, object storage o peers remotos por RDMA.

vllm serve Qwen/Qwen3.6-35B-A3B \
    --kv-transfer-config '{
        "kv_connector_extra_config": {
            "spec_name": "TieringOffloadingSpec",
            "cpu_bytes_to_use": 107374182400,
            "secondary_tiers": [{"type": "fs", "root_dir": "/mnt/kv-cache"}]
        }
    }'

Ese comando reserva 100 GB de RAM para el tier primario y monta un tier de filesystem en NVMe. El tier de object storage existe para S3/MinIO cuando quieres cache compartido barato, y el tier P2P comparte KV entre instancias vía RDMA — de eso se alimenta el prefill/decode disaggregation del próximo capítulo.

El detalle que convierte esto en infraestructura y no en un truco: los chunks se nombran por contenido (hash de la secuencia de tokens). Dos instancias que montan el mismo storage comparten cache sin configuración extra, y cualquier request cuyo prefijo coincida — el mismo system prompt, el mismo historial inicial — es un hit.

Los números que justifican el flag

El benchmark oficial del framework: Qwen3.6-35B-A3B en 2×H100, conversaciones multi-turn de 12K tokens iniciales más 4K por turno, 8 turnos, 64 de concurrencia. La escala del problema por número de conversaciones simultáneas:

  • Hasta ~64: la HBM aguanta el working set. Todos los métodos van bien.
  • 64 a 128: la HBM se llena y el throughput cae en picada sin offloading. Con offload a CPU se mantiene.
  • Más de 128: la RAM también se llena. El tier de storage sostiene un hit rate alto y más que duplica el throughput contra las alternativas.

La matemática es simple: un hit en disco tarda milisegundos; un recompute de 12K tokens de prefill tarda segundos. Storage gana aunque su latencia sea la peor del sistema. Scripts de reproducción: fs-offload-experiments.

Mídelo como el resto

El framework expone sus métricas en el mismo /metrics de siempre: utilización del tier primario, throughput de transferencias, latencias por tier y, la que de verdad importa, hit rate por tier. Si tu hit rate de CPU ronda el cero, tu workload no reutiliza prefijos y el offloading solo te está ocupando RAM. Si el de storage sube cuando las conversaciones pasan de 128, la escalera está haciendo su trabajo.

Para orquestadores, el framework emite eventos de KV: qué chunks se guardaron o expulsaron, desde qué tier. Routers cache-aware como llm-d o Dynamo los consumen para mandar cada request a la instancia que más probablemente tenga su cache — scheduling consciente del cache en vez de round-robin a ciegas.

Checkpoint y el error típico

Checkpoint: tu server corre con TieringOffloadingSpec, tienes un volumen NVMe para el tier de filesystem y vigilas los hit rates por tier en Prometheus. La prueba: duplica la carga de conversaciones y mira si el throughput cae en picada (sin offloading) o se degrada suave (con él).

El error típico: dimensionar cpu_bytes_to_use a ojo. Ese número compite con el resto del sistema por RAM: si le das toda la memoria del host, el tier de filesystem empieza a swapear y las latencias de reload se comen la ganancia. Empieza en 25% de la RAM y sube mirando la métrica de utilización del tier primario.

Siguiente capítulo

Una GPU, dos, cuatro... y la pregunta de producción real: ¿prefill y decode deberían vivir en máquinas separadas? Cierre de la guía con multi-GPU y disaggregation.

> Más posts