KV Cache Offloading 完全拆解:把显存装不下的历史塞进 CPU、SSD 与远端内存

7416 字
37 分钟
KV Cache Offloading 完全拆解:把显存装不下的历史塞进 CPU、SSD 与远端内存

长上下文推理最硬的墙不是算力,而是显存容量。一个 70B 模型在 128K 上下文、批量 16 时,KV Cache 能涨到几百 GB,远超单张卡 80GB 的 HBM;就算把模型权重压到 4-bit,KV Cache 依然按序列长度和批量线性增长。推理系统过去十年解决的一直是”算得快不快”,而 KV Cache 逼着所有人回答另一个问题:显存装不下的那些历史,能不能放到别的存储层级里去,用的时候再取回来?

这就是 KV Cache Offloading(KV 缓存卸载)要解决的问题。它把 KV Cache 从”必须常驻 GPU 显存”这个假设里解放出来,允许一部分甚至大部分 KV 放在 CPU 内存(DRAM)、本地 SSD,乃至远端节点的内存池里,只在需要参与注意力计算时按需搬回 GPU。看似只是”换个大点的仓库”,真正的难点全在搬运:GPU 与 CPU 之间的 PCIe/NVLink 带宽有限、延迟不可忽略,怎么保证搬运不成为新的瓶颈、怎么只搬有用的那部分、怎么让搬运和计算重叠起来——这三件事构成了这个方向几乎全部的技术内容。

这篇文章不做单篇论文的精读,而是把 KV Cache 卸载当作一个完整的技术领域来讲:先讲清楚它为什么存在、代价从哪来,再拆开分层放置、按需召回、异步流水三根支柱,然后走一遍从 FlexGen、InfiniGen 到 Mooncake、LMCache 再到现在这批 2026 年新工作的演进脉络,最后落到工程实践中真正要做的取舍。站内已有 PagedAttention 完全拆解 讲 KV Cache 在显存里怎么分页管理,vAttention 完全拆解 讲怎么用 CUDA 虚拟内存保住连续布局,FlexGen 完全拆解 讲用线性规划编排三级存储——那几篇是把 KV 或权重放进”更大的仓库”,这篇则聚焦”搬运”本身以及由此衍生的一整套系统设计。

显存容量墙:这个问题从哪来

KV Cache 到底有多大

先把这个墙的高度算清楚。Transformer 解码时,每生成一个 token,都要把这一层的新 Key 和 Value 追加到缓存里,供后续所有 token 的注意力查询使用。单层单 token 的 KV 大小是:

bytes per token per layer=2×nkv×dhead×b\text{bytes per token per layer} = 2 \times n_{kv} \times d_{head} \times b

其中 nkvn_{kv} 是 KV 头数,dheadd_{head} 是每个头的维度,bb 是每元素字节数(FP16 为 2),前面的 2 是 Key 和 Value 各一份。对一个 70B 级、采用 GQA(分组查询注意力)的模型,取 nkv=8n_{kv}=8、dhead=128d_{head}=128、L=80L=80 层、FP16,则每 token 的 KV 总量是:

2×8×128×2×80≈327,680 bytes≈320 KB/token2 \times 8 \times 128 \times 2 \times 80 \approx 327{,}680\ \text{bytes} \approx 320\ \text{KB/token}

320 KB 听起来不大,但它是每 token的量。一次 128K 上下文的长文档问答,单条请求的 KV 就是 128000×320KB≈39 GB128000 \times 320\text{KB} \approx 39\ \text{GB}——一条请求就吃掉半张 80GB 卡的可用显存。如果再叠上批量 16,KV 总量直接到 600GB 级别,而模型权重(FP16 约 140GB)此时反而成了小头。这就是为什么长上下文场景下,纯显存方案无论如何省都省不下来:KV Cache 是随序列长度线性增长的活跃状态,不是可以提前一次性放好的静态权重。

图 1 来自 InfiniGen 论文(arXiv:2406.19707,2024 年发表于 OSDI),它把 KV Cache 和模型权重的大小随序列长度、批量的增长画在一起:虚线是模型权重的固定体积,实线是 KV Cache——随着序列长度或批量增大,KV 迅速越过权重成为显存的主要占用者。

InfiniGen 论文对比 KV Cache 与模型权重随序列长度和批量的增长(图来自 arXiv:2406.19707)
InfiniGen 论文对比 KV Cache 与模型权重随序列长度和批量的增长(图来自 arXiv:2406.19707)

为什么不能只靠”压缩”或”丢弃”

面对容量墙,历史上出现过两条”不搬运”的路线,各自都撞到了天花板。

第一条是压缩:把 KV 量化成 INT8、INT4 甚至 INT2。站内 KIVI 完全拆解、KVQuant 完全拆解 讲的就是这条路线,2-bit 量化能把 KV 压到原来的八分之一。但量化有精度下限,而且压缩比是有限的——从 FP16 到 2-bit 只能省 8 倍,128K 上下文的 39GB 变 5GB,批量一上来照样爆。更关键的是,量化在有些任务(尤其是需要从长文里大量提取信息的情境密集任务)上会明显掉点,一篇 2026 年的工作专门研究了这点,我们在后面会讲到。

第二条是驱逐:只保留”重要”的 KV,把不重要的丢掉。站内 H2O 完全拆解、SnapKV 完全拆解、StreamingLLM 完全拆解 都属于这条路线。驱逐的问题是不可逆:一旦丢掉,需要时就得重算,而重算的代价和丢弃量成正比。驱逐本质上是在赌”这些 KV 以后用不上”,赌错了就要付重算的账。

Offloading 是第三条路,它和前两条的根本区别在于不丢信息、不降精度,只换位置。KV 一个都不少,只是大部分时间待在便宜的存储里,用到时才搬回来。它把”容量”问题和”精度”问题解耦了:想要多大上下文就准备多大的 DRAM/SSD,精度完全不受影响。代价则转移到了”搬运”上——而搬运正是 PCIe 这条窄管道要付的过路费。

搬运到底有多贵

把 KV 放到 CPU DRAM,GPU 要读的时候得走 PCIe。PCIe 5.0 x16 的双向带宽约 128 GB/s,PCIe 4.0 只有一半左右约 64 GB/s。把它和 GPU 的 HBM 带宽比一比:一张 H100 的 HBM3 带宽是 3.35 TB/s,是 PCIe 5.0 的 26 倍。也就是说,从 DRAM 搬回来的数据,读取速度只有显存的 1/26。这个差距决定了卸载系统的设计逻辑:搬运必须尽量少、尽量异步、尽量只搬真正需要的部分。

一篇 2025 年底的工作(arXiv:2601.19910,Understanding Bottlenecks for Efficiently Serving LLM Inference With KV Offloading)把这个代价做了定量分析。它推导了一个关键比值 κcrit\kappa_{\text{crit}}——“已缓存的 token 数与需要重新预填充的 token 数之比”的临界值,超过它执行就从算力受限变成内存带宽受限。论文的结论很刺眼:典型工作负载超过这个临界值几个数量级,实测中 99% 的延迟花在了搬运上,而 GPU 在服务卸载请求时只消耗了额定 TDP 的 28%。翻译过来就是:GPU 大部分时间在干等数据,算力闲置了七成以上。

卸载场景下延迟几乎全部花在数据传输与等待上(图来自 arXiv:2601.19910 Figure kv_offload)
卸载场景下延迟几乎全部花在数据传输与等待上(图来自 arXiv:2601.19910 Figure kv_offload)

这个数字解释了为什么卸载看起来简单(“把数据搬来搬去”),做起来却极难:如果不能让搬运和计算充分重叠、不能让搬运量降到最低,那么卸载系统只是把显存瓶颈换成了带宽瓶颈,GPU 从”算不动”变成”闲着等”。整个 KV Cache 卸载方向的技术演进,本质上都是在回答”怎么让这 99% 的搬运时间不白白浪费”。

卸载技术的三条支柱

把上面所有问题收拢,KV Cache 卸载系统无论多复杂,都在围绕三件事做文章。理解了这三条支柱,后面看任何一个具体系统都能快速定位它在解决什么。

第一,分层放置(placement):决定哪些 KV 放在哪一级存储。GPU 显存是最快但最小的,CPU DRAM 容量大但要走 PCIe,SSD 容量更大但带宽低且延迟高,远端节点内存池容量近乎无限但要看网络。放置策略要回答的核心问题是”什么数据值得留在显存里”——是把最近生成的留在显存、把旧的全部推到下级,还是按注意力重要性动态决定?这直接决定了后面要搬多少。

第二,按需召回(recall):决定一次前向需要把哪些 KV 搬回来。如果每步解码都把所有层的全部历史 KV 搬一趟,带宽立刻爆掉。所以召回几乎总是”稀疏化”的:只搬那些当前查询真正会用到的 KV。这里的关键是预测准不准——预测多了浪费带宽,预测少了精度受损。Recall 的策略是整个领域创新最密集的地方。

第三,异步流水(overlap):决定搬运怎么和计算并行。PCIe 传输和 GPU 计算用的是不同硬件单元,理论上可以完全重叠:当 GPU 在算第 ll 层的注意力时,CPU-GPU 总线正把第 l+1l+1 层要用的 KV 搬过来。让搬运藏在计算背后、而不是让计算停下来等数据,是把”99% 搬运时间”压下去的唯一办法。CUDA 的异步拷贝(cudaMemcpyAsync)、多流(stream)、以及更激进的常驻 kernel 都是为这件事服务。

三条支柱在具体系统里的分工可以用一张图概括(图 2 来自 Mooncake 论文的 KV Cache 池示意,论文 arXiv:2407.00079,Moonshot AI 与清华等合作,2024 年发布,是 Kimi 的服务平台):GPU 集群里未被充分利用的 CPU、DRAM、SSD 被组织成一个分布式的 KV Cache 池,每个 KV 块带前缀哈希用于去重,按需在池与实例之间经 RDMA 搬运。

Mooncake 的分布式 KV Cache 池:每个 KV 块附带前缀哈希用于去重,池由集群内闲置的 CPU/DRAM/SSD 组成(图来自 arXiv:2407.00079 Figure 3)
Mooncake 的分布式 KV Cache 池:每个 KV 块附带前缀哈希用于去重,池由集群内闲置的 CPU/DRAM/SSD 组成(图来自 arXiv:2407.00079 Figure 3)

下面按这三条支柱展开,讲清楚每一层的机制、代表工作和设计取舍。

支柱一:分层放置与存储层级

三级存储的物理参数

要谈放置,先得知道每一级存储的硬参数。下表把 LLM 推理相关的存储层级列在一起,数字取当前主流服务器配置的量级。

层级典型容量带宽量级访问延迟量级相对显存成本
GPU HBM(显存)40–192 GB/卡2–8 TB/s~百 ns1×
CPU DRAM(主机内存)数百 GB–数 TBPCIe 5.0 双向约 128 GB/s~μs 级(含 PCIe 往返)约 1/10
NVMe SSD数 TB–数十 TB数 GB/s 顺序读数十–数百 μs约 1/100
远端内存池(RDMA)近乎无限视网络(100–400 Gb/s)数 μs–数十 μs视集群

注意带宽量级差:HBM 是 TB/s,PCIe 是百 GB/s,SSD 是 GB/s,三者各差一个数量级。这个”数量级阶梯”决定了放置的基本哲学:越靠近 GPU 越贵越小,数据要按”最近是否用到”从内向外分层;越往外层,召回一次越贵。

显存里留什么:从 LRU 到重要性感知

分层放置第一个决策是显存里留哪些 KV。最朴素的做法是 LRU(最近最少使用):最近生成的 token 最可能被后续 token 关注,所以留最近窗口。这正好对应滑窗注意力,站内 SWA 滑窗注意力完全拆解 讲过只保留最近窗口会掉点、需要 attention sink 兜底。

但 LRU 忽视了注意力的一个重要结构:早期的”重击者”(heavy hitter)token 会被反复关注,即使它们很旧。站内 H2O 完全拆解、StreamingLLM 完全拆解 讲的正是这个注意力幂律分布与 attention sink 现象。卸载系统把这套”重要性保留”思想用在了层级选择上:不是简单丢最旧的,而是把”最近窗口 + 少数高累计注意力分数的老 token”留在显存,其余下沉到 DRAM。

Mooncake 走的是另一条路:它不做重要性排序,而是做前缀哈希去重 + 缓存感知路由。同一个系统提示前缀被多个请求共享时,只需要一份 KV;调度器把请求导向”缓存命中最多的节点”,从而让显存里留下的恰好是即将被复用的那部分。这是把”放置”和”请求调度”耦合起来的设计,也是它相对纯 KV 管理方案的独特之处。

往下放什么:容量层的选择

显存之外的第二级通常是 CPU DRAM,因为 PCIe 是现成的高带宽通道。再往下,SSD 提供了近乎无限的容量但带宽骤降。站内 FlexGen 完全拆解 讲的正是用线性规划在 GPU、CPU、SSD 三级之间编排存取(arXiv:2303.06865,2023 年发表),它主要针对高吞吐、延迟不敏感的批处理场景:用 SSD 换容量,接受带宽损失。

到端侧设备,存储层级形态又不一样。KVSwap(arXiv:2511.11907,2025 年发表,面向移动/嵌入式)指出:端侧的 CPU 与 GPU/NPU 通常共享统一内存,没有独立的”主机内存”这一级,而闪存(磁盘)I/O 带宽又很有限。所以它直接把 KV Cache 卸到磁盘,用一个紧凑的内存元数据层记录”哪些 KV 项重要”,只在需要时从磁盘取回关键子集。

KVSwap 面向端侧:KV Cache 全量存盘,内存只留紧凑元数据,按需取回关键子集(图来自 arXiv:2511.11907 solver)
KVSwap 面向端侧:KV Cache 全量存盘,内存只留紧凑元数据,按需取回关键子集(图来自 arXiv:2511.11907 solver)

层级的选择不是越深越好,而是由设备形态决定:数据中心 GPU 服务器适合 DRAM 池 + SSD 后端;端侧适合磁盘 + 统一内存;多卡服务器则还有第三条 PCIe 链路可挖(后面会讲多路径)。

支柱二:按需召回——稀疏化与预测

为什么必须稀疏召回

一次解码步,每一层都要对该层的全部历史 KV 做注意力。如果这些 KV 都在 DRAM 里,就需要把该层全部历史 KV 搬回显存。假设某层历史有 SS 个 token、每 token 每层 KV 是 2nkvdheadb2 n_{kv} d_{head} b 字节,那么单层单步的搬回量就是这一个 token 的 KV 总和。以 320 KB/token、L=80L=80 层、S=128KS=128\text{K} 为例,把所有层搬一遍就是:

L×S×320 KB≈80×128000×320KB≈3.3 TBL \times S \times 320\ \text{KB} \approx 80 \times 128000 \times 320\text{KB} \approx 3.3\ \text{TB}

一步搬 3.3 TB,用 PCIe 5.0 的 128 GB/s 要 25 秒——完全不可行。结论很直接:不可能全量召回,必须只搬当前查询真正会用到的那一小部分。

稀疏召回的核心:预测哪些 KV 重要

于是问题的核心变成”怎么知道当前查询会关注哪些历史 KV”。理想的答案是直接算一遍注意力,但那需要 KV 在手上,形成循环依赖。所有卸载系统都在用某种廉价的近似来打破这个循环。图 3 来自 InfiniGen(arXiv:2406.19707,2024 年 OSDI),它的关键洞察是:后续层真正重要的 KV,可以用当前层的输入加上后续层的部分 query 权重与 key 缓存做一次极小的”排练”(rehearsal)来推测,从而只预取那部分关键 KV,而不必把所有 KV 都取回。

InfiniGen 的核心思想:用一次最小排练推测后续层所需的关键 KV,从而只预取关键条目(图来自 arXiv:2406.19707 framework_diagram)
InfiniGen 的核心思想:用一次最小排练推测后续层所需的关键 KV,从而只预取关键条目(图来自 arXiv:2406.19707 framework_diagram)

执行时,它把卸载的 KV 放在 CPU 内存,用推测结果指导预取,并让预取与后续层计算重叠。论文报告相对已有 KV 管理方法最高 3.00× 加速,同时精度最高提升 32.6 个百分点。

从 InfiniGen 开始,“预测式稀疏召回”成为卸载领域的主流范式,后续工作都在改进”预测得准不准”和”预测得贵不贵”。表 2 把几个代表性系统的召回策略并列出来。

系统召回依据关键机制主要场景
InfiniGen当前层输入 + 后续层部分 Q/K 排练推测关键 KV 后预取长文本生成,单机 DRAM
ScoutAttention层级提前的 CPU 预计算GPU-CPU 协作的分块稀疏注意力长上下文解码
PulseInfer页级稀疏选择 + 自适应准入可中断的分层调度 + gather-scatter I/OSGLang 上的服务化部署
NOSA原生可训练的稀疏模式显式约束 CPU-GPU 传输量需要训练-推理一致的场景

2025–2026 的新进展:只搬该搬的

近一年的工作把召回环节做得更细。这里挑三个方向讲,因为它们分别代表了召回优化的三种不同切入角度。

ScoutAttention(arXiv:2603.27138,2026 年 3 月) 切入点是”谁来算”。传统卸载要么让 GPU 等 I/O,要么把注意力计算整个丢给 CPU 但 CPU 算得太慢。它的做法是 GPU-CPU 协作的分块稀疏注意力:GPU 负责它擅长的那部分,CPU 用层级提前预计算来分担一部分注意力,让两侧都不成为瓶颈。它的动机可以从图 4 直接看出来——对比三种执行方式:全注意力在长上下文下计算量过大;InfiniGen 会因为 KV 召回慢而产生流水线气泡;而 CPU 侧计算慢的方案(HGCA)同样有气泡。ScoutAttention 想同时避开这两种气泡。

三种执行方式的对比:全注意力计算过重、InfiniGen 受召回慢影响产生气泡、CPU 侧计算慢同样产生气泡(图来自 arXiv:2603.27138 arch)
三种执行方式的对比:全注意力计算过重、InfiniGen 受召回慢影响产生气泡、CPU 侧计算慢同样产生气泡(图来自 arXiv:2603.27138 arch)

PulseInfer(arXiv:2609.34555,2026 年 9 月,实现于 SGLang) 切入点是”I/O 模式本身有多糟”。它发现已有卸载系统把瓶颈转移到了 CPU-GPU 召回 I/O 上,而且这个 I/O 有两个特征让事情更糟:一是召回量在不同层、不同解码步、不同请求之间剧烈波动,二是逐头稀疏选择会把召回打碎成很多小的 PCIe 传输。图 5 展示了召回负载在层间的分布——注意中位召回量的层间差异可达约 5×。针对这两点,PulseInfer 用三招应对:可中断的分层调度来隐藏波动的召回延迟、I/O 自适应的卸载准入来动态决定该步卸多少、以及 SoloHead 稀疏选择加 gather-scatter I/O 引擎来合并碎片化传输。论文报告相对 SGLang 解码吞吐最高 4.7×、相对最佳已有卸载基线最高 2.6×。

PulseInfer:召回负载在层间、步间、请求间差异极大,逐头选择还会碎片化传输(图来自 arXiv:2609.34555 kv_heads_heatmap)
PulseInfer:召回负载在层间、步间、请求间差异极大,逐头选择还会碎片化传输(图来自 arXiv:2609.34555 kv_heads_heatmap)

NOSA(arXiv:2510.13602,2025 年 10 月) 切入点是”训练-推理是否一致”。免训练的稀疏卸载(如 InfiniGen)由于稀疏模式与训练时的稠密注意力不匹配,长生成质量会掉;而可训练的稀疏注意力又不能直接用于卸载,因为不受约束的 KV 访问会强迫大量 CPU-GPU 传输、把吞吐收益吃掉。NOSA 的解法是设计一种原生为卸载优化的可训练稀疏注意力,显式约束 CPU-GPU KV 传输量,从而同时拿到低通信开销和高解码吞吐,并配套 KV 卸载推理系统 NOSI。图 6 给出 NOSA 的框架:训练阶段的可训练稀疏与推理阶段的卸载系统联合设计。

NOSA 框架:可训练稀疏注意力与 KV 卸载推理系统联合设计,显式约束 CPU-GPU 传输量(图来自 arXiv:2510.13602 framework)
NOSA 框架:可训练稀疏注意力与 KV 卸载推理系统联合设计,显式约束 CPU-GPU 传输量(图来自 arXiv:2510.13602 framework)

三个方向合起来看,召回优化的演进脉络是清晰的:从”预测哪些重要”(InfiniGen)到”预测 + 分工 + 合并 I/O”(ScoutAttention/PulseInfer)再到”让稀疏模式本身就可训练、可约束”(NOSA)。每一步都在把”搬多少”这个数字往下降。

支柱三:异步流水与搬运工程

让搬运藏在计算背后

即使搬得再少,只要搬运是同步的(计算停下等数据),延迟就无法隐藏。所以卸载系统的第三根支柱是把 CPU-GPU 传输变成异步、并发的。

GPU 侧的武器是 CUDA 的异步能力:cudaMemcpyAsync 让拷贝和 kernel 异步执行,多 stream 让不同层的拷贝与计算并行,拷贝引擎(copy engine)与计算单元是独立硬件,理论上可以完全重叠。更极端的做法是常驻 kernel:把调度逻辑搬进 GPU 内部的一个长驻 kernel,让它自己决定何时拷贝、何时计算,从而彻底去掉 host 侧逐 kernel 启动的开销。站内 CUDA Graph 完全拆解 讲的 CUDA Graph 与 FlashInfer 完全拆解 都在讨论如何把执行图和执行引擎做得更”贴硬件”。

搬运量:从全量到分块稀疏

异步只能隐藏延迟,不能减少总量。真正减少总量的是”分块稀疏召回”:把历史 KV 分成块,只召回当前查询需要的块。这在系统层面等价于稀疏注意力,但放在卸载上下文里,它多了一层约束——召回的块数量必须受控,因为每次召回都是一次 PCIe 事务。NOSA 显式约束传输量、PulseInfer 用 SoloHead 选择来合并小传输,都是在把”块级稀疏”翻译成”I/O 友好的稀疏”。

多路径搬运:把闲置链路用起来

当单机有多张 GPU 时,一个新的问题浮现:每张卡的 host-GPU 拷贝都被限制在它自己那条 PCIe 链路上,即使服务器里还有别的 PCIe 链路和 GPU 互联带宽。MMA(MultiPath Memory Access,arXiv:2512.16056,2025 年 12 月)把这个观察变成了系统:它让一次 host-GPU 拷贝分散到多条直连和接力路径上,用软件定义的方式,在不改硬件、驱动和应用的前提下打破单卡 PCIe 带宽瓶颈,并保持 CUDA stream 语义。对 KV 卸载和权重搬运这两条路径都有直接价值。

量化卸载:让搬运的数据更小

前面三条都在减少”搬的次数和路径”,还有一条正交的路线是减少每次搬的数据量——把卸载的 KV 做量化。InfiniGen 这类工作常与量化结合:站内 KIVI 完全拆解(ICML 2024)的 2-bit 非对称量化把 KV 压到八分之一,站内 KVQuant 完全拆解(NeurIPS 2024)的 3-bit 方案同样如此。量化后的 KV 搬到 GPU 再解量化,PCIe 上的字节数直接减少,等于把带宽”变相”变宽。代价是解量化算子和精度损失,但和卸载的收益相比通常是划算的。

系统级协同:卸载如何进入推理框架

卸载与分页、调度的耦合

卸载不是独立模块,它必须和推理框架的 KV 内存管理、请求调度咬合。站内 PagedAttention 完全拆解 讲的分块 KV 管理是基础:只有当 KV 以固定大小的块存在、块之间可独立搬运时,卸载才能按块进行。站内 vAttention 完全拆解 讲的 CUDA 虚拟内存保住连续布局,则让”按需映射物理页”成为可能——物理页可以在 DRAM 和显存之间重映射,而注意力 kernel 看到的仍是连续虚拟地址。这两种内存管理方式给了卸载不同的抓手。

调度层面,卸载引入了新的权衡:缓存感知的路由(把请求导向 KV 命中最多的节点,Mooncake、RadixAttention 都这么做)、过载时的提前拒绝(Mooncake 的 prediction-based early rejection)、以及按 SLO 决定卸载深度。站内 DistServe 完全拆解 讲的 P/D 分离与 goodput 调度、连续批处理完全拆解 讲的迭代级调度,都是卸载系统必须兼容的调度框架。

与 PD 分离的合流

现代推理服务普遍走向 P/D 分离(Prefill 与 Decode 拆成两组实例)。卸载在 P/D 分离里获得了天然位置:Prefill 算出的 KV 本来就要跨实例传到 Decode 节点,这条传输通道顺理成章地承担了”KV 从存储池取回”的职责。站内 Sarathi-Serve 完全拆解 的分块 Prefill、SGLang 源码完全拆解(五):KV 内存池与 RadixCache 的所有权闭环 讲的三级缓存与写回,都是这条线路上的节点。

真实系统中的表现与代价

收益:容量与吞吐

把卸载的价值量化,最干净的表述是上下文长度上限的提升。MOM(arXiv:2504.12526,2025 年 4 月)给出了一个具体例子:通过把关键层拆成更小的”迷你序列”并与 KV 卸载无缝集成,它在 Meta-Llama-3.2-8B 上把单张 A100 80GB 的最大上下文从 155K 扩展到 455K token,且输出完全一致、不掉精度,同时相比传统分块预填充多扩展了 35% 的上下文。图 7 展示了 MOM 的卸载路径:把层拆成迷你序列,配合 KV 卸载,把峰值显存压下来。

MOM:把关键层拆成迷你序列并与 KV 卸载集成,单卡上下文从 155K 扩到 455K(图来自 arXiv:2504.12526 Offload)
MOM:把关键层拆成迷你序列并与 KV 卸载集成,单卡上下文从 155K 扩到 455K(图来自 arXiv:2504.12526 Offload)

吞吐侧的数字前面已经给过:InfiniGen 最高 3.00×、PulseInfer 相对 SGLang 最高 4.7×、Mooncake 在模拟长上下文场景吞吐最高提升 525%(这是论文报告的数字,需注意是模拟场景)。

代价:延迟、复杂度与场景依赖

收益之外,卸载的代价必须讲清楚。

第一,延迟上升。 卸载的本质是”用带宽换容量”,PCIe 往返延迟(μs 级)叠加在一次解码步上,会让 TPOT 变长。所以卸载几乎总在长上下文、延迟相对不敏感的场景里才划算;对延迟极敏感的短请求,卸载反而有害。图 8 来自 arXiv:2601.19910,展示归一化 TTFT 随缓存/prefill token 比 ktk_t 的变化——可以清楚看到在什么比值之后性能急剧恶化。

归一化 TTFT 随缓存-to-prefill token 比的变化:超过某个阈值后性能急剧恶化(图来自 arXiv:2601.19910 avg_normalized_ttft_vs_kt)
归一化 TTFT 随缓存-to-prefill token 比的变化:超过某个阈值后性能急剧恶化(图来自 arXiv:2601.19910 avg_normalized_ttft_vs_kt)

第二,情境密集任务上会掉点。 一篇 2026 年的工作(arXiv:2604.08426,KV Cache Offloading for Context-Intensive Tasks)专门研究了这个问题:已有卸载评估大多集中在”不需要从上下文大量提取信息”的任务上,而当任务需要从输入里查找大量信息(如把原始文本抽取成结构化知识),现代 KV 卸载在 Llama 3 和 Qwen 3 上都出现显著性能退化。它为此构建并开源了 Text2JSON 基准。这说明卸载不是”无损”的银弹——召回不完整时,需要大量查找的任务会明显受损。

第三,工程复杂度高。 卸载系统要在存储、传输、调度、kernel 之间做联合优化,还要处理预测失准、缓存命中率抖动、节点故障等现实问题。这也是为什么卸载功能往往以”KV Connector”这类插件形式进入框架,而不是默认开启。

工程落地的取舍清单

把前面的机制收成一个可操作的判断框架。当你要决定”要不要卸载、怎么卸载”时,依次问这几个问题:

  1. 吞吐还是延迟优先? 延迟敏感(在线对话)慎用卸载,吞吐优先(批处理、长文本、RL rollout)是卸载的主场。
  2. 任务是提取密集还是生成密集? 提取密集(大量查找)要警惕掉点,需要更保守的稀疏比甚至全量召回。
  3. 存储层级怎么选? 数据中心用 DRAM 池 + SSD 后端;端侧用磁盘 + 统一内存;多卡挖掘多路径。
  4. 召回策略用哪种? 免训练稀疏(InfiniGen 路线)上手快但有训练-推理不匹配风险;可训练稀疏(NOSA 路线)需要训练配合但更稳。
  5. 传输怎么排? 一定要异步 + 多流 + 分块稀疏 + 传输合并,否则异步省下的延迟会被碎片化 I/O 吃掉。
  6. 要不要叠量化? 搬运前先量化能把 PCIe 字节数直接砍掉数倍,通常值得。

小结

KV Cache 卸载要解决的是一面容量墙:解码状态随上下文线性增长,显存永远不够,而压缩和驱逐各有天花板。卸载的思路是把 KV 放到便宜的存储层级、按需召回,代价转移到 PCIe 搬运上。整个领域围绕三条支柱展开——分层放置决定数据放哪,按需召回决定搬多少,异步流水决定搬运能否藏在计算背后。从 2023 年的 FlexGen、2024 年的 InfiniGen/Mooncake,到 2025–2026 年的 NOSA、ScoutAttention、PulseInfer、MMA,演进主线是把”搬多少、搬得准不准、搬得快不快”这三件事持续优化;同时,量化卸载和 P/D 分离的合流让卸载获得了两条正交的助力。代价也很清楚:延迟上升、提取密集任务可能掉点、工程复杂度高。卸载不是让显存变大的魔法,而是在存储层级之间做一场以带宽换容量的工程交易——判断它值不值,就看你愿意用什么换什么。

参考资料

  1. FlexGen: High-Throughput Generative Inference of Large Language Models with a Single GPU
  2. InfiniGen: Efficient Generative Inference of Large Language Models with Dynamic KV Cache Management
  3. Mooncake: A KVCache-centric Disaggregated Architecture for LLM Serving
  4. MOM: Memory-Efficient Offloaded Mini-Sequence Inference for Long Context Language Models
  5. NOSA: Native and Offloadable Sparse Attention
  6. KVSwap: Disk-aware KV Cache Offloading for Long-Context On-device Inference
  7. Understanding Bottlenecks for Efficiently Serving LLM Inference With KV Offloading
  8. MultiPath Memory Access: Breaking Host-GPU Bandwidth Bottlenecks in LLM Services
  9. ScoutAttention: Efficient KV Cache Offloading via Layer-Ahead CPU Pre-computation for LLM Inference
  10. KV Cache Offloading for Context-Intensive Tasks
  11. PulseInfer: I/O-Centric Sparse KV Cache Offloading for Efficient Long-Context LLM Decoding
  12. Mooncake 开源项目(kvcache-ai/Mooncake)

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

KV Cache Offloading 完全拆解:把显存装不下的历史塞进 CPU、SSD 与远端内存
https://pinghaoyang.com.cn/articles/insights/posts/kv-cache-offloading/
作者
平昊阳
发布于
2026-10-18
许可协议
CC BY-NC-SA 4.0

评论区

访客信息
--°天气加载中

正在获取所在地区…

💨 风力--
💧 湿度--

正在读取天气

Profile Image of the Author
平昊阳
乘长风,破巨浪, 展鸿图于未央!
公告
欢迎来到我的个人博客!欢迎关注交流吖!
更多相关公告,见
「社交-留言」。
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
站点统计
文章
448
分类
29
标签
451
总字数
6,178,652
运行时长
0 天
最后活动
0 天前

文章目录