KV-cache 量化为什么一直"不敢开"

做过 vLLM 生产部署的人大概都纠结过一个问题:context 越来越长,KV-cache 吃掉的 GPU 显存越来越多,但官方文档里那个 --kv-cache-dtype 参数,你真的敢在生产环境打开吗?

vLLM 团队今年 5 月发了一篇 TurboQuant 的系统性基准测试1,结论相当诚实:除了 FP8 之外,所有 KV-cache 量化方案都在"拿吞吐量换容量"。具体来说:

  • TurboQuant 4bit-nc:最高 3.4 倍 KV-cache 容量,但吞吐量损失 40-52%,延迟增加最高 60%
  • TurboQuant 3bit-nc:精度大幅下降,推理和长上下文任务表现尤其差
  • FP8:2 倍容量,吞吐量基本无损,但容量增幅有限

说白了就是:你要么选 FP8(安全但只翻倍),要么选更激进的量化(容量大但吞吐暴跌)。在推理密集型场景——比如长上下文 Agent、代码生成、数学推理——吞吐量下降意味着请求排队变长、用户等待变久,这在生产环境是不可接受的。

所以 vLLM 官方的建议一直是:默认用 BF16,显存不够就用 FP8,其他量化方案慎用。

这就是 KVarN 想解决的问题。

KVarN 是什么:华为的 KV-cache 量化新方案

KVarN(读作 /kvɑːɳ/,瑞典语"研磨机"的意思)来自华为通讯系统实验室(Huawei CSL),论文在 arXiv 上2,代码以 Apache 2.0 开源在 GitHub3,目前 196 星,最新提交就在今天。

它的核心卖点用一句话概括:FP16 级精度、FP16 级吞吐、3-5 倍 KV-cache 容量,不需要校准数据。

在 Qwen3-32B 的 AIME25 基准测试中(16K context,TP=2),KVarN 的精度和吞吐量都匹配甚至超过 FP16,同时提供约 4 倍的 KV-cache 容量。这在之前的量化方案中从未同时实现过。

它怎么做到的:四步流水线

KVarN 的量化流程分四个阶段,每一步都有明确的工程目的:

第一步:Cache — 原始 FP16 KV-cache 瓦片(channels × tokens),直接来自注意力计算。

第二步:Hadamard 旋转 — 沿通道维度做 Hadamard 变换。这一步的直觉是:原始 KV-cache 中某些通道有极端值(outlier),直接量化会丢失大量信息。Hadamard 旋转是正交变换,不改变注意力分数,但会把极端值"摊开"到所有通道,让量化更容易。

第三步:方差归一化 — 交替沿行和列做标准差归一化(类似 Sinkhorn 迭代),在对数空间中操作。这一步让瓦片内部的方差均匀分布,进一步减少量化误差。

第四步:非对称舍入量化 — 在低比特宽度下做 round-to-nearest,读取时还原 scale。关键设计是给 key 分配更多比特,给 value 分配更少(默认配置 kvarn_k4v2_g128:key 4-bit,value 2-bit)。

用人话说就是:先把数据"打散"(旋转),再"抹平"(归一化),最后才"压缩"(量化)。传统量化方法直接在原始数据上压,极端值会让误差爆炸;KVarN 的前两步就是在为量化创造更好的条件。

论文的核心发现:推理场景的误差累积

论文2指出了一个之前被忽视的问题:现有的 KV-cache 量化方法主要在 prefill 类场景中评测,但推理(autoregressive decoding)场景下,量化误差会跨时间步累积。

具体来说,在长序列生成过程中,每一步解码都会读取之前所有 token 的 KV-cache。如果某个 token 的 scale 估错了,这个误差会随着解码的推进不断放大。论文发现,驱动误差累积的主要因素是不正确的 token scale

KVarN 的方差归一化正是针对这个问题:通过对 KV 矩阵的行和列做联合归一化,直接消除不均匀的 token scale,从而大幅减少误差累积。

在 MATH500、AIME24 和 HumanEval 等生成式基准上,KVarN 在 2-bit 精度下达到了新的 state-of-the-art。

与 TurboQuant 和 FP8 的实测对比

根据 KVarN 的 README3 和 vLLM TurboQuant 博客1的数据:

方案容量倍数吞吐量(相对 FP16)精度校准需求
FP8≈100%接近 FP16
TurboQuant 4bit-nc2.3-3.7×48-60%降 1-4 分
TurboQuant 3bit-nc3-5×更低明显下降
KVarN k4v2_g1283-5×≥100%匹配 FP16

KVarN 的 Pareto 前沿占据了 TurboQuant 和 FP8 都够不到的位置:右上角——既比 FP8 容量大,又比 TurboQuant 吞吐高,精度还不打折。

vLLM 官方 TurboQuant 博客的结论是"FP8 是目前最安全的默认选择"。但如果 KVarN 的数据经得起更广泛验证,这个结论可能需要更新。

工程实践:一行配置启用

KVarN 的部署极其简单——它是 vLLM v0.22.0 的 fork,安装方式和 vLLM 一样:

git clone https://github.com/huawei-csl/KVarN.git
cd KVarN
VLLM_USE_PRECOMPILED=1 pip install -e .

使用时只需要加一个参数:

from vllm import LLM

llm = LLM(
    model="Qwen/Qwen3-32B",
    dtype="float16",
    kv_cache_dtype="kvarn_k4v2_g128",  # ← 就这一行
    block_size=128,
)

Serving 模式同理:

vllm serve Qwen/Qwen3-32B --dtype float16 --kv-cache-dtype kvarn_k4v2_g128 --block-size 128

没有模型改动,没有校准数据集,没有额外配置。KVarN 的 kernel 是 Triton 实现的,运行时 JIT 编译。

有一个需要注意的点:在单卡显存紧张的情况下,vLLM 的 CUDA graph 内存分析器可能会过度预留显存,导致 KV-cache 池缩小。可以通过设置 VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS=0 或提高 --gpu-memory-utilization 来恢复完整容量。

需要关注的几个问题

1. 为什么是 fork 而不是 PR?

HN 评论区4第一条就问了这个问题。KVarN 选择 fork vLLM 而不是提交 PR 到上游,可能是因为它的 kernel 实现需要修改 vLLM 的注意力后端接口。短期内 fork 意味着你需要自己同步 vLLM 的更新,长期能否合并回上游还有待观察。

2. 196 星,能用在生产吗?

项目很新(论文和代码都是 2026 年 6 月),社区验证还在早期阶段。如果你的场景对精度极其敏感(比如金融计算),建议先在测试环境跑自己的基准。但如果你只是想在有限显存下跑更长的 context——比如 32K 甚至 128K 的 Agent 场景——KVarN 的风险收益比相当不错。

3. 目前只支持 128 的 block size

README 说其他 page size “coming soon”。在那之前,你需要确保 block_size=128 与你的工作负载兼容。

4. 对硬件的要求

KVarN 的 kernel 基于 Triton,理论上支持所有能跑 vLLM 的 GPU。但实际性能可能因硬件而异,A100/H100 上的表现和消费级卡上可能不同。

我的判断

KV-cache 量化是 LLM 推理优化中被低估的一个方向。大家的注意力大多在模型量化(GPTQ、AWQ)、投机解码、架构创新上,但对长上下文场景来说,KV-cache 才是真正的显存瓶颈。

KVarN 的工程价值在于它打破了"容量 vs 吞吐"的二选一困境,而且实现方式极其轻量——不需要校准数据,不需要改模型,一行配置搞定。如果后续社区验证它在更多模型和场景上的一致性,它很可能成为 vLLM 长上下文部署的标配。

对于正在用 vLLM 做生产部署的团队,我的建议是:

  1. 短上下文、显存充裕:继续用 BF16,不用折腾
  2. 中等上下文、显存紧张:FP8 是安全选择,2 倍容量,无损吞吐
  3. 长上下文 Agent / 推理密集型:关注 KVarN,它可能给你 4 倍容量的同时不牺牲吞吐
  4. 想尝鲜:在测试环境部署 KVarN,跑你自己的业务 benchmark,对比 FP16 和 FP8