跳到主要内容

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 tablePagedAttention 的逻辑→物理地址映射,类似 OS page table
Internal fragmentationKV 预分配最大长度但实际只用 20-38%,浪费显存
External fragmentationKV 块释放后剩余空隙不能合并复用
Attention sinkStreamingLLM 发现的现象:前几个 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 长度:

ContextKV cache (单请求)
1K128 MB
4K512 MB
32K4 GB
128K16 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 长度成正比
  • 关系示意
$$\begin{equation} \text{Total VRAM} \approx \text{Weights} + B \cdot T \cdot \text{KV/token} \label{eq:kv-total-vram} \end{equation}$$

实际部署核心问题:在固定显存预算下,怎么平衡 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%
Throughput2-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×
  • 批大小扩展
  • 吞吐提升 2.35-3.47×
  • 输出质量可比 FP16

KVQuant (NeurIPS 2024): 3-bit 1M context

KVQuant[5] 用四技术叠加:

  1. Pre-RoPE Key 量化 (RoPE 之前 K 分布更适合量化)
  2. Per-channel 非对称量化
  3. 非均匀量化 (考虑 K/V 分布形状)
  4. 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 损业界采用
INT88-bit+0.12TRT-LLM / lmdeploy
FP88-bit~0TRT-LLM (H100 默认)
KIVI2-bit< 1%学术为主
KVQuant3-bit5.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
PagedAttention16 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 INT8ATOM 实测 PPL 升 0.12,显存 ×2
KIVI2-bit + per-channel K + per-token V, batch ×4, throughput ×3
KVQuant3-bit + 4 技术叠加,单 A100 支持 LLaMA-7B 1M context
KV FP8H100 原生,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% 已经接近无损,是否还能进一步?学术上有研究但工业化未定

延伸阅读

参考资料

  1. Kwon et al. Efficient Memory Management for Large Language Model Serving with PagedAttention. SOSP 2023. https://arxiv.org/abs/2309.06180
  2. Agrawal et al. SARATHI: Efficient LLM Inference by Piggybacking Decodes with Chunked Prefills. 2023. https://arxiv.org/abs/2308.16369
  3. Agrawal et al. Sarathi-Serve: Taming Throughput-Latency Tradeoff in LLM Inference. OSDI 2024. https://arxiv.org/abs/2403.02310
  4. Liu et al. KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache. ICML 2024. https://arxiv.org/abs/2402.02750
  5. Hooper et al. KVQuant: Towards 10 Million Context Length LLM Inference. NeurIPS 2024. https://arxiv.org/abs/2401.18079
  6. Xiao et al. Efficient Streaming Language Models with Attention Sinks. ICLR 2024. https://arxiv.org/abs/2309.17453