<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Lmcache on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/lmcache/</link><description>Recent content in Lmcache on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Fri, 12 Jun 2026 05:00:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/tags/lmcache/index.xml" rel="self" type="application/rss+xml"/><item><title>Contexto largo y KV offloading: cuando el cuaderno de notas no cabe en la mesa</title><link>https://blog.lo0.es/posts/contexto-largo-kv-offloading-tiered/</link><pubDate>Fri, 12 Jun 2026 05:00:00 +0200</pubDate><guid>https://blog.lo0.es/posts/contexto-largo-kv-offloading-tiered/</guid><description>&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>Servir contexto largo &lt;strong>no es servir un modelo más listo, es gestionar un cuaderno de notas que no cabe en la mesa&lt;/strong>. La ventana de contexto la pone el modelo (y se extiende con RoPE scaling / YaRN), pero el coste de servirla lo pone el &lt;strong>KV cache&lt;/strong>, que crece linealmente con la longitud de secuencia y, en producción, &lt;strong>explota&lt;/strong>: un contrato de 300k tokens en Llama 3 70B se come ~93 GB de KV —más que una H100 entera— y un millón de tokens pide ~125 GB. Cuando el KV no cabe en la HBM, solo hay dos salidas: &lt;strong>recomputar&lt;/strong> (carísimo, la atención es cuadrática) o &lt;strong>descargar&lt;/strong> (offload) el KV a una jerarquía de memoria más barata: DRAM, NVMe, red. El estado del arte OSS de 2026 —&lt;strong>LMCache&lt;/strong>, &lt;strong>Mooncake&lt;/strong> (la plataforma de Kimi) y &lt;strong>NVIDIA Dynamo/KVBM&lt;/strong>— convierte ese offload en una &lt;strong>capa de KV de primera clase&lt;/strong>, con reutilización entre peticiones, &lt;em>prefill/decode disaggregation&lt;/em> y &lt;em>routing&lt;/em> consciente del caché. Resultados reportados: &lt;strong>3×–10×&lt;/strong> menos latencia con LMCache y hasta &lt;strong>+525 %&lt;/strong> de throughput en escenarios de contexto largo con Mooncake. El precio: cada salto de memoria añade latencia de transferencia, así que el offload &lt;strong>solo compensa cuando lo que ahorras recomputando supera lo que cuesta mover los bytes&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h2 id="la-analogía">La analogía&lt;/h2>
&lt;p>Un investigador trabaja en una mesa pequeña. Encima caben los papeles de los que está tirando &lt;strong>ahora mismo&lt;/strong> —eso es la HBM de la GPU, rapidísima pero diminuta. Cuando el caso es largo (un sumario de mil páginas, un contexto de un millón de tokens), los papeles no caben. Tiene tres opciones:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Tirar papeles y volver a pedirlos al archivo cada vez que los necesita.&lt;/strong> Es recomputar el KV: correcto, pero lentísimo, porque &amp;ldquo;pedirlos al archivo&amp;rdquo; en un LLM significa volver a pasar todo el prompt por la atención —y la atención es &lt;strong>cuadrática&lt;/strong>.&lt;/li>
&lt;li>&lt;strong>Poner una estantería al lado de la mesa&lt;/strong> (la DRAM de la CPU) y, más allá, &lt;strong>un almacén en el sótano&lt;/strong> (NVMe) y &lt;strong>un depósito en otro edificio&lt;/strong> (red/objeto). Mover papeles entre la mesa y la estantería cuesta segundos, no horas. Eso es el &lt;strong>KV offloading jerárquico&lt;/strong>.&lt;/li>
&lt;li>&lt;strong>Que dos investigadores se repartan el trabajo:&lt;/strong> uno lee y subraya todo el sumario (prefill), pasa sus notas al segundo, que solo redacta (decode). Eso es la &lt;strong>arquitectura disaggregated KVCache-centric&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>Este post va de las opciones 2 y 3, que son las que hacen viable el contexto largo en producción. La 1 es la que pagas por defecto si no haces nada.&lt;/p>
&lt;hr>
&lt;h2 id="parte-1--por-qué-el-contexto-largo-es-un-problema-de-memoria">Parte 1 · Por qué el contexto largo es un problema de memoria&lt;/h2>
&lt;h3 id="el-kv-cache-crece-con-la-secuencia">El KV cache crece con la secuencia&lt;/h3>
&lt;p>Por cada token que entra o sale, el modelo guarda sus vectores &lt;em>key&lt;/em> y &lt;em>value&lt;/em> en cada capa para no recomputarlos. El tamaño es:&lt;/p>
$$\text{KV bytes} = 2 \times L \times h_{kv} \times d_{head} \times s \times b$$
&lt;p>con \(L\) capas, \(h_{kv}\) cabezas KV (con GQA, pocas), \(d_{head}\) la dimensión por cabeza, \(s\) la longitud de secuencia y \(b\) los bytes por elemento. Todo es constante del modelo &lt;strong>salvo \(s\)&lt;/strong>: el KV es &lt;strong>lineal en la longitud de contexto&lt;/strong>. Doblar el contexto dobla el KV. Es la base de &lt;a href="https://blog.lo0.es/posts/kv-cache-fundamentos/">KV cache: la memoria de trabajo de la inferencia&lt;/a>.&lt;/p>
&lt;h3 id="los-números-asustan">Los números asustan&lt;/h3>
&lt;p>A escala de producción, ese término lineal se vuelve brutal:&lt;/p>
&lt;ul>
&lt;li>Un &lt;strong>contrato de 300k tokens&lt;/strong> en &lt;strong>Llama 3 70B&lt;/strong> consume &lt;strong>~93 GB&lt;/strong> solo de KV — &lt;strong>más que los 80 GB de una H100 entera&lt;/strong> (&lt;a href="https://www.digitalocean.com/community/tutorials/long-context-inference-production-cost">DigitalOcean · Long-Context Inference Cost&lt;/a>).&lt;/li>
&lt;li>Un &lt;strong>contexto de 1M tokens&lt;/strong> necesita &lt;strong>~125 GB&lt;/strong> de KV, que excede tanto una RTX 4090 (24 GB) como una A100 de 80 GB (&lt;a href="https://introl.com/blog/long-context-llm-infrastructure-million-token-windows-guide">Introl · Long-Context LLM Infrastructure&lt;/a>).&lt;/li>
&lt;li>Incluso un &lt;strong>7B a 128k&lt;/strong> sube a &lt;strong>~14 GB&lt;/strong> de KV (frente a ~6 GB a 4k).&lt;/li>
&lt;/ul>
&lt;p>El KV deja de ser un detalle de implementación y se convierte en &lt;strong>el recurso que dicta tu concurrencia&lt;/strong>.&lt;/p>
&lt;h3 id="y-encima-la-atención-es-cuadrática">Y encima la atención es cuadrática&lt;/h3>
&lt;p>El KV es lineal, pero el &lt;strong>cómputo de la atención es cuadrático&lt;/strong> en la longitud. Cuando el prompt llega a 1M tokens, generar cada token puede requerir del orden de &lt;strong>1.765 segundos, con más del 96 % de la latencia gastada en atención&lt;/strong> (&lt;a href="https://introl.com/blog/long-context-llm-infrastructure-million-token-windows-guide">Introl&lt;/a>). El efecto agregado es un &lt;strong>colapso de throughput de 10×–100×&lt;/strong> frente a contextos cortos. Por eso el contexto largo no se &amp;ldquo;arregla&amp;rdquo; solo con más VRAM: hay un problema de memoria (KV) &lt;strong>y&lt;/strong> un problema de cómputo (atención) a la vez.&lt;/p>
&lt;h3 id="extender-la-ventana--servirla-barato">Extender la ventana ≠ servirla barato&lt;/h3>
&lt;p>Una confusión habitual: &amp;ldquo;mi modelo soporta 1M de contexto&amp;rdquo; no significa &amp;ldquo;puedo servir 1M barato&amp;rdquo;. La ventana se &lt;strong>extiende&lt;/strong> con técnicas de &lt;em>RoPE scaling&lt;/em> como &lt;strong>YaRN&lt;/strong>, que es la opción práctica para &lt;em>fine-tunear&lt;/em> modelos open source a contexto largo: necesita &lt;strong>10× menos tokens de entrenamiento y 2,5× menos pasos&lt;/strong> que la interpolación RoPE naíf, y llevó LLaMA-2 de 4k a 32k y 128k (&lt;a href="https://arxiv.org/html/2402.13753v1">YaRN/LongRoPE&lt;/a>). Pero eso resuelve que el modelo &lt;strong>atienda&lt;/strong> contexto largo, no que &lt;strong>te quepa el KV en la GPU&lt;/strong>. Según los recopilatorios de 2026, mayo de 2026 marcó la primera generación de modelos open de &lt;strong>millón de tokens&lt;/strong> (con familias que soportan 256k extensibles a 1M vía YaRN) (&lt;a href="https://letsdatascience.com/blog/long-context-models-working-with-1m-token-windows">letsdatascience&lt;/a>) — trátalo como tendencia, no como spec cerrada, y verifica capacidades por modelo concreto.&lt;/p>
&lt;hr>
&lt;h2 id="parte-2--la-jerarquía-de-memoria-del-kv">Parte 2 · La jerarquía de memoria del KV&lt;/h2>
&lt;p>La idea central del offload es vieja en sistemas: &lt;strong>caching jerárquico&lt;/strong>. El KV &amp;ldquo;caliente&amp;rdquo; (lo que se está usando) vive en HBM; el &amp;ldquo;templado&amp;rdquo; baja a DRAM; el &amp;ldquo;frío&amp;rdquo; a NVMe; el &amp;ldquo;compartido entre nodos&amp;rdquo; a red u objeto. Cada salto multiplica la capacidad y divide el ancho de banda.&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 300" role="img" aria-label="Jerarquía de memoria del KV cache: HBM, DRAM, NVMe y red/objeto, con capacidad y ancho de banda crecientes/decrecientes" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.tl{font:600 13px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.2;stroke-dasharray:4 3;marker-end:url(#kvm)}&lt;/style>
&lt;defs>&lt;marker id="kvm" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;text x="20" y="26" class="tl">Caliente · rápido · pequeño&lt;/text>
&lt;rect class="bx" x="20" y="36" width="430" height="44" rx="6"/>
&lt;text x="34" y="55" class="tl">GPU HBM&lt;/text>
&lt;text x="34" y="72" class="ts">~80 GB · varios TB/s · aquí vive el KV activo&lt;/text>
&lt;rect class="bx" x="20" y="92" width="500" height="44" rx="6"/>
&lt;text x="34" y="111" class="tl">CPU DRAM&lt;/text>
&lt;text x="34" y="128" class="ts">cientos de GB–TB · decenas–cientos GB/s · KV templado, reuse entre peticiones&lt;/text>
&lt;rect class="bx" x="20" y="148" width="570" height="44" rx="6"/>
&lt;text x="34" y="167" class="tl">NVMe SSD (local)&lt;/text>
&lt;text x="34" y="184" class="ts">TB · GB/s · KV frío, prefijos persistentes, sesiones largas&lt;/text>
&lt;rect class="bx" x="20" y="204" width="640" height="44" rx="6"/>
&lt;text x="34" y="223" class="tl">Red / objeto (RDMA, S3, Redis)&lt;/text>
&lt;text x="34" y="240" class="ts">~ilimitado · cientos MB–GB/s · KV compartido entre nodos del cluster&lt;/text>
&lt;path class="ar" d="M690,58 L690,226"/>
&lt;text x="700" y="120" class="ts" transform="rotate(90 700 120)">+capacidad&lt;/text>
&lt;text x="700" y="200" class="ts" transform="rotate(90 700 200)">−ancho banda&lt;/text>
&lt;text x="20" y="276" class="ts">El KV baja de nivel a medida que se enfría. Recuperarlo de un nivel inferior cuesta una transferencia,&lt;/text>
&lt;text x="20" y="292" class="ts">pero SIEMPRE es más barato que recomputarlo (atención cuadrática) si el prefijo se reutiliza.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;h3 id="el-cálculo-de-cuándo-compensa-el-offload">El cálculo de cuándo compensa el offload&lt;/h3>
&lt;p>El offload no es magia: mover KV de DRAM/NVMe de vuelta a HBM cuesta tiempo. La regla es simple — &lt;strong>descarga compensa cuando el coste de recomputar supera el coste de transferir&lt;/strong>:&lt;/p>
$$t_{recompute}(s) \;>\; t_{transfer} = \frac{\text{KV bytes}}{BW_{enlace}}$$
&lt;p>Como \(t_{recompute}\) crece con la atención (cuadrático) y \(t_{transfer}\) crece solo con el tamaño del KV (lineal) dividido por el ancho de banda del enlace, &lt;strong>cuanto más largo el contexto, más a favor del offload juega la balanza&lt;/strong>. Por eso el offload es la palanca &lt;em>del&lt;/em> contexto largo: es justo donde recomputar se vuelve insoportable. La documentación de LMCache lo dice sin rodeos: el offload &lt;strong>solo ayuda cuando la recomputación que evita es mayor que el overhead que introduce&lt;/strong> (&lt;a href="https://levelup.gitconnected.com/vllm-prefix-caching-vs-lmcache-benchmarking-kv-reuse-tradeoffs-944fbaf98b56">LMCache benchmark&lt;/a>).&lt;/p>
&lt;hr>
&lt;h2 id="parte-3--kv-offloading-en-la-práctica-oss">Parte 3 · KV offloading en la práctica (OSS)&lt;/h2>
&lt;h3 id="el-límite-del-prefix-caching-de-serie">El límite del prefix caching &amp;ldquo;de serie&amp;rdquo;&lt;/h3>
&lt;p>vLLM ya cachea prefijos en HBM (ver &lt;a href="https://blog.lo0.es/posts/prefix-cache-hit-rate-engineering/">prefix cache: ingeniería del hit rate&lt;/a> y &lt;a href="https://blog.lo0.es/posts/pagedattention-deep-dive/">PagedAttention&lt;/a>). El problema: &lt;strong>solo vive en HBM&lt;/strong>, que es pequeña. Cuando el KV útil excede la HBM, se desaloja y, en la siguiente petición, hay que recomputarlo — &lt;strong>no hay speedup aunque el prefix caching esté activado&lt;/strong>, porque el caché ya no está (&lt;a href="https://levelup.gitconnected.com/vllm-prefix-caching-vs-lmcache-benchmarking-kv-reuse-tradeoffs-944fbaf98b56">benchmark vLLM vs LMCache&lt;/a>). El prefix caching nativo es necesario pero &lt;strong>insuficiente&lt;/strong> para contexto largo.&lt;/p>
&lt;h3 id="lmcache-la-capa-de-kv-persistente">LMCache: la capa de KV persistente&lt;/h3>
&lt;p>&lt;strong>LMCache&lt;/strong> añade backends de almacenamiento persistente al prefix cache de vLLM (y soporta también SGLang y NVIDIA Dynamo como motores). Extrae el KV de la HBM y lo &lt;strong>comparte entre motores y entre consultas&lt;/strong>, cubriendo una jerarquía de &lt;strong>tres niveles: GPU HBM, CPU DRAM y NVMe SSD&lt;/strong>, con backends adicionales de Redis/Valkey, Mooncake, InfiniStore, S3 y NIXL/GDS (&lt;a href="https://github.com/LMCache/LMCache">GitHub LMCache&lt;/a>, &lt;a href="https://arxiv.org/pdf/2510.09665">arXiv 2510.09665&lt;/a>). Como la DRAM/NVMe guardan &lt;strong>mucho más&lt;/strong> KV que la HBM, el &lt;em>hit ratio&lt;/em> sube, y LMCache reporta &lt;strong>3×–10× menos latencia&lt;/strong> combinado con vLLM. Frente al offload básico de vLLM a CPU, LMCache carga el KV &lt;strong>a nivel de chunk con kernels CUDA de alto rendimiento&lt;/strong>, lo que reduce el overhead de transferencia.&lt;/p>
&lt;p>Un arranque mínimo, offload a CPU:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># vLLM con LMCache como capa de KV (offload a DRAM)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install lmcache
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">LMCACHE_CONFIG_FILE&lt;/span>&lt;span class="o">=&lt;/span>lmcache.yaml &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span>vllm serve meta-llama/Llama-3.1-70B-Instruct &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --tensor-parallel-size &lt;span class="m">4&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --kv-transfer-config &lt;span class="s1">&amp;#39;{&amp;#34;kv_connector&amp;#34;:&amp;#34;LMCacheConnector&amp;#34;,&amp;#34;kv_role&amp;#34;:&amp;#34;kv_both&amp;#34;}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># lmcache.yaml — jerarquía HBM -&amp;gt; DRAM -&amp;gt; NVMe&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">chunk_size&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">256&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">local_cpu&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># KV templado en DRAM&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">max_local_cpu_size&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">200&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># GB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">local_disk&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;file:///nvme/lmcache&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># KV frío en NVMe&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">max_local_disk_size&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">2000&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># GB&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>El offload a CPU añade overhead moderado pero &lt;strong>retiene buena parte del beneficio&lt;/strong> frente a recomputar (&lt;a href="https://docs.lmcache.ai/getting_started/quickstart/offload_kv_cache.html">LMCache docs&lt;/a>). Ojo a la ruta física: ese tráfico HBM↔DRAM↔NVMe pasa por PCIe y por los nodos NUMA — conviene leer &lt;a href="https://blog.lo0.es/posts/pcie-topology-gpudirect-p2p-acs/">topología PCIe, GPUDirect y ACS&lt;/a> y &lt;a href="https://blog.lo0.es/posts/numa-hugepages-aislamiento-cpu-inferencia/">NUMA, hugepages y aislamiento de CPU&lt;/a>, porque un offload mal cableado puede comerse su propia ganancia.&lt;/p>
&lt;h3 id="combínalo-con-fp8">Combínalo con FP8&lt;/h3>
&lt;p>El offload reduce &lt;strong>dónde&lt;/strong> vive el KV; la cuantización reduce &lt;strong>cuánto&lt;/strong> pesa. Son ortogonales y se suman: KV en FP8 corta el tamaño a la mitad (ver &lt;a href="https://blog.lo0.es/posts/fp8-end-to-end-pesos-kv-calidad/">FP8 end-to-end&lt;/a>), lo que significa la mitad de bytes que transferir en cada offload. En contexto largo, &lt;strong>FP8 + offload&lt;/strong> es la combinación por defecto.&lt;/p>
&lt;hr>
&lt;h2 id="parte-4--arquitectura-kvcache-centric--disaggregated">Parte 4 · Arquitectura KVCache-centric / disaggregated&lt;/h2>
&lt;p>El siguiente nivel no es solo descargar KV, sino &lt;strong>rediseñar el serving alrededor del KV&lt;/strong>.&lt;/p>
&lt;h3 id="mooncake-la-plataforma-de-kimi">Mooncake (la plataforma de Kimi)&lt;/h3>
&lt;p>&lt;strong>Mooncake&lt;/strong>, la plataforma de serving de &lt;strong>Kimi&lt;/strong> (Moonshot AI), es la referencia de arquitectura &lt;strong>KVCache-centric disaggregated&lt;/strong>: separa los clusters de &lt;strong>prefill&lt;/strong> y &lt;strong>decode&lt;/strong>, y aprovecha los recursos &lt;strong>infrautilizados de CPU, DRAM y SSD&lt;/strong> del cluster GPU para montar un caché de KV distribuido. Su núcleo es un &lt;strong>scheduler centrado en el KVCache&lt;/strong> que maximiza throughput respetando SLOs, con una &lt;strong>política de rechazo temprano basada en predicción&lt;/strong> para escenarios sobrecargados (&lt;a href="https://arxiv.org/abs/2407.00079">arXiv 2407.00079&lt;/a>, &lt;a href="https://www.usenix.org/conference/fast25/presentation/qin">USENIX FAST'25&lt;/a>). Los números en contexto largo son llamativos: hasta &lt;strong>+525 % de throughput&lt;/strong> en ciertos escenarios simulados respetando SLO, y en producción Kimi maneja &lt;strong>115 % y 107 % más peticiones&lt;/strong> en clusters A800 y H800 respectivamente frente a sistemas previos. El título de su charla en FAST lo resume: &lt;em>&amp;ldquo;Trading More Storage for Less Computation&amp;rdquo;&lt;/em> — exactamente la regla de la Parte 2.&lt;/p>
&lt;h3 id="nvidia-dynamo-y-kvbm">NVIDIA Dynamo y KVBM&lt;/h3>
&lt;p>&lt;strong>NVIDIA Dynamo&lt;/strong> es el framework open source de serving distribuido con &lt;strong>prefill/decode disaggregated&lt;/strong>, scheduling dinámico de GPU y &lt;strong>routing consciente del KV&lt;/strong>: el &lt;em>PrefillRouter&lt;/em> calcula &lt;strong>overlap scores&lt;/strong> entre la petición entrante y los bloques de KV ya cacheados, y enruta a las GPUs que &lt;strong>ya tienen&lt;/strong> el KV relevante, evitando recomputar (&lt;a href="https://docs.dynamo.nvidia.com/dynamo/design-docs/disaggregated-serving">Dynamo · Disaggregated Serving&lt;/a>, &lt;a href="https://docs.nvidia.com/dynamo/latest/user-guides/kv-cache-aware-routing">Dynamo · KV-aware routing&lt;/a>). La transferencia de KV entre prefill y decode la hace &lt;strong>NIXL&lt;/strong> directamente GPU-a-GPU por el mejor transporte disponible (NVLink, InfiniBand). Y su gestor de offload, &lt;strong>KVBM (KV Block Manager)&lt;/strong>, tiene una arquitectura de tres capas (runtime LLM, gestión lógica de bloques, transporte NIXL) que &lt;strong>descarga el KV frío a CPU RAM, NVMe o almacenamiento en red&lt;/strong> para liberar HBM (&lt;a href="https://docs.nvidia.com/dynamo/backends/v-llm/kv-cache-offloading">Dynamo · KV Cache Offloading&lt;/a>). Conecta con &lt;a href="https://blog.lo0.es/posts/disaggregated-serving-prefill-decode/">disaggregated serving: prefill y decode en pods especializados&lt;/a>.&lt;/p>
&lt;p>El proyecto &lt;strong>llm-d&lt;/strong> recorre el mismo camino desde el prefix caching de vLLM hasta el scheduling distribuido con conciencia de KV (&lt;a href="https://llm-d.ai/blog/kvcache-wins-you-can-see">llm-d · KV-Cache Wins You Can See&lt;/a>).&lt;/p>
&lt;div class="diagram" style="max-width:780px;margin:1rem auto;">
&lt;svg viewBox="0 0 780 250" role="img" aria-label="Arquitectura KVCache-centric disaggregated: router consciente de KV, cluster de prefill, caché de KV distribuido por niveles y cluster de decode" xmlns="http://www.w3.org/2000/svg">
&lt;style>.bx{fill:none;stroke:currentColor;stroke-width:1.3}.dsh{fill:none;stroke:currentColor;stroke-width:1.3;stroke-dasharray:5 3}.tl{font:600 12px sans-serif;fill:currentColor}.ts{font:11px sans-serif;fill:currentColor}.ar{fill:none;stroke:currentColor;stroke-width:1.3;marker-end:url(#dgm)}&lt;/style>
&lt;defs>&lt;marker id="dgm" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="currentColor"/>&lt;/marker>&lt;/defs>
&lt;rect class="bx" x="20" y="40" width="120" height="46" rx="6"/>
&lt;text x="32" y="60" class="tl">Router KV-aware&lt;/text>
&lt;text x="32" y="77" class="ts">overlap score&lt;/text>
&lt;path class="ar" d="M140,63 L185,63"/>
&lt;rect class="bx" x="185" y="40" width="150" height="46" rx="6"/>
&lt;text x="197" y="60" class="tl">Cluster PREFILL&lt;/text>
&lt;text x="197" y="77" class="ts">lee+subraya el contexto&lt;/text>
&lt;path class="ar" d="M260,86 L260,120"/>
&lt;rect class="dsh" x="120" y="120" width="430" height="50" rx="6"/>
&lt;text x="134" y="140" class="tl">Caché de KV distribuido (HBM · DRAM · NVMe · red)&lt;/text>
&lt;text x="134" y="158" class="ts">KVBM / LMCache / Mooncake store · reuse entre peticiones y nodos&lt;/text>
&lt;path class="ar" d="M410,120 L410,86"/>
&lt;rect class="bx" x="430" y="40" width="150" height="46" rx="6"/>
&lt;text x="442" y="60" class="tl">Cluster DECODE&lt;/text>
&lt;text x="442" y="77" class="ts">solo genera tokens&lt;/text>
&lt;path class="ar" d="M335,63 L430,63"/>
&lt;text x="600" y="50" class="ts">NIXL: transferencia&lt;/text>
&lt;text x="600" y="66" class="ts">KV GPU↔GPU directa&lt;/text>
&lt;text x="600" y="82" class="ts">(NVLink/IB)&lt;/text>
&lt;text x="20" y="200" class="ts">El KV deja de ser un subproducto del decode y pasa a ser el ciudadano de primera clase: el router enruta por&lt;/text>
&lt;text x="20" y="216" class="ts">solapamiento de KV, prefill y decode se separan, y el caché distribuido evita recomputar prefijos compartidos.&lt;/text>
&lt;text x="20" y="232" class="ts">Es "cambiar almacenamiento por cómputo": guardas más KV para calcular menos atención.&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h2 id="parte-5--arquitectura-de-referencia">Parte 5 · Arquitectura de referencia&lt;/h2>
&lt;p>Sobre el cluster de ejemplo del blog —un nodo &lt;strong>4×H100 SXM (80 GB, NVLink)&lt;/strong> con &lt;strong>NVMe local&lt;/strong> y DRAM holgada— una pila sensata para servir contexto largo sin recomputar a lo bobo:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>KV en FP8&lt;/strong> en el motor (vLLM), para empezar con la mitad de bytes.&lt;/li>
&lt;li>&lt;strong>LMCache como capa de KV&lt;/strong> con jerarquía DRAM→NVMe, de modo que los prefijos largos (un manual, un código base, un sumario) &lt;strong>se cacheen una vez y se reutilicen&lt;/strong> entre sesiones sin recomputar.&lt;/li>
&lt;li>&lt;strong>Routing consciente de KV&lt;/strong> (Dynamo o llm-d) si tienes varios nodos: que la petición vaya a la GPU que ya tiene el prefijo, no a una cualquiera.&lt;/li>
&lt;li>&lt;strong>Prefill/decode disaggregation&lt;/strong> cuando el prefill de contextos enormes empiece a robarle decode a las demás peticiones — el prefill de 300k tokens monopoliza la GPU y mata la latencia de todos.&lt;/li>
&lt;li>&lt;strong>Métricas&lt;/strong> de &lt;em>hit ratio&lt;/em> del KV, bytes transferidos por nivel y ratio prefill/decode, exportadas a tu observabilidad (&lt;a href="https://blog.lo0.es/posts/vllm-otel-instrumentacion-optimizaciones/">instrumentar vLLM con OTel&lt;/a>). Sin esas tres métricas, el offload es fe, no ingeniería.&lt;/li>
&lt;/ol>
&lt;p>Para &lt;strong>prototipar&lt;/strong> la pila —validar la config de LMCache, el formato de los backends, el comportamiento de hit/miss— una &lt;strong>RTX 5090 (Blackwell, 32 GB)&lt;/strong> con NVMe sobra para servir un 7–14B y ver el offload funcionando a contextos de 32–128k. No esperes servir 1M en una tarjeta de consumo: el problema de contexto largo es, precisamente, que ni el KV ni la atención caben en hardware pequeño. El dimensionamiento real parte del SLO y de la distribución de longitudes de contexto de tu tráfico, no de la ventana máxima del modelo — y ahí enlaza con &lt;a href="https://blog.lo0.es/posts/capacity-planning-inferencia-llm-on-premise/">capacity planning&lt;/a>.&lt;/p>
&lt;hr>
&lt;h2 id="parte-6--pitfalls-operativos-y-escepticismo-honesto">Parte 6 · Pitfalls operativos (y escepticismo honesto)&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>Creer que el prefix caching nativo basta.&lt;/strong> Solo vive en HBM; en contexto largo se desaloja y recomputas. Necesitas una capa persistente (LMCache/KVBM) para que el caché sobreviva.&lt;/li>
&lt;li>&lt;strong>Offload sin medir el break-even.&lt;/strong> Si tus prefijos &lt;strong>no se reutilizan&lt;/strong>, el offload solo añade latencia de transferencia sin ahorrar recomputación. El offload brilla con &lt;strong>prefijos compartidos&lt;/strong> (RAG sobre el mismo corpus, agentes con el mismo system prompt, sesiones largas), no con peticiones únicas e irrepetibles.&lt;/li>
&lt;li>&lt;strong>Olvidar que la atención sigue siendo cuadrática.&lt;/strong> El offload arregla la &lt;strong>memoria&lt;/strong> del KV, no el &lt;strong>cómputo&lt;/strong> de la atención. Para 1M tokens reales necesitas además atención eficiente, &lt;em>sparse attention&lt;/em> o enfoques de recuperación (RetrievalAttention, ShadowKV); el offload por sí solo no baja los 1.765 s/token.&lt;/li>
&lt;li>&lt;strong>Cablear mal el offload.&lt;/strong> HBM↔DRAM↔NVMe atraviesa PCIe y NUMA. Un offload que cruza el nodo NUMA equivocado o satura un carril PCIe puede ser &lt;strong>más lento que recomputar&lt;/strong>. Mide el ancho de banda real del enlace, no el del datasheet.&lt;/li>
&lt;li>&lt;strong>Tratar las cifras como constantes.&lt;/strong> El 93 GB, el +525 %, el 3–10× dependen de modelo, longitud, hardware y patrón de reutilización. Son &lt;strong>órdenes de magnitud para diseñar&lt;/strong>; mide en tu carga.&lt;/li>
&lt;li>&lt;strong>Confiar specs de modelos de million-token sin verificar.&lt;/strong> El panorama de modelos open de 1M es muy reciente (2026) y se mueve rápido; los nombres y límites concretos cambian entre versiones. Verifica la ventana &lt;strong>y el coste de servirla&lt;/strong> por modelo, no te fíes del titular.&lt;/li>
&lt;/ol>
&lt;p>Una nota de prudencia para junio de 2026: la capa de KV distribuido (LMCache, Mooncake, KVBM, NIXL, llm-d) está &lt;strong>cuajando ahora mismo&lt;/strong>, con APIs y backends que cambian release a release. Es exactamente el tipo de pieza que conviene montar con una abstracción por medio (el &lt;em>kv_connector&lt;/em> de vLLM) y no acoplarse a un solo proveedor.&lt;/p>
&lt;hr>
&lt;h2 id="cierre">Cierre&lt;/h2>
&lt;p>El contexto largo es un problema de &lt;strong>memoria y de cómputo a la vez&lt;/strong>, y el KV cache es el cuello de los dos. La ventana la regala el modelo; servirla barata es ingeniería de sistemas: &lt;strong>cuantiza el KV (FP8), descárgalo por niveles (HBM→DRAM→NVMe→red) con una capa persistente, y reorganiza el serving alrededor del KV&lt;/strong> (routing consciente, prefill/decode separados) cuando la escala lo pida. La regla que lo gobierna todo es la de Mooncake: &lt;strong>cambiar almacenamiento por cómputo&lt;/strong>. Guardas más KV en sitios baratos para no recomputar atención cara. Hazlo bien y un nodo de 4×H100 sirve contextos que, recomputando, ni siquiera arrancarían. Hazlo mal —o no lo hagas— y cada petición larga vuelve a leerse el sumario entero desde cero, mientras la GPU se ahoga en una atención cuadrática que ya habías pagado una vez.&lt;/p>
&lt;h2 id="fuentes">Fuentes&lt;/h2>
&lt;ul>
&lt;li>LMCache · GitHub — &lt;a href="https://github.com/LMCache/LMCache">https://github.com/LMCache/LMCache&lt;/a>&lt;/li>
&lt;li>LMCache: An Efficient KV Cache Layer (arXiv 2510.09665) — &lt;a href="https://arxiv.org/pdf/2510.09665">https://arxiv.org/pdf/2510.09665&lt;/a>&lt;/li>
&lt;li>LMCache · Offload KV cache to CPU (docs) — &lt;a href="https://docs.lmcache.ai/getting_started/quickstart/offload_kv_cache.html">https://docs.lmcache.ai/getting_started/quickstart/offload_kv_cache.html&lt;/a>&lt;/li>
&lt;li>vLLM Prefix Caching vs. LMCache: Benchmarking KV Reuse Tradeoffs — &lt;a href="https://levelup.gitconnected.com/vllm-prefix-caching-vs-lmcache-benchmarking-kv-reuse-tradeoffs-944fbaf98b56">https://levelup.gitconnected.com/vllm-prefix-caching-vs-lmcache-benchmarking-kv-reuse-tradeoffs-944fbaf98b56&lt;/a>&lt;/li>
&lt;li>Mooncake: A KVCache-centric Disaggregated Architecture (arXiv 2407.00079) — &lt;a href="https://arxiv.org/abs/2407.00079">https://arxiv.org/abs/2407.00079&lt;/a>&lt;/li>
&lt;li>Mooncake · USENIX FAST'25 (Trading More Storage for Less Computation) — &lt;a href="https://www.usenix.org/conference/fast25/presentation/qin">https://www.usenix.org/conference/fast25/presentation/qin&lt;/a>&lt;/li>
&lt;li>Mooncake · GitHub — &lt;a href="https://github.com/kvcache-ai/Mooncake/">https://github.com/kvcache-ai/Mooncake/&lt;/a>&lt;/li>
&lt;li>NVIDIA Dynamo · Disaggregated Serving (docs) — &lt;a href="https://docs.dynamo.nvidia.com/dynamo/design-docs/disaggregated-serving">https://docs.dynamo.nvidia.com/dynamo/design-docs/disaggregated-serving&lt;/a>&lt;/li>
&lt;li>NVIDIA Dynamo · KV-aware routing (docs) — &lt;a href="https://docs.nvidia.com/dynamo/latest/user-guides/kv-cache-aware-routing">https://docs.nvidia.com/dynamo/latest/user-guides/kv-cache-aware-routing&lt;/a>&lt;/li>
&lt;li>NVIDIA Dynamo · KV Cache Offloading / KVBM (docs) — &lt;a href="https://docs.nvidia.com/dynamo/backends/v-llm/kv-cache-offloading">https://docs.nvidia.com/dynamo/backends/v-llm/kv-cache-offloading&lt;/a>&lt;/li>
&lt;li>llm-d · KV-Cache Wins You Can See — &lt;a href="https://llm-d.ai/blog/kvcache-wins-you-can-see">https://llm-d.ai/blog/kvcache-wins-you-can-see&lt;/a>&lt;/li>
&lt;li>Long-Context Inference at Scale: The Hidden Infrastructure Cost · DigitalOcean — &lt;a href="https://www.digitalocean.com/community/tutorials/long-context-inference-production-cost">https://www.digitalocean.com/community/tutorials/long-context-inference-production-cost&lt;/a>&lt;/li>
&lt;li>Long-Context LLM Infrastructure · Introl — &lt;a href="https://introl.com/blog/long-context-llm-infrastructure-million-token-windows-guide">https://introl.com/blog/long-context-llm-infrastructure-million-token-windows-guide&lt;/a>&lt;/li>
&lt;li>YaRN / LongRoPE (arXiv 2402.13753) — &lt;a href="https://arxiv.org/html/2402.13753v1">https://arxiv.org/html/2402.13753v1&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>