01 · 权重固定,KV 却随上下文和请求数增长
历史 KV 复用免去了重复计算,却需要为每个有效位置保留各层的 K/V。提示词越长、输出越长、并发请求越多,缓存压力通常越大。即使权重已经量化,KV 也可能仍是 BF16;给权重换格式不会自动改变缓存。
量化缓存的目标是把每个历史元素的表示缩小。缓存受限时,这可能让同一设备容纳更多有效位置或请求;读取带宽受限时,更小的表示也可能减少流量,但还需要算上还原和内核适配。
| 假定普通 K/V 结构 | 单请求有效数据容量 |
|---|---|
| 32层、8个KV头、头维128、4096位置、16位 | 2×32×8×128×4096×2 = 512 MiB |
| 相同元素改为8位 | 256 MiB,不含尺度等元数据 |
| 相同元素理想打包为4位 | 128 MiB,不含尺度等元数据 |
02 · 沿数据路径找量化发生的位置
- 产生:当前输入经过每层计算,得到本层新的 K 和 V,按实现约定完成位置编码等处理。
- 写入:确定尺度,把新 K/V 变成低比特表示;把整数/低位浮点码与必要的尺度、边界信息一起保存。
- 读取:新 Q 读取历史的压缩 K/V。Attention 后端把其还原成合适的计算输入,或直接走支持的低精度路径。
- 追加与管理:继续维护有效长度、分页地址、共享前缀和尺度生命周期,不能只知道数据 dtype。
# 每层每次处理新增位置,概念性顺序
q, k_new, v_new = layer_project(current_hidden)
q, k_new = apply_position_encoding_if_required(q, k_new, positions)
k_codes, k_meta = encode(k_new, cache_recipe)
v_codes, v_meta = encode(v_new, cache_recipe)
cache.append(k_codes, v_codes, k_meta, v_meta)
output = attention_backend(q, cache, correct_causal_visibility)scale 的生命周期尤其重要。如果旧整数码按 s=0.1 编码,却仅因为新值变大就把整段缓存的全局 scale 改为0.2,旧元素会被错误地放大两倍。必须保持旧块的尺度,或按正确转换规则重新编码旧内容;动态缩放不等于随便覆盖一个标量。
03 · K 的误差和 V 的误差,影响位置不同
Attention 中,K 先参与 QKᵀ 产生分数,再经过 Softmax 决定各历史位置的权重;V 则在这些权重确定后被汇总。所以 K 的近似可能改变“看谁”,V 的近似可能改变“读到什么”。二者不一定使用相同粒度或尺度。
一个单查询、标量 V 的教学例子:
Q = 1,K=[0.14,0.26],V=[1,3],不额外缩放
原分数=[0.14,0.26],Softmax≈[0.47004,0.52996]
原输出≈2.05993
若 K 用整数格点 s=0.2 量化:K_hat=[0.2,0.2]
两项分数相同,权重变为[0.5,0.5]
保持 V 不变,输出=2.0如果固定权重 [0.5,0.5],V=[1.0,2.8] 用 s=0.5 就近舍入后成为 [1.0,3.0],输出则从1.9变为2.0。这是另一个只看 V 的例子。真实模型同时含多维 K/V、位置编码和后续层,不能把单步误差直接当作最终任务质量损失。
04 · 格式、粒度与近期缓存,是三个不同选择
| 选择 | 要回答的问题 | 可能的代价 |
|---|---|---|
| 整数 / 浮点位数 | 保存 INT8、FP8、低位整数或其他编码? | 误差与硬件支持不同,8位不是一个统一格式。 |
| 量化粒度 | 每层、每头、每 token、每通道还是每块共享尺度? | 粒度细可改善表示,但增加元数据和计算。 |
| 近期高精度区 | 是否暂时保留最近一段,再成组压缩旧内容? | 避免每步碎片转换,却增加常驻高精度容量和管理。 |
| 敏感层例外 | 哪些层或头继续保存原精度? | 改善质量,但降低总体压缩比例。 |
KIVI 的论文根据其观测选择 K 的 per-channel 与 V 的 per-token 量化,并提出低位 KV 方案。其具体分组和近期高精度缓存设计是一种方法路径,不能推广为所有模型必须如此,也不能把2位研究方案与 FP8 引擎选项当作同一实现。
依据:KIVI · A Tuning-Free Asymmetric 2bit Quantization for KV Cache ↗
研究级更低位方案常需要专用编码、残差或离群值处理和专用内核。方法在论文中成立,不代表当前使用的通用推理后端已经支持该格式。
05 · 加上元数据后,还剩多少容量收益
回到512 MiB 的16位基线,共有268,435,456个 K/V 标量。假设只对低位区域每128个标量存一个 FP16 scale,没有零点或其他元数据。8位码256 MiB加尺度4 MiB,共260 MiB;4位码128 MiB加尺度4 MiB,共132 MiB。
把元数据也算上:有效 KV 到底占多少
32层、8个KV头、头维128、4096位置、单请求。低位区每128个标量存一个2字节尺度;无零点、近期高精度区、对齐、块表与工作区。不模拟具体 FP8 或 INT4 内核。这是完整缓存统一格式的简化估算。如果最近128个位置仍保留16位,其他3968个位置用4位:近期数据16 MiB,旧数据124 MiB,码的总量已是140 MiB,再加旧区尺度等开销。不能继续拿128 MiB作为总量。
也不能把全缓存容量当作每步实际 HBM 读取量:Attention 后端的分块、共享、窗口与并行切分会影响流量。容量公式适合预算,带宽与延迟需要 profiling。
06 · 缓存小了,Attention 就一定快吗
| 读取路径 | 怎样执行 | 检查什么 |
|---|---|---|
| 先还原完整历史到 HBM | 低位缓存 → 完整高精度临时表 → 普通 Attention | 展开后的工作区和写回可能抵消部分收益。 |
| 分块读取并在内核内还原 | 读压缩块与尺度 → 局部解码 → 计算 | 需要融合读取/解码的后端,解码仍消耗资源。 |
| 原生或专用低精度 Attention | 对 Q/K/V 按支持配方执行低精度运算 | 不只是缓存格式;查询量化与数值配方可能一起变化。 |
vLLM 的 FP8 KV 文档说明,一些后端除了存储低位 K/V,还会让 Attention 使用低精度输入,包括量化 Q。实际走哪种路径需按后端、模型、头维与版本确认;不能把“FP8 KV”一律解释为“只存得小,计算完全不变”。
依据:vLLM · Quantized KV Cache(latest) ↗
可能的收益包括:减少缓存占用、降低某些路径的读取量、允许更大并发。可能的代价包括:确定尺度、写入转换、解码、元数据读取与工作区。相同并发和更大并发应分别测量,才能区分单请求变快与容量释放带来的整体吞吐提升。
07 · 与其他 KV 优化怎样区分、怎样组合
| 方法 | 主要改变 | 是否减少保留的位置 |
|---|---|---|
| 历史 KV 复用 | 保存历史结果,避免重算 | 通常保留仍需使用的历史。 |
| 分页 KV | 分配、逻辑到物理地址映射 | 本身不删除有效历史。 |
| 前缀缓存 | 跨请求复用兼容起始部分 | 共享相同结果,不是裁剪输入。 |
| KV 量化 | 每个元素的近似表示 | 通常保留位置,但数值近似。 |
| 卸载 / 分层缓存 | 把缓存放到其他存储并按需取回 | 未必删除,但增加传输与等待。 |
| 淘汰、稀疏选择、滑动窗口 | 选择仍允许访问的历史范围 | 可能减少可见位置;需区分模型原生语义与额外近似策略。 |
总览里的“量化与压缩”是一个方法族入口。这篇先详解降低元素位数;位置裁剪、稀疏选择与跨层结构压缩各有自己的数学条件,不能由量化公式证明正确。M017 等关联入口进一步区分这些方向。
组合前缀共享时,还需让双方认同尺度和缓存格式;不能只有 token 前缀相同,就把不同编码缓存直接解释为当前格式。分页则需要让低位数据和元数据一起被正确寻址。
08 · 先验证一个 Attention,再接服务后端
下方 CPU 例子比较原始 Attention、只量化 K、只量化 V、两者都量化的输出;同时用8位码验证“改了旧数据的 scale 却不重新编码”会改变还原值。它实现的是均匀对称整数教学方案,不模拟 FP8、KIVI 或实际 Attention 内核。
reference = run_fixed_tokens(cache_dtype=original_dtype)
recipe = choose(cache_format, k_granularity, v_granularity, scales, exclusions)
compressed = run_same_tokens_with_compatible_backend(recipe)
compare_logits(reference, compressed, tolerance_and_task_requirements)
measure(valid_cache_bytes, metadata_bytes, peak_memory, decode_time)
# 再固定并发测延迟,以及固定显存预算测可服务并发/吞吐vLLM 的官方 FP8 KV 文档提供缓存格式、尺度校准、粒度和层例外的实现入口。部署时分别记录“权重量化配置”和“KV 配置”,并检查导出尺度是否被实际读取;默认尺度不是完成了代表性校准的证明。不要假设所有粒度组合都被所有后端支持。
09 · NVIDIA 与昇腾的实现入口及验证边界
NVIDIA 路径可查 TensorRT-LLM 的缓存与数值精度文档、vLLM 的 FP8 KV 配方。昇腾的 ModelSlim 26.0.0 有 INT8 KVCache 说明,描述 key_states/value_states 的校准与伪量化;文档也明确其伪量化阶段仍需原精度内存,真实节省需要底层算子支持。工具可以模拟误差,不等于运行时缓存已经按 INT8 存放;推理引擎的消费格式与内核要继续核对。
依据:TensorRT-LLM · KV Cache System ↗ · TensorRT-LLM · Numerical Precision ↗ · ModelSlim 26.0.0 · KVCache INT8 量化算法说明 ↗
- 使用相同原始模型和 token 输入,观察各层误差、最终 logits 与任务质量。
- 覆盖短/长上下文、检索与复制任务、领域输入、长输出及位置边界;不要只测一般短问答。
- 测试缓存追加、分页边界、前缀共享、并行切分与模型支持的组合。
- 分别测相同并发延迟与相同容量下的吞吐,并记录格式、尺度来源、内核和工作区。
缓存成为容量或带宽瓶颈时更值得尝试;短上下文、计算或通信占主要时间、或量化内核不适配时,收益可能有限甚至变慢。本文给出机制与教学验证,不提供未经硬件实测的两家性能排名。
10 · 用四个问题检验理解
- 权重已经 INT4,为什么 KV 仍可能是 BF16?
- 为何 K 与 V 的量化误差影响不同环节?
- 低位码没变,为什么改 scale 会把旧数据读错?
- 缓存容量减半,为什么单 token 生成时间未必减半?
流程位置与关联方法
这个方法可能出现在多个位置;计算线包含模型计算与算子实现,执行线包含运行时、内存和通信。
资料与核查范围
原理依据以原始论文和官方文档为主;算例与时间线是教学推导。文档核查日期:2026-10-08。引用 main / latest 文档时,实际接口以安装版本为准。
- vLLM · Quantized KV Cache(latest) ↗
- KIVI · A Tuning-Free Asymmetric 2bit Quantization for KV Cache ↗
- TensorRT-LLM · KV Cache System ↗
- ModelSlim 26.0.0 · KVCache INT8 量化算法说明 ↗
- TensorRT-LLM · Numerical Precision ↗
代码为接入片段或教学伪代码;本页没有声称完成 NVIDIA / 昇腾硬件实测。