KV cache
显存占用计算、PagedAttention 管理机制与 KV 量化压缩方法
核心要点:
- KV 显存公式:$2 L \cdot n_{kv} \cdot d_{head} \cdot 2$ bytes/token (BF16)
- Llama 3 8B 每 token 128 KB, 4K context 512 MB, 128K context 16 GB
- 连续分配显存利用率仅 20-40%, PagedAttention 提升到 96%+, throughput ×2-4
- Chunked prefill (Sarathi) 把长 prompt 切 chunk 与 decode 交错,容量 +2.6× (Mistral 7B)
- KV INT8 perplexity 升 0.12, KIVI 2-bit batch ×4 throughput ×3
- StreamingLLM attention sink:前 4 token + 滑动窗口,4M+ context 不崩
- GQA / MQA / MLA:架构层面压缩 KV,外链长上下文章
名词定义
本篇共享名词在 8.1 总览 已定义 (KV cache / PagedAttention / Chunked prefill)。本篇新引入:
| 名词 | 定义 |
|---|---|
| Block table | PagedAttention 的逻辑→物理地址映射,类似 OS page table |
| Internal fragmentation | KV 预分配最大长度但实际只用 20-38%,浪费显存 |
| External fragmentation | KV 块释放后剩余空隙不能合并复用 |
| Attention sink | StreamingLLM 发现的现象:前几个 token 在 attention 上获得高权重,永久保留它们能稳定长上下文 |
| KIVI | 用 K per-channel + V per-token 非对称量化的 2-bit KV cache 方法 |
| KVQuant | 用 4 技术叠加 (Pre-RoPE Key / per-channel / 非均匀 / outlier 隔离) 实现 3-bit KV |
@tbl-kv-glossary 本篇新引入名词
KV cache 显存有多大?
核心问题:推理时 KV cache 占多少显存?跟 batch / seqlen / 模型架构什么关系?
KV cache 显存 = $2 \cdot L \cdot n_{kv\_head} \cdot d_{head} \cdot 2$ bytes per token (BF16),随 batch × seqlen 线性增长——是长上下文推理的主要瓶颈。
KV 显存公式 (per token)
$$\begin{equation} \text{KV bytes/token} = 2 \cdot L \cdot n_{kv\_head} \cdot d_{head} \cdot 2 \quad (\text{BF16}) \label{eq:kv-per-token} \end{equation}$$- 2: K 和 V 各一份
- $L$:层数
- $n_{kv\_head}$: K/V head 数 (GQA 下 < $n_{head}$)
- $d_{head}$: head 维度 (现代 LLM 工业收敛 128)
- $\times 2$: BF16 每数 2 bytes
Llama 3 8B 实际数字
Llama 3 8B 配置:$L = 32$, $n_{kv\_head} = 8$ (GQA), $d_{head} = 128$:
$$\begin{equation} 2 \cdot 32 \cdot 8 \cdot 128 \cdot 2 = 131{,}072 \text{ bytes} \approx 128 \text{ KB/token} \label{eq:kv-llama3-8b} \end{equation}$$按 context 长度:
| Context | KV cache (单请求) |
|---|---|
| 1K | 128 MB |
| 4K | 512 MB |
| 32K | 4 GB |
| 128K | 16 GB (超过 A100 单卡可用显存) |
@tbl-kv-llama3-context Llama 3 8B 单请求 KV 显存随 context 增长
KV 与模型权重竞争显存:Llama 3 8B 权重约 16 GB (BF16), 128K context KV 也是 16 GB——一加就吃满 A100 80GB 显存的大半。这是长上下文部署的根本难点。
与模型权重的关系
- 模型权重固定:不随 batch / seqlen 变,加载一次
- KV cache 动态:$O(B \cdot T)$,跟 batch 和 context 长度成正比
- 关系示意:
实际部署核心问题:在固定显存预算下,怎么平衡 batch size (吞吐) 和 context 长度?PagedAttention 就是解决这个空间利用问题。
PagedAttention:把 KV 切成 page
核心问题:传统 KV cache 给每个请求预分配最大长度的连续显存——但 90% 请求用不到最大长度,显存大量浪费。怎么改?
PagedAttention 把 KV 切成固定大小 block (16 token/block),用 block table 索引,按需分配,借鉴 OS virtual memory——显存利用率从 20-40% 提升到 96%+。
传统 KV 分配的两大碎片
vLLM 论文 (Kwon SOSP 2023)[1] 量化两类碎片:
- Internal fragmentation:给每个请求预分配 max_seq_len (如 2048) 的连续 KV 显存,但实际只用 200 (10%),剩余 1848 槽位浪费
- External fragmentation:不同请求的 KV 块释放后,显存出现"洞",新请求需要的连续块装不下
结果:KV 实际利用率 20-40%, 60-80% 显存浪费在碎片。
PagedAttention 的核心思路
借鉴 OS 虚拟内存:
- KV cache 切成 16 token/block 的固定块
- 每个请求维护一张 block table,记录"我的第 0-15 个 token 在物理块 X,第 16-31 在物理块 Y"
- 块按需分配,块间无需物理连续
- 共享前缀 (system prompt) 用 copy-on-write
PagedAttention 的算子
attention 计算时:
- 用 block table 查 K/V 物理位置
- block 内 K/V 仍连续,在 block 内做 attention
- 跨 block 合并:scaled dot-product 与 softmax 在合并所有 block 后做
核心收益:
| 指标 | 传统 | PagedAttention |
|---|---|---|
| 显存利用率 | 20-40% | 96%+ |
| 碎片率 | 60-80% | < 4% |
| Throughput | 1× | 2-4× vs FasterTransformer / Orca |
@tbl-kv-vllm vLLM PagedAttention 工业实测 (Kwon SOSP 2023)
算法的工程实现
vLLM block table 实现:
# 概念伪代码
class KVCachePool:
free_blocks: List[BlockId] # 全局空闲块池
request_block_tables: Dict[ReqId, List[BlockId]] # 每请求的块表
def allocate(self, req_id, new_tokens):
# 按需分配新块
n_blocks = ceil(new_tokens / 16)
new_blocks = self.free_blocks[:n_blocks]
self.free_blocks = self.free_blocks[n_blocks:]
self.request_block_tables[req_id].extend(new_blocks)
Chunked prefill:让 prefill 不阻塞 decode
核心问题:在线推理同时有"新请求 prefill (一次性算 4K token)" 和"老请求 decode (每秒 1 个 token)"。如果一个 8K prompt 进来 prefill, decode 必须停 1 秒——延迟尖峰。怎么破?
Chunked prefill 把长 prompt prefill 切成 512 token chunk,每个 batch = 1 prefill chunk + 所有 decode,让 decode 不被长 prompt 阻塞。
问题:prefill 长尾恶化 decode 延迟
Orca / vLLM 原始策略:新请求 prefill 时停顿所有 decode。
长 prompt 让 TBT (time-between-tokens) 恶化 28.3× (Sarathi 论文实测):
- 没新请求时,decode 100 ms/token
- 8K prompt 进来 prefill, decode 暂停 ~2.8 秒
- 用户感受到对话"卡顿"
Sarathi (Agrawal 2023) 切 chunk
Sarathi[2]:
- 把 prefill 切成 512 token/chunk
- 每 batch = 1 prefill chunk + 所有 decode (decode-maximal batching)
- decode 顺搭 prefill 的计算,成本降一个数量级
Sarathi-Serve (OSDI 2024) 形式化
Sarathi-Serve[3] 把它形式化为 stall-free 调度算法:
- 推荐 chunk size 512 (严格 SLO) 或 2048 (高吞吐)
- 实测:
- Mistral-7B 服务容量 +2.6×
- Falcon-180B +6.9×
- Pipeline bubble 减少 6.29×
这是 vLLM / SGLang / TRT-LLM 标配,现代推理引擎都用 chunked prefill。
KV cache 量化
核心问题:长上下文 KV 占主导,能不能用 INT8 / INT4 量化 KV cache 把显存再省 2-4×?
KV INT8 量化几乎无损 (PPL 升 0.12),INT4 / 2-bit 在精细量化方案下也几乎无损;H100 上 FP8 是原生硬件支持。
KV INT8:显存省 2×
ATOM (MLSys 2024) 实测 KV INT8:
- FP16 → INT8 节省 2× 显存
- Perplexity 仅升高 0.12
- TRT-LLM / lmdeploy 都支持;vLLM 截至 2026 Q1 仅支持 FP8, INT8 在 RFC 阶段
KIVI (ICML 2024): 2-bit + 混合非对称
KIVI[4] 创新点:
- K per-channel 量化 (K 在 channel 维度分布平稳)
- V per-token 量化 (V 在 token 维度分布平稳)
- 主推 2-bit, INT4 也适用
实测 (Llama-2 / Falcon / Mistral):
- 峰值显存 (含权重) 节省 2.6×
- 批大小扩展 4×
- 吞吐提升 2.35-3.47×
- 输出质量可比 FP16
KVQuant (NeurIPS 2024): 3-bit 1M context
KVQuant[5] 用四技术叠加:
- Pre-RoPE Key 量化 (RoPE 之前 K 分布更适合量化)
- Per-channel 非对称量化
- 非均匀量化 (考虑 K/V 分布形状)
- Dense-sparse outlier 隔离 (outlier 单独存 FP16)
实测:
- 3-bit 量化 perplexity 降级 < 0.1 (Wikitext-2 / C4)
- 单 A100-80GB 支持 LLaMA-7B 1M context
- 8 卡支持 10M context
- CUDA kernel 加速 ~1.7×
KV FP8 (H100 原生)
H100 / H200 原生支持 FP8 (e4m3 / e5m2):
- 内存节省 50%
- Decode-heavy 场景吞吐提升 1.45×
- TRT-LLM 一键开启:
KvCacheConfig(dtype='fp8') - H100 峰值 > 10,000 tokens/s, TTFT < 100 ms
TRT-LLM 推荐 FP8 优先于 INT8 (精度更优 + 硬件原生)。
KV 量化对比表
| 方案 | 位宽 | 显存省 | PPL 损 | 业界采用 |
|---|---|---|---|---|
| INT8 | 8-bit | 2× | +0.12 | TRT-LLM / lmdeploy |
| FP8 | 8-bit | 2× | ~0 | TRT-LLM (H100 默认) |
| KIVI | 2-bit | 4× | < 1% | 学术为主 |
| KVQuant | 3-bit | 5.3× | < 0.1 | 学术为主 |
@tbl-kv-quant-compare KV cache 量化方案对比
StreamingLLM: attention sink 让长流式不崩
核心问题:推理时 KV cache 随生成无限增长,context 超长后显存爆炸。直接"扔掉最早 KV" (滑动窗口) 会怎样?
直接滑动窗口让模型崩溃;StreamingLLM 发现保留前 4 个 token (attention sink) + 滑动窗口可以稳定 4M+ context。
滑动窗口直接崩溃
Xiao et al. ICLR 2024[6] 实测:把 KV cache 当滑动窗口 (扔掉最早 K tokens):
- Llama-2-13B PG19 dataset
- 滑动窗口 PPL 直接崩到 5158 (正常 ~5)
- 模型完全不可用
Attention sink 现象
论文核心发现:
- 训练好的 LLM 总是把 attention 高权重分给最前几个 token (无论这些 token 内容是什么)
- 这是"模型需要一个 attention sink 锚点" 的副产物
- 扔掉前几个 token, attention 分布完全失常,模型崩
解决方案:保留 attention sink + 滑动窗口
- 永久保留前 4 个 token (initial tokens)
- 后续用滑动窗口 (e.g., 1024 token window)
- 新 token 不断进来,滑动窗口里最早的扔掉,但前 4 永远在
实测:
- Llama-2-13B PG19 PPL = 5.40 (vs 滑动窗口崩到 5158)
- 支持 4M+ token 流式生成
- 比"滑动窗口 + 每次重算" 加速 22.2×
局限
- 中间 token 直接丢弃,无语义选择
- 跨窗口的历史不可检索 (没存)
- 适合流式对话,不适合长文档 retrieval
KV cache 与架构压缩
核心问题:PagedAttention / chunked prefill / 量化都是"运行时优化",不动模型结构。能不能直接在模型结构上压缩 KV?
GQA / MQA / MLA 是 KV 架构压缩方向,直接减小 $n_{kv\_head}$ 或共享 KV,详见 knowledge/03-长上下文/05-kv-cache架构压缩。
简要回顾 (详见 04-注意力机制/05-多头注意力 与长上下文章):
- GQA (Llama 3, Qwen): $n_{kv\_head} = 8$ 而 $n_{head} = 32-128$, KV 显存缩 4-16×
- MQA (Shazeer 2019): $n_{kv\_head} = 1$,极致压缩但表达力受损
- MLA (DeepSeek-V2/V3): K/V 从共享 low-rank latent 重建,KV cache 压缩 98.6%
这三种是"训练时" 的架构选择,推理时无需额外工程。本章本节仅指出存在性,详细数学和实证归长上下文章。
Takeaway
| 知识点 | 核心结论 |
|---|---|
| KV 显存公式 | $2 L \cdot n_{kv} \cdot d_{head} \cdot 2$ bytes/token (BF16) |
| Llama 3 8B 数字 | 128 KB/token, 4K → 512 MB, 128K → 16 GB (撑满 A100) |
| 与权重关系 | 权重固定,KV 随 $B \cdot T$ 线性增长,长上下文主瓶颈 |
| 传统碎片 | 显存利用率 20-40%, 60-80% 浪费在 internal/external fragmentation |
| PagedAttention | 16 token/block + block table,利用率 96%+, throughput +2-4× |
| Chunked prefill (Sarathi) | 512 token chunk + decode-maximal batching, Mistral-7B +2.6×, Falcon-180B +6.9× |
| KV INT8 | ATOM 实测 PPL 升 0.12,显存 ×2 |
| KIVI | 2-bit + per-channel K + per-token V, batch ×4, throughput ×3 |
| KVQuant | 3-bit + 4 技术叠加,单 A100 支持 LLaMA-7B 1M context |
| KV FP8 | H100 原生,TRT-LLM 推荐,1.45× throughput |
| StreamingLLM | 前 4 token attention sink + 滑动窗口,4M+ context 不崩,vs 重算 ×22.2 |
| 架构压缩 | GQA / MQA / MLA 在训练时减 KV,详见长上下文章 |
开放问题
- KV 量化的极限:2-bit (KIVI) 仍无损,是否能 1-bit (binary) 不崩?Binary KV 仍在研究
- PagedAttention 之后:vLLM 之后是否有更好的 KV 管理方案?SGLang RadixAttention 把前缀树用上,是否会成新标准
- Chunked prefill vs PD 分离:二者都解 prefill-decode 干扰,是否有最终的"统一方案"
- StreamingLLM 的语义局限:不能 retrieval 中间 token,是否会被"KV cache eviction 智能策略" 取代
- GQA → MLA 之后:KV 压缩到 98.6% 已经接近无损,是否还能进一步?学术上有研究但工业化未定
延伸阅读
- 上一篇:prefill 与 decode → 8.2 Prefill 与 Decode
- 下一篇:采样与解码 → 8.4 采样与解码
- KV 架构压缩 (GQA/MQA/MLA) → knowledge/03-长上下文/05-kv-cache架构压缩
- 长上下文 KV 推理管理 → knowledge/03-长上下文/07-推理-kv管理
- vLLM PagedAttention 详细 → https://vllm.ai/blog/2023-06-20-vllm
- Sarathi-Serve 论文 → https://arxiv.org/abs/2403.02310
参考资料
- Kwon et al. Efficient Memory Management for Large Language Model Serving with PagedAttention. SOSP 2023. https://arxiv.org/abs/2309.06180
- Agrawal et al. SARATHI: Efficient LLM Inference by Piggybacking Decodes with Chunked Prefills. 2023. https://arxiv.org/abs/2308.16369
- Agrawal et al. Sarathi-Serve: Taming Throughput-Latency Tradeoff in LLM Inference. OSDI 2024. https://arxiv.org/abs/2403.02310
- Liu et al. KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache. ICML 2024. https://arxiv.org/abs/2402.02750
- Hooper et al. KVQuant: Towards 10 Million Context Length LLM Inference. NeurIPS 2024. https://arxiv.org/abs/2401.18079
- Xiao et al. Efficient Streaming Language Models with Attention Sinks. ICLR 2024. https://arxiv.org/abs/2309.17453