~/yomxxx/posts/2026-09-19-kv-cache-compression-limits-long-form.mdx
Long-form

$ cat 深度长文:890 字节一个 token——KV 缓存压缩到底能压到什么程度

-- 6 min read ·

推理成本的战场在最近两年发生了转移。过去大家盯着解码吞吐:怎么让每个输出的 token 更快、更便宜。但当 Agent 成为主要工作负载形态之后,重心挪到了输入侧。

原因很直白。一个长程 Agent 每一轮工具调用,都要把此前累积的全部上下文重新送进模型——对话历史、工具返回、文件内容,动辄几十万 token。而这一轮真正生成的输出可能只有几十个 token。于是输入侧算力消耗主导了总成本,而同一段上下文被反复处理几十次。

DeepSeek 新发布的 V4.1-Flash 技术报告,把这个问题的解法写得很清楚:不是单点优化,而是在三个层面同时推进。论文的开篇就把话说透了——prefill 计算昂贵、大 KV 缓存压迫 HBM 与 SSD 容量、数据传输带宽吃紧,这三者共同构成进一步降低部署成本的主要瓶颈。

下面逐个拆解。

一、先把数字摆在前面

指标DeepSeek-V4.1-Flash
骨干参数量552B(多模态 MoE)
支持上下文最高 1M token
decode 每 token 激活16B
prefill 每 token 激活8B
全局 KV 缓存占用890 字节 / token(常驻 HBM)
相对上一代全局占用约四分之一
持久化 KV 占用约上一代的八分之一
预训练语料45T token(多模态)

模型权重已在 Hugging Face 上放出。先记住 890 字节这个数字——它意味着支撑 100 万 token 上下文的常驻 KV 缓存约为 890 MB。这个量级让单卡长上下文推理从「理论上可行」变成「工程上可规划」。

二、第一层:让每个 token 的 KV 更小

压缩 KV 的第一条路径是减少单 token 的表示成本,V4.1-Flash 在这里用了两个手段。

跨层 KV 复用被放进了压缩稀疏注意力 2(CSA2)里。核心洞察是不同层的 KV 表示存在大量冗余,与其让每一层独立维护完整的键值张量,不如让若干层共享压缩后的表示。跨层复用是纯粹的结构性收益——它不损失数值精度,因为被消除的本来就是冗余。

在此基础上叠加 FP4 KV 缓存,把每个数值的比特宽度压到 4 位。

低比特存 KV 听起来危险,但 D-Quant 那篇论文给出了一个关键观察:经过旋转和归一化之后,KV 值近似服从正态分布,绝大多数值集中在中心区域,只有极少数落在尾部。这种分布对量化相当友好——离群值少,动态范围可控。相比权重矩阵里常见的长尾分布,KV 值的压缩空间反而更大。

两个手段叠加的效果是全局 KV 缓存降到 890 字节每 token。

三、第二层:让 KV 不必一直待在 HBM 里

单 token 成本压下来之后,下一个瓶颈换成了总容量:1M 上下文乘以 890 字节仍然是一笔不小的常驻开销。V4.1-Flash 的应对是把 KV 缓存按访问频率分成两层。

  • 全局 KV 缓存:始终驻留 HBM,成本 890 字节每 token;
  • 持久 KV 缓存:放在 SSD 或主机内存里,需要时取回。

关键的工程机制叫 SWA 有界重放(SWA Bounded Replay)。它利用滑动窗口注意力的结构特性:超出窗口的历史条目不会被当前解码步直接访问,因此没有必要常驻显存。当某个历史跨度重新进入需要时,按有界的方式重放取回。

论文给出的结果是持久化占用降到上一代的约八分之一。这不是靠更激进的量化,而是靠重新划分冷热数据的位置——把访问局部性之外的部分搬到更便宜的存储层级。

💡 提示:这一类优化的前提是访问模式有局部性。Agent 场景恰好满足:当前几轮的上下文被高频访问,几十轮之前的内容只在少数回溯场景下才会用到。如果你的负载是随机问答且每次都要遍历全部历史,分层缓存就失效了。

四、第三层:让 prefill 本身更便宜

前两层压缩的是存储,第三层压缩的是计算。

V4.1-Flash 引入了因果编解码器架构(Causal Encoder-Decoder,CED)。它的特点是 decode 与 prefill 的激活量不对称:

decode  : 每 token 激活 16B 参数
prefill : 每 token 激活  8B 参数

也就是说,处理输入时只激活骨干的一半。这个设计针对的正是 Agent 工作负载的成本结构:输入量远大于输出量,把权重压缩的重心放在输入侧,边际收益最大。

把三层合起来看,逻辑是自洽的:CED 降低单位输入的计算成本,CSA2 加 FP4 降低单位 token 的存储成本,SWA 有界重放降低常驻存储的总量。三者作用在不同的成本项上,互不冲突。

五、另一条路线:熵编码为什么难进 kernel

固定位宽量化有个信息论上的硬伤。一个 b 位的表示最多只能表达 2^b 个量化级,位宽每降一档,可用级数就指数级减少——4 位只有 16 个级,2 位只有 4 个。在这种约束下,位宽越低压损越剧烈。

但固定位宽的浪费也很明显:它对高频符号和稀有符号分配同样多的比特。既然 KV 值近似正态分布,中心区域的值出现频率远高于尾部,理论上应该用更短的码字表示高频值、更长的码字表示稀有值。这正是熵编码的强项,它能逼近信息论下限,大幅降低平均位数。

问题是熵编码输出变长。而现代 attention kernel 依赖规则的内存布局和固定步长的并行访问:算子需要根据 token 索引直接算出地址偏移,反量化要在 kernel 内部并行完成。变长编码让每个 token 的边界位置都不同,地址计算失去统一公式,反量化只能串行解包,并行度被摧毁。

D-Quant 的解法是引入一个 drift 机制:把每个 token 的变长熵编码重新对齐成固定大小的比特流。这样既保留了熵编码的压缩效率,又恢复了规则的内存访问模式,反量化可以在 kernel 内并行执行。

对比两种路线可以看出设计哲学差异:

维度跨层复用加低比特量化熵编码加定长对齐
压缩原理消除结构冗余,降低比特宽度逼近信息论下限
主要风险精度损失累积工程复杂度、kernel 重写
落地难度较低,可复用现有算子高,需要新 kernel
与硬件的关系依赖低精度算力支持依赖存储格式重构

两条路线并不互斥。V4.1-Flash 选择前者,是因为它要交付的是一个可以立刻部署的服务模型;D-Quant 探索的是压缩率的上界在哪里。

六、这些数字对你的部署意味着什么

把结论落到可操作层面,有三个判断。

第一,先算清楚你的输入输出比。 如果工作负载以短输入长输出为主(比如内容生成),prefill 优化带来的收益有限,成本重心仍在 decode。只有当输入远大于输出(Agent、检索增强、长文档分析)时,CED 这类设计才体现出结构性优势。

第二,KV 缓存的成本要按「常驻」和「持久」分开算。 890 字节每 token 是常驻成本,持久层的八分之一是另一个数字。选型时如果只对比总占用,会错过分层设计真正的价值——它改变的是需要多少 HBM 才能跑起来,而不是总存储量。

第三,熵编码类方案短期内别指望开箱可用。 它的收益依赖 kernel 层面的重写,属于基础设施级投入。对绝大多数团队来说,先在应用层使用跨层复用与低比特缓存这类现成能力,比自研压缩格式划算得多。

⚠️ 注意:论文提出的压缩比例都建立在特定硬件与部署配置上,实际收益会随 batch 规模、并发数、序列长度分布变化。上线前务必在自己的流量分布下做一次压测,不要直接套用论文中的倍数关系。

Frequently asked questions

为什么长程 Agent 会让 prefill 变成主要成本?
因为 Agent 的每一轮工具调用都要把此前的全部对话与工具返回重新送进模型。解码阶段的输出只有几十个 token,但输入可能是几十万 token,于是算力消耗被输入侧主导。传统上大家关注 decode 的吞吐优化,但 Agent 工作负载把重心移到了 prefill:同一段上下文会被反复处理几十次,每次都要重算注意力。这就是论文说负载变得 input-heavy 的原因。
跨层 KV 复用具体复用了什么?
复用相邻层之间高度相似的 KV 表示。Transformer 不同层的注意力模式存在强相关性,低层和高层算出的键值向量在很多头上有大量冗余。跨层共享相当于让若干层共用一个压缩表示,而不是每层独立存一份完整的 KV。这直接减少了需要驻留在显存里的张量总量,是 890 字节每 token 这个数字的主要贡献来源之一。
FP4 存 KV 不会导致精度崩掉吗?
关键在于压缩的对象是缓存而不是权重,而且经过旋转和归一化之后,KV 值的分布相当集中。D-Quant 论文观察到的现象可以解释这一点:旋转归一化后 KV 值近似服从正态分布,绝大多数值落在中心区域,只有极少数落在尾部。这种分布对低比特量化其实比权重更友好,因为极端离群值少。配合跨层复用带来的冗余消除,FP4 是可以承受的。
SWA 有界重放是怎么做到持久化降到八分之一的?
它把 KV 缓存分成常驻和持久两层。全局 KV 始终放在 HBM 里,这部分被压到 890 字节每 token;而超出滑动窗口范围的历史 KV 不需要一直占着显存,可以落到 SSD 或主机内存里,需要时按有界重放的方式取回。因为 Agent 场景的访问模式有局部性,远离当前位置的历史条目命中率低,把它们从 HBM 挪走几乎不影响延迟,却大幅降低了常驻占用。
熵编码压缩率更好,为什么反而难落地?
因为熵编码输出的是变长比特流,而现代 attention kernel 依赖规则内存布局和固定步长的并行访问。变长意味着每个 token 的边界位置都不同,无法用统一的偏移量计算地址,反量化也只能串行解包,直接摧毁了 kernel 的并行度。D-Quant 的解法是引入 drift 机制,把每个 token 的变长编码重新对齐成固定大小的比特流,等于在压缩率和可并行性之间找一个可工程化的交点。
// next.txt ›

Some outbound links in this post are affiliate links — see disclosure.