Capítulo 8 — Gestión de caché KV de nueva generación
Octava entrega del recorrido capítulo por capítulo de LLM Primer VI: Scaling AI Systems. El capítulo que trae la intuición de paginación del sistema operativo dentro del motor de inferencia — y convierte la caché KV, de losa de bytes reservados, en un recurso compartido, evictable y prefix-cacheable.
Por qué existe este capítulo
El Capítulo 7 dejó al batching continuo con una deuda pendiente. Las secuencias entran y salen en cada iteración; una disposición ingenua da a cada slot su propia losa de tamaño máximo, la mayor parte desperdiciada; la ganancia de throughput del batching se devuelve en parte. La deuda es fragmentación interna, exactamente el modo de fallo que los sistemas operativos resolvieron con paginación en los años 1960. El Capítulo 8 trata de aplicar esa solución al serving de LLMs: parte la caché KV en pequeños bloques físicos, desacóplalos de las posiciones lógicas de token con una tabla de páginas, y deja que políticas de eviction y de caché reclamen o compartan bloques entre secuencias. PagedAttention es el movimiento fundacional; H2O e InfiniGen son las políticas de eviction; el caché de prefijos es la técnica que permite a un cluster de producción servir millones de peticiones agénticas sobre una docena de GPUs.
8.1 PagedAttention es memoria virtual para la caché KV
El artículo de PagedAttention de vLLM (2023) sube la intuición del SO directo al motor. La caché KV se parte en bloques de tamaño fijo — típicamente 16 tokens cada uno — mantenidos en un pool físico plano. Una secuencia se representa por una tabla de bloques: un array de punteros que mapea posiciones lógicas a IDs de bloques físicos. El kernel de atención toma la tabla de bloques como entrada extra y recoge las claves y los valores por indirección en vez de por rebanado contiguo; en Hopper la L2 absorbe suficientemente bien el patrón de acceso aleatorio para que el kernel corra dentro de un pequeño porcentaje del que usa losas. Las ganancias son grandes. La fragmentación interna cae del 60–80 por ciento a en torno al 4 por ciento (un bloque de cola parcial por secuencia), lo que sube la concurrencia disponible entre 2 y 4 veces. La compartición de bloques con conteo de referencias hace casi gratuito el muestreo complejo — best-of-8 sobre un prompt de 2.000 tokens cae de 16.800 a 2.800 bloques de token — y es el sustrato sobre el que se construye el caché de prefijos.
8.2 H2O e InfiniGen evictan los tokens que no importan
PagedAttention resuelve la fragmentación pero no resuelve contextos que superan la VRAM en cualquier disposición. Un contexto de 200.000 tokens en Llama-3-70B necesita 60 GB de KV además de los pesos. H2O ("Heavy Hitter Oracle") observa que los pesos de atención durante el decoding se concentran en un pequeño conjunto de posiciones fuente — tokens recientes, tokens de sumidero de atención al principio, y un conjunto sparse de aciertos relevantes al contenido — mientras que la mayoría de posiciones históricas reciben peso esencialmente cero. El motor rastrea una puntuación de atención acumulada por posición; cuando el presupuesto KV de la secuencia se acerca al tope, evicta las posiciones de puntuación más baja excepto una ventana garantizada de tokens recientes y de sumidero. El ahorro es grande; el coste es la permanencia — si una consulta posterior necesita una posición que se evictó, el motor no puede recuperarla. InfiniGen refina el intercambio con una selección dinámica y recuperable: en lugar de tirar tokens por completo, hace offload de su KV a memoria de CPU y los pagina de vuelta si la atención empieza a concentrarse otra vez sobre ellos. La política de eviction correcta depende de cuánto una carga re-consulta su propia historia larga; las cargas agénticas castigan la eviction permanente y recompensan la recuperación al estilo InfiniGen.
8.3 El caché de prefijos es la palanca de mayor impacto que PagedAttention desbloquea
En tráfico real, los primeros miles de tokens de la mayoría de prompts son idénticos entre peticiones. Un servicio de chat reutiliza el mismo system prompt para cada conversación. Un servicio de RAG pega los mismos pasajes recuperados en el prompt para cada usuario que buscó por ellos. Un agente inyecta las mismas descripciones de herramientas y andamios de razonamiento en cada paso. PagedAttention hace mecánica la compartición: hashea el prompt en trozos de tamaño de bloque; si el hash está en el caché global de bloques ya rellenados con cómputo, apunta la tabla de bloques de la nueva petición al bloque cacheado y sáltate el prefill de ese prefijo por completo; si no, corre el prefill y el bloque resultante se registra. Las tasas de acierto en producción son dramáticas — por encima del 99 por ciento para el system prompt de un servicio de chat, 30–60 por ciento para prefijos de RAG dependientes de recuperación, cerca de 1,0 para un andamio agéntico. RadixAttention de SGLang lleva la idea más lejos con un árbol radix que llavea prefijos compartidos de cualquier longitud, no solo alineados a bloque. El caché de prefijos es la técnica que con mayor frecuencia convierte un cluster de serving fuera de presupuesto en uno dentro de presupuesto.
Lo que prepara el Capítulo 8
Paginación, eviction y caché de prefijos han reducido la huella KV por token y han hecho tratable el comportamiento de memoria del motor a alta concurrencia. Lo que ninguno de ellos aborda es la dependencia secuencial fundamental del decoding: un token de salida por iteración por secuencia, sin importar cuántos slots estén activos. El Capítulo 9 asume esa restricción con la decodificación especulativa — la familia de técnicas que predice varios tokens por delante con un draft barato y verifica la suposición en un único forward pass caro, rompiendo el suelo de un-token-por-paso para las secuencias que más importan a la latencia percibida por el usuario.
Próximamente — Capítulo 9: Decodificación especulativa. Draft, verify, y la aritmética de cuándo compensa la especulación.