PagedAttention por dentro: bloques, tabla de páginas, evicción y el estado del arte del KV cache en 2026

TL;DR

PagedAttention (Kwon et al., SOSP 2023) fue la idea que convirtió la gestión del KV cache de un problema de malloc clásico —reservar contiguo, malgastar el 60-80%— en un problema resuelto como lo resuelven los sistemas operativos desde hace medio siglo: bloques pequeños de tamaño fijo, una tabla de páginas por proceso, asignación bajo demanda. El paper midió un desperdicio menor al 4% y 2-4× más throughput agregado en el mismo hardware. Tres años después, PagedAttention sigue siendo el modelo mental dominante, pero su implementación literal ya no es la de ningún sistema de inferencia serio: la propia documentación de vLLM califica al paper original de “documento histórico”. Han llegado vAttention (paginar usando la MMU de CUDA, no la indirección software), EvicPress (combinar compresión y evicción), KVTC (transform coding del cache), LaProx (evicción como aproximación matricial), disaggregated serving (prefill y decode en GPUs distintas, en producción en NVIDIA Dynamo, llm-d, Mooncake y media docena más), RadixAttention de SGLang (trie de prefijos compartidos, con hit rates del 85% en cargas de agentes) y la nueva generación de speculative decoding (EAGLE-3, DeepSeek MTP, Mirror Speculative). Este artículo desmonta PagedAttention al nivel del bloque, explica qué hace vLLM hoy en su lugar, y traza el mapa del estado del arte para que no te pierdas eligiendo entre quince siglas en la primera reunión.

Este artículo cierra una mini-serie. El primero —KV cache: la memoria de trabajo que sostiene la inferencia LLM— explicó por qué cada token consume VRAM. El segundo —vLLM en Kubernetes: la pieza de inferencia LLM que sí escala— mostró cómo se sirve eso en producción. Éste baja al fondo: cómo se gestiona el cache dentro del motor, y qué hay después de PagedAttention.

La analogía: pasar de malloc() al kernel multiproceso

Un programa C ingenuo pide memoria con malloc(N) y recibe un bloque contiguo de N bytes. Si pide muchos bloques de tamaños distintos y los libera en cualquier orden, el heap se llena de huecos: hay tres megabytes libres en total, pero ningún hueco contiguo de un megabyte, y el siguiente malloc(1MB) falla. Fragmentación externa. Si reserva siempre el peor caso “para estar seguro” —malloc(MAX_POSSIBLE_SIZE)— el heap se queda lleno con bloques medio vacíos. Fragmentación interna.

Los sistemas operativos modernos no permiten que eso pase con la memoria virtual de un proceso. La memoria virtual se divide en páginas (4 KB típicamente), cada una asignada a un marco físico en RAM mediante una tabla de páginas específica del proceso. El proceso ve un espacio contiguo enorme; el SO lo respalda con marcos físicos dispersos, asignados bajo demanda y liberados cuando dejan de usarse. El concepto tiene 50 años y funciona.

Antes de PagedAttention, los motores de inferencia LLM eran programas C ingenuos. Cada sesión reservaba un bloque contiguo de KV cache dimensionado al peor caso max_context_len × bytes_per_token × n_layers × 2. Una conversación que usa 273 tokens reservaba sitio para 32 768. Cuando el motor servía 50 sesiones simultáneas, el 60-80% de la VRAM dedicada a KV cache estaba reservada y vacía. El paper de PagedAttention midió este desperdicio en cargas reales y propuso lo evidente: tratar el KV cache como memoria virtual. Bloques físicos pequeños (16 tokens), tabla de páginas por sesión, asignación bajo demanda. El resultado: < 4% de desperdicio, 2-4× más throughput agregado en el mismo hardware.

La idea no era nueva fuera del mundo LLM, era nueva dentro. Y eso vale como contribución: a veces traer una técnica madura de otro campo es más impactante que inventar algo desde cero.

El paper original, en cristiano

Kwon et al. publicaron Efficient Memory Management for Large Language Model Serving with PagedAttention en SOSP 2023 e implementaron simultáneamente vLLM, que en seis meses pasó de proyecto académico a “el motor de inferencia que todo el mundo usa”. Las tres aportaciones del paper, en orden de importancia:

  1. Cuantificación del problema: medir el desperdicio en sistemas existentes y mostrar que el 60-80% de la VRAM se estaba quemando en peor-caso reservations que no se usaban.
  2. El algoritmo de paging: cómo dividir el KV cache, qué tamaño de bloque elegir, cómo gestionar la tabla de páginas en GPU.
  3. El kernel CUDA: cómo implementar la operación de atención cuando los tokens de una secuencia están dispersos por la VRAM, sin destruir el rendimiento.

El modelo de bloques

El KV cache se divide en bloques de tamaño fijo. La elección por defecto en vLLM es 16 tokens por bloque, decisión que el paper justifica con un barrido empírico: bloques más pequeños reducen la fragmentación interna pero aumentan el overhead de metadata y de indirección; bloques más grandes mejoran throughput pero pierden eficiencia. 16 es el punto razonable para los modelos y cargas medidas.

Cada bloque almacena los K y V de N tokens consecutivos de una sola sesión en una sola capa del modelo. Para un Llama 3 8B con 32 capas, una sesión de 128 tokens necesita aproximadamente 128 / 16 × 32 = 256 bloques (uno por capa por grupo de 16 tokens). Los bloques son lógicamente independientes entre sí: pueden vivir en cualquier dirección física de VRAM.

La tabla de páginas (block table)

Cada sesión tiene asociada una block table: una lista ordenada de identificadores de bloques físicos. Cuando vLLM calcula la atención para el token 200 de la sesión X, mira la block table de X, encuentra que el bloque que contiene el token 200 está en la posición 200 / 16 = 12 de la lista, lee qué bloque físico corresponde y va a buscarlo.

La block table vive en VRAM, no en RAM como la tabla de páginas del SO. Si viviese en CPU, cada paso de decode tendría que hacer una indirección PCIe, lo que mataría el throughput. Está en VRAM, junto al cache, y el kernel CUDA la lee como una estructura más durante el cómputo.

Block table (sesión X)VRAM (pool de bloques físicos)posición 0 → bloque #7posición 1 → bloque #2posición 2 → bloque #11posición 3 → bloque #5posición 4 → bloque #9#0 libre#1 libre#2 sesión X#3 sesión Y#4 sesión Z#5 sesión X#6 sesión Y#7 sesión X#8 libre#9 sesión X#10 sesión Z#11 sesión Xlos bloques de una misma sesión están dispersos; la block table reconstruye su orden lógicocuando un bloque queda libre (sesión termina), vuelve al pool y otra sesión lo ocupa en el siguiente paso

Cuando una sesión genera su token N-ésimo, vLLM mira si el último bloque de la block table aún tiene huecos (N mod 16 != 0). Si los tiene, escribe ahí. Si no, pide un bloque nuevo del pool global, lo añade al final de la block table y escribe en su primera posición. Crecer la sesión cuesta una asignación O(1) en el pool global más una append O(1) a la block table. Liberar una sesión devuelve sus bloques al pool: también O(N_bloques) y rapidísimo.

El pool de bloques

El pool global se dimensiona al arrancar el motor. Lo típico:

bloques_disponibles = (VRAM_total - modelo - activations - overhead) / block_size_bytes

Para una RTX 4090 (24 GB) sirviendo Llama 3 8B BF16 con cache también en BF16:

modelo:           ~16 GB
activations:       ~1.5 GB
overhead vLLM:     ~1 GB
disponible para KV cache: ~5.5 GB

block_size = 16 tokens × 32 capas × 2 (K,V) × 8 KV heads × 128 head_dim × 2 bytes = 2 MB
bloques disponibles ≈ 5.5 GB / 2 MB ≈ 2800 bloques

tokens cacheables totales (todas sesiones) ≈ 2800 × 16 = 44800 ≈ 44 K tokens

Si una sola sesión pide 32 K tokens, ocupa 2 000 bloques (de 2 800). Si las sesiones son más cortas, caben más simultáneas. El pool es un recurso compartido global, no per-sesión, y ahí está la clave del aprovechamiento.

Copy-on-write para sampling paralelo

Una sutileza elegante del paper: cuando una petición usa sampling paralelo o beam search, las N secuencias comparten el prefijo (el prompt + lo que se haya generado hasta el punto de divergencia). En lugar de duplicar el KV cache de ese prefijo, vLLM hace que las N secuencias compartan los bloques físicos vía la block table. Solo cuando una secuencia diverge —genera un token distinto que las otras— vLLM copia el último bloque afectado (no toda la secuencia) y la rama esa pasa a tener su propia versión.

Esto es exactamente lo que hace el kernel de Linux con fork(): copy-on-write de las páginas. La memoria solo se duplica cuando se modifica. En beam search con N=4 y prefijos largos, el ahorro es enorme.

El kernel CUDA

El reto técnico no obvio: el cómputo de atención debe seguir la indirección de la block table para cada token. En la versión naïve (cache contiguo), el kernel asume que los tokens 0..N-1 de la sesión X están en direcciones contiguas y los lee de un tirón. Con paging, los tokens 0..15 están en el bloque #7, los 16..31 en el #2, los 32..47 en el #11, etc.

El kernel paged_attention de vLLM resuelve esto con block-aware tiling: divide el cómputo de atención en chunks alineados con el tamaño de bloque (16 tokens), y para cada chunk localiza el bloque físico vía la block table y lo procesa. Es más complejo que el kernel contiguo, pero el coste medido es solo 5-10% de latencia adicional frente a la operación contigua equivalente, contra una ganancia de 2-4× en throughput agregado por la mejor utilización de VRAM. Compromiso aplastante.

Evicción y preemption: qué hace cuando el pool se agota

El KV cache crece. Cada token nuevo en cualquier sesión consume bloques. En un servidor con tráfico alto, el pool global se vacía. ¿Qué hacer cuando llega una nueva petición y no hay bloques libres?

Tres opciones: rechazar la petición (mala UX), bloquear hasta que algo se libere (mala latencia), o expulsar alguna sesión existente para hacer sitio (preemption). vLLM elige la tercera, con dos estrategias seleccionables:

Estrategia 1: recompute

Cuando vLLM expulsa una sesión, libera todos sus bloques y la pone en cola de espera. Cuando vuelve a haber sitio (otras sesiones terminan), vLLM rehace el prefill entero de la sesión expulsada desde el prompt original. El KV cache se reconstruye desde cero.

Ventaja: liberación instantánea, no consume bandwidth de PCIe. Coste: la sesión rehace todo el cómputo del prefill, segundos o decenas de segundos para prompts largos.

Estrategia 2: swap

vLLM mueve los bloques de la sesión expulsada a RAM de CPU (vía PCIe), liberando la VRAM. Cuando la sesión vuelva a tocar, vLLM la trae de vuelta a VRAM.

Ventaja: conserva el cache, no rehace cómputo. Coste: tiempo de transferencia PCIe (~32 GB/s en PCIe gen4 x16). Mover 4 GB de KV cache cuesta ~125 ms ida y vuelta.

vLLM elige entre las dos en función del tamaño del cache de la sesión y de la latencia esperada. Para sesiones cortas, recompute suele ganar; para sesiones largas con prompts grandes, swap. Es configurable con --swap-space.

El problema de la preemption agresiva

Hay un fallo de modo: si el sistema está saturado y vLLM no para de expulsar y reincorporar las mismas sesiones, todas hacen poco progreso y el throughput se hunde. Este es thrashing, exactamente el mismo problema que tiene un SO cuando la presión de paginación es muy alta.

La solución operativa es la misma que en SO: admission control. Configurar --max-num-seqs para limitar cuántas sesiones puede atender vLLM simultáneamente. Si llegan más, esperan en la cola HTTP. Mejor tener 10 sesiones avanzando rápido que 100 thrasheando.

Lo que vLLM hace hoy: más allá del paper original

La documentación oficial de vLLM señala que el paper de PagedAttention es ya un documento histórico que ya no describe la implementación actual. ¿Qué ha cambiado?

Chunked prefill integrado con paged KV

El kernel original asumía que el prefill ocupaba el batch entero un paso, y el decode ocupaba batches separados. El motor actual mezcla prefill (troceado en chunks) con decode en el mismo paso, usando el mismo paged KV cache para ambos. Esto mejora la utilización de tensor cores cuando hay pocas peticiones en prefill y muchas en decode.

Prefix caching cross-session

El paper original ya tenía copy-on-write para sampling paralelo en una sola petición. La extensión natural fue compartir bloques de prefijo entre peticiones distintas que llegan con el mismo system prompt. En vLLM se activa con --enable-prefix-caching. Es una versión más simple que la de SGLang (no usa radix tree explícito, hace hash de bloques) pero efectiva: 30-70% mejora de TTFT en cargas con prompts compartidos.

Sliding window attention

Modelos como Mistral 7B usan atención con ventana deslizante: solo atienden a los últimos K tokens (4 096 en Mistral). El motor mantiene únicamente los bloques de la ventana activa, liberando los más viejos. Esto cambia la economía: para esos modelos, el cache no crece sin límite.

FlashAttention-3 paged

Las versiones recientes de FlashAttention (especialmente FA-3) tienen kernels paged-aware optimizados para Hopper (H100). vLLM los usa por defecto en H100 cuando están disponibles, con ganancias adicionales del 15-30% sobre el kernel paged original.

vAttention: paging sin reescribir el kernel

El paper de vAttention (Prabhu et al., arxiv 2405.04437) hace una observación incómoda: el coste de PagedAttention no es solo el 5-10% del kernel. Hay dos costes ocultos:

  1. Inadaptable a kernels nuevos: cada vez que sale una optimización de atención (FlashAttention-2, FlashAttention-3, kernel custom), hay que reescribir su versión paged. Eso ha hecho que vLLM frecuentemente esté 1-2 versiones por detrás del frente de FlashAttention.
  2. Block tables en VRAM: pequeño pero constante. Para muchas sesiones, las block tables ocupan VRAM y cuestan accesos.

La propuesta de vAttention: usar CUDA Virtual Memory Management (VMM), las primitivas de virtual memory que NVIDIA expone desde CUDA 11.2. Con VMM puedes reservar un rango virtual contiguo enorme y asignar memoria física bajo demanda en porciones, mapeándolas en posiciones del rango virtual. El kernel de atención ve un rango contiguo (no necesita ser paged-aware); el runtime mete el paging dentro de la API de CUDA.

Resultado medido en el paper: hasta 1.99× decode throughput sobre vLLM con FlashAttention-2 original. Y el kernel de atención es el de FlashAttention estándar, sin modificar.

La idea es disruptiva porque sugiere que la abstracción del paper de PagedAttention era inadecuada: el problema nunca fue que el cache tenía que ser físicamente paginado, sino que la asignación tenía que ser dinámica. La forma de resolverlo es delegar el paging al hardware (MMU + VMM de CUDA), no implementarlo en software.

vAttention no ha desplazado a PagedAttention en vLLM por inercia y por consideraciones de portabilidad (VMM no está disponible en GPUs AMD ni Intel; PagedAttention sí). Pero los runtimes nuevos —y algunos forks de vLLM— ya lo están adoptando. Es plausible que en 2027 sea el default.

Compresión y evicción inteligente: lo que ha llegado en 2025-2026

PagedAttention y vAttention atacan dónde vive el cache. Otra línea de trabajo ataca qué vive en el cache: si no necesitas todo el KV de un contexto largo, ¿por qué guardarlo todo?

StreamingLLM (Xiao et al., 2024): los attention sinks

El precursor conceptual. Observación: los primeros 4 tokens de cualquier contexto reciben atención desproporcionada de los tokens posteriores, incluso cuando semánticamente no son relevantes (son “sinks” para que el softmax se normalice). Si descartas todo el cache excepto los primeros 4 tokens más una ventana deslizante de los últimos K, el modelo sigue generando con calidad razonable indefinidamente.

Impacto: permite contexto efectivamente infinito con cache acotado. Coste: olvido real del contenido medio.

H2O, SnapKV (2024): eviction por attention score

Variantes que mantienen un score acumulado de atención por token y, cuando el cache se llena, descartan los tokens con menor score. Son métodos por sesión, no por sistema: cada sesión decide qué partes de su propio cache descartar.

EvicPress (Microsoft Research, 2026)

El paper EvicPress: Joint KV-Cache Compression and Eviction for Efficient LLM Serving hace una observación elegante: hasta ahora, evicción y compresión se han tratado como técnicas separadas. Si vas a expulsar un bloque, ¿por qué no comprimirlo y guardarlo en RAM o NVMe en lugar de tirarlo? Y si lo tienes comprimido en un tier más lento, ¿cuándo merece la pena descomprimirlo y volver a HBM?

EvicPress modela el problema como optimización conjunta sobre múltiples tiers de almacenamiento (HBM, RAM, NVMe), aplica compresión lossy a los bloques candidatos a evicción y mantiene metadata para decidir cuándo trasladar de un tier a otro. Resultados: 2.19× faster TTFT a igual calidad de generación.

La idea importa porque cambia el framing: el KV cache deja de ser “está o no está” para pasar a ser “está, en qué tier, con qué fidelidad”. Es directamente análogo a la jerarquía de caches L1/L2/L3 en CPUs.

KV Cache Transform Coding (KVTC, 2026)

KV Cache Transform Coding for Compact Storage in LLM Inference (arxiv 2511.01815) aplica al KV cache una técnica clásica de compresión de imágenes y vídeo: transform coding, similar a DCT en JPEG/MPEG. Descompone los bloques de KV en una base de transformadas, descarta los coeficientes de menor energía y guarda el resto. Testeado con Llama 3, Mistral NeMo y R1-Qwen 2.5, supera a quantization (INT4) y a SVD como métodos de compresión del cache. Importante: el resultado es un cache comprimido reutilizable, no comprimido on-the-fly cada vez.

LaProx (2026)

LaProx: Reformulating KV Cache Eviction Problem for Long-Context LLM Inference (arxiv 2605.07234) reformula la evicción de KV cache. Hasta ahora la mayoría de métodos son head-wise y por promedios —miran scores por cabeza de atención y los promedian para decidir qué descartar—. LaProx la convierte en un problema output-aware y layer-wise: aproximar la multiplicación entre los attention maps y los projected value states como una matriz que se puede comprimir minimizando el error en la salida real del modelo, no en métricas auxiliares.

La consecuencia práctica: las decisiones de evicción mejoran porque están alineadas con lo que realmente afecta a la generación, no con un proxy.

Disaggregated serving: separar prefill de decode

PagedAttention y derivados optimizan un motor sirviendo peticiones mezcladas. La siguiente revolución conceptual fue darse cuenta de que prefill y decode no deberían correr en la misma GPU.

El problema de mezclarlos

Prefill es compute-bound: usa los tensor cores intensamente. Decode es memory-bound: mueve el KV cache a través del HBM. Si los mezclas en el mismo batch, una de las dos fases siempre va a ralentizar a la otra. Si entra una petición con prompt de 32 K tokens mientras hay 50 sesiones en decode, el prefill pausa a todas durante un segundo o más. Si llega una avalancha de prefills, los decodes en curso ven su latencia de token siguiente subir.

DistServe (Zhong et al., 2024)

DistServe (arxiv 2401.09670) propuso lo evidente: dedicar GPUs distintas a prefill y a decode. Las peticiones llegan a una GPU de prefill, que procesa el prompt y produce el KV cache inicial; ese KV cache se transfiere a una GPU de decode, que se encarga de generar los tokens uno a uno. Resultado: 7.4× más goodput, o el mismo throughput con SLO 12.6× más estrictos.

El truco no obvio es la transferencia del KV cache entre nodos. En GPUs con NVLink/NVSwitch del mismo nodo es trivial (~300 GB/s). Entre nodos con InfiniBand, el coste es manejable pero no despreciable. DistServe asume topologías que lo soporten.

Splitwise (Microsoft, 2024)

Splitwise llevó la idea un paso más allá: GPUs heterogéneas. Los prefills, compute-bound, corren en H100 o A100 (compute-optimizadas). Los decodes, memory-bound, corren en GPUs con más memoria por dólar pero menor compute (algunas variantes datacenter). Ganancia: 1.4× más throughput por dólar.

2026: producción

Disaggregated serving es ya producción mainstream:

  • NVIDIA Dynamo (sucesor de Triton): primitivo nativo.
  • vLLM: soporta disaggregation con flags --disaggregation-prefill-instances / --disaggregation-decode-instances.
  • SGLang, Ray Serve LLM, llm-d, LMCache, Mooncake: idem.
  • Operadores con stacks propios: Fireworks, Perplexity, Meta, Amazon, Modular, DeepInfra, Weka.

Disaggregated Inference: 18 Months Later (Hao AI Lab, 2026) hace una retrospectiva: lo que en 2024 era investigación es, en 2026, “como tener separados webservers de bases de datos”. Asumido.

PPD: no todos los prefills son iguales (2026)

El refinamiento más reciente: Not All Prefills Are Equal: PPD Disaggregation for Multi-turn LLM Serving (arxiv 2603.13358). Observación: en cargas multi-turn (asistentes conversacionales, agentes), los “prefills” sucesivos tienen estructura distinta: el primer turno es prompt nuevo, los siguientes son extensiones del cache anterior. PPD discrimina entre tipos de prefill y los enruta a clusters distintos, mejorando aún el aprovechamiento.

RadixAttention: el camino alternativo (SGLang)

Mientras vLLM iteraba sobre PagedAttention con prefix caching basado en hashing, SGLang tomó otra ruta: mantener un trie (radix tree) explícito de todos los prefijos que existen actualmente en el cache.

La idea

Cuando llega una petición nueva con tokens [t1, t2, t3, ..., tN], SGLang baja por el trie tokens-a-tokens. Si los primeros K tokens del prompt coinciden con un camino del trie, esos K tokens ya tienen su KV cache calculado y se reutilizan. Solo se procesa el prefill de los tokens N-K restantes.

Esto es prefix caching, pero con una estructura de datos que captura todas las relaciones de prefijo entre todas las sesiones activas simultáneamente, no solo los matches exactos de hash. Si dos peticiones comparten 137 tokens iniciales, RadixAttention lo encuentra; si una tercera comparte 89, también.

Eviction inteligente del trie

Los nodos del trie tienen un score basado en cuántas veces se han usado recientemente y cuántos descendientes tienen. Cuando hay presión de memoria, SGLang descarta los nodos menos valiosos primero, manteniendo los caminos más “calientes”. Esto es LRU + un peso por reutilización potencial.

Resultados

El paper de SGLang y benchmarks posteriores reportan hasta 6.4× throughput vs sin prefix caching, y un gap consistente del 29% sobre el prefix caching basado en hash de vLLM en cargas mixtas. En cargas con prefijos muy compartidos (agentes ReAct, multi-tenant SaaS, repo Q&A con system prompt común), los hit rates llegan al 60-85% y el ahorro de coste por petición es de 5-12×.

Producción

SGLang está en producción en xAI (sirviendo Grok 3) y Microsoft Azure (DeepSeek R1 en GPUs AMD), entre otros. No es un experimento; es un sistema de inferencia maduro.

Cuándo elegirlo sobre vLLM

Para cargas con prefijos compartidos masivos y predecibles, SGLang gana claramente. Para cargas genéricas mezcladas, vLLM rinde mejor por simplicidad operativa. El criterio operativo: si tu hit rate de prefix caching estimado en vLLM pasaría del 50%, plantéate SGLang.

Speculative decoding: la dimensión ortogonal

PagedAttention y sus sucesores optimizan dónde y cómo vive el cache. Speculative decoding ataca cómo se generan los tokens, ortogonalmente al cache. La idea genérica: usar un modelo pequeño y rápido para adivinar varios tokens por adelantado, validarlos en paralelo con el modelo grande y aceptar los que coinciden.

EAGLE-3 (2025)

EAGLE-3 (huggingface.co/papers/2401.15077, versión 3 de 2025) entrena una cabeza auto-regresiva pequeña que se condiciona en tres puntos del hidden state del modelo target (early, middle, late layers) en lugar de solo en el último. Esta fusión tri-layer es la razón por la que EAGLE-3 supera a EAGLE-2 en un 20-40%. Latencia medida: 2-6× speedup según tamaño de modelo y batch.

Medusa y DeepSeek MTP

Medusa fija N cabezas de decodificación adicionales al modelo, cada una prediciendo posición +1, +2, +3. DeepSeek-V3 ships con MTP (Multi-Token Prediction) nativo, n=4, entrenado conjuntamente con el modelo principal (no es un drafter externo). En inferencia, basta un flag en SGLang o vLLM (--speculative-model deepseek-v3-mtp) y obtienes 1.8× speedup out of the box, sin entrenar nada adicional, sin pesos extras que hospedar.

Mirror Speculative Decoding (2025)

Mirror Speculative Decoding (arxiv 2510.13161) ataca un límite que se daba por dado: la verificación de los tokens especulados sigue siendo serial dentro del modelo target. Mirror Decoding reorganiza el cómputo para paralelizar también la verificación, rompiendo la barrera serial del paradigma original. Las ganancias añadidas dependen del modelo y del batch, pero el paper lo posiciona como el próximo paso de la trayectoria EAGLE → EAGLE-2 → EAGLE-3.

Estado en 2026

Speculative decoding dejó de ser optimización experimental en 2026 para convertirse en capa por defecto de cualquier stack serio. Combinado con KV cache optimizado, los números reportados son 2.8× menos latencia y 47% menos coste por token.

Caveat operativo: speculative decoding es contraproducente en cargas de baja concurrencia. Si el modelo target tiene poco batch para llenar la GPU, las cabezas especulativas no compensan su overhead. Por debajo de ~4 sesiones simultáneas, suele bajar el throughput. Por encima, lo sube. Mídelo en tu carga antes de activarlo.

Implicaciones operativas: el config 2026 para vLLM

Si en 2026 montas vLLM en producción sin pensar mucho, los flags razonables por defecto son:

args:
- --model=...
- --tensor-parallel-size=N
- --max-model-len=...
- --kv-cache-dtype=fp8                      # cuantización del cache
- --enable-prefix-caching                   # ahorro fácil en cargas con prompts compartidos
- --enable-chunked-prefill                  # mejor mezcla prefill/decode
- --gpu-memory-utilization=0.92             # ya cubierto en el post anterior
- --speculative-model=...                   # SI batch sostenido >4
- --num-speculative-tokens=4                # acompaña al anterior
- --max-num-seqs=128                        # admission control para evitar thrashing
- --preemption-mode=recompute               # o swap si sesiones largas

Para cargas con prefijos masivamente compartidos (agentes), considera migrar a SGLang: el delta de eficiencia compensa la curva de aprendizaje. Para cargas de baja latencia con modelos estables (entrenados in-house, no cambias cada semana), TensorRT-LLM sigue ganando en latencia pura. Para todo lo demás —que es la mayoría—, vLLM con los flags de arriba está dentro del 10% del óptimo en throughput.

Para arquitecturas grandes (>100 sesiones concurrentes, SLO estricto), disaggregated serving ya no es opcional. NVIDIA Dynamo o llm-d como orquestadores; vLLM o SGLang como motores debajo. La división típica: 1 nodo de prefill por cada 3-4 de decode, ajustando ratios según la longitud media de los prompts.

Trampas y mitos comunes

“PagedAttention vs vAttention” como dilema

No es un dilema. vAttention es una optimización de runtime; el modelo mental sigue siendo paging. La elección es entre dos implementaciones del mismo concepto. Operativamente: si tienes la versión de vLLM que lo soporta y CUDA VMM disponible, vAttention da más throughput; si no, paged va perfectamente.

“Cache compression sin probar calidad”

La industria de papers de compresión es prolífica y los benchmarks varían enormemente entre los del autor y los reales en producción. Compresión 8× parece mágico hasta que mides degradación en tu corpus real. Siempre evalúa con tus datos antes de activar compresión agresiva. Un FP8 cache es seguro casi siempre. Un INT4 cache requiere medir caso por caso.

“Prefix caching con prompts no determinísticos”

Si tu pipeline inyecta timestamps, IDs únicos o cualquier variabilidad en el system prompt, el hit rate de prefix caching se cae a cero. Es la trampa más común. Para que funcione, los prompts compartidos deben ser bit-a-bit idénticos. Estructura los prompts en capas: parte estática primero, variable al final.

“Speculative decoding en cargas bajas”

Ya lo mencionamos: por debajo de ~4 sesiones simultáneas, speculative suele ser contraproducente. Si tu carga es batch puro o muy esporádica, no la actives.

“Disaggregated en cluster sin red rápida”

Si tu inter-nodo es Ethernet 25 GbE o peor, la transferencia del KV cache entre prefill y decode se convierte en cuello de botella. Disaggregation es para clusters con InfiniBand o RoCE 100/200/400 GbE. Sin eso, mejor colocated.

Lo que no hemos cubierto

Hay terreno suficiente para otra serie:

  • Mooncake (Kimi/Moonshot, 2024+): KV cache como pool compartido entre instancias, persistente en RAM/NVMe. Producción real con cientos de millones de queries.
  • LMCache: cache de KV persistente en disco entre arranques de vLLM. Reduce el coste de los primeros tokens en cargas con repetición temporal.
  • vLLM Production Stack: distribución k8s-native de vLLM con HPA, métricas, multi-modelo, ya probada en producción a escala.
  • Inference scheduling teórico: hay literatura aplicando CFS-like algorithms (el scheduler de Linux) al LLM serving. Promete fairness multi-tenant medible. Aún en fase académica.
  • Quantization del modelo combinada con quantization del cache: AWQ/GPTQ sobre los pesos + FP8 sobre el cache + INT4 sobre cache evictado. La pirámide completa.

Referencias

Los papers fundacionales y las extensiones más leídas, en orden cronológico:

Operacional: