为什么KV Cache如此重要?
在自回归生成中,每生成一个新token都需要计算它与之前所有token的注意力。如果不缓存,生成第N个token需要重新计算前N-1个token的K和V,复杂度为O(N²)。KV Cache将已计算的K/V存储起来,使每步生成只需O(N)的计算。
但缓存本身带来了巨大的显存压力。以Llama-3-70B为例,FP16精度下单个token的KV Cache占用约320KB。在32K上下文、batch_size=32的场景下,KV Cache总量达到约320GB——远超模型本身的140GB。
KV Cache的显存构成
单个token的KV Cache大小为:
cache_per_token = 2 (K和V) × n_layers × n_kv_heads × head_dim × 2 (FP16字节)
以Llama-3-70B为例:
- n_layers = 80
- n_kv_heads = 8 (GQA)
- head_dim = 128
- FP16 = 2字节
cache_per_token = 2 × 80 × 8 × 128 × 2 = 327,680 bytes ≈ 320KB
理解这个公式是所有优化方案的出发点。
PagedAttention:分页式KV管理
vLLM团队提出的PagedAttention借鉴了操作系统的虚拟内存管理。核心思想:
块式管理
将KV Cache分为固定大小的块(Block),每个块存储若干token的KV。块不需要在物理上连续,通过块表(Block Table)映射逻辑位置到物理位置。
class PagedKVCache:
def __init__(self, n_blocks, block_size, n_layers, n_kv_heads, head_dim):
self.block_size = block_size # 每块的token数
self.n_blocks = n_blocks
# 预分配所有块的存储
self.k_blocks = torch.zeros(n_blocks, n_layers, block_size, n_kv_heads, head_dim)
self.v_blocks = torch.zeros(n_blocks, n_layers, block_size, n_kv_heads, head_dim)
self.free_blocks = list(range(n_blocks))
self.block_tables = {} # seq_id -> [block_indices]
def allocate(self, seq_id, n_tokens):
n_blocks_needed = (n_tokens + self.block_size - 1) // self.block_size
blocks = []
for _ in range(n_blocks_needed):
if not self.free_blocks:
raise OOMError("No free KV cache blocks")
blocks.append(self.free_blocks.pop())
self.block_tables[seq_id] = blocks
def append(self, seq_id, token_idx, k, v):
block_idx = token_idx // self.block_size
offset = token_idx % self.block_size
physical_block = self.block_tables[seq_id][block_idx]
self.k_blocks[physical_block, :, offset] = k
self.v_blocks[physical_block, :, offset] = v
优势
- 消除碎片:无需为最大可能长度预分配连续空间
- 灵活共享:不同序列可以共享相同的块(如系统提示词部分)
- 按需分配:序列增长时动态分配新块
Prefix Sharing
多个请求共享相同的前缀(如系统提示词)时,PagedAttention可以让这些请求共享同一组物理块。这在对话AI场景中可节省30-50%的显存。
KV Cache量化
将KV Cache从FP16量化到更低精度是另一个重要优化方向。
INT8量化
将KV Cache从FP16量化到INT8可以节省一半显存。由于K和V的分布不同,通常需要分别处理:
def quantize_kv_cache(k_cache, v_cache, scale_k, scale_v):
"""对称量化KV Cache到INT8"""
k_int8 = (k_cache / scale_k).round().clamp(-128, 127).to(torch.int8)
v_int8 = (v_cache / scale_v).round().clamp(-128, 127).to(torch.int8)
return k_int8, v_int8
def dequantize_kv_cache(k_int8, v_int8, scale_k, scale_v):
return k_int8.float() * scale_k, v_int8.float() * scale_v
关键在于缩放因子的选择。实践中,逐通道量化(per-channel)比逐张量量化(per-tensor)精度损失更小。
INT4量化
更激进的量化方案。INT4 KV Cache将显存节省4倍,但精度损失更为显著。研究表明,K Cache的INT4量化会导致较大的性能退化,而V Cache对INT4更为鲁棒。因此,许多系统采用混合精度——K用INT8、V用INT4。
FP8量化
NVIDIA H100支持的原生FP8格式提供了精度和效率的更好平衡。FP8有两种变体:E4M3和E5M2,前者精度更高,后者动态范围更大。KV Cache量化通常使用E5M2(因为KV值范围较大)。
KV Cache的压缩与驱逐
滑动窗口
只保留最近W个token的KV Cache,超出的直接丢弃。Mistral-7B使用W=4096的滑动窗口。这种方法虽然简单,但会丢失长距离信息。
结构化剪枝
基于注意力分数分析,识别出"不重要"的token并驱逐其KV。H2O(Heavy-Hitter Oracle)算法保持了约5%的token就能维持大部分性能:
def h2o_eviction(attention_scores, kv_cache, keep_ratio=0.05):
"""基于注意力分数的KV Cache驱逐"""
n_tokens = attention_scores.shape[-1]
n_keep = int(n_tokens * keep_ratio)
# 计算每个token的累计注意力分数
cumulative_scores = attention_scores.sum(dim=-2) # [n_tokens]
# 保留分数最高的token
keep_indices = torch.topk(cumulative_scores, n_keep).indices
keep_indices, _ = keep_indices.sort()
return kv_cache[keep_indices], keep_indices
语义聚类压缩
将语义相似的token的KV进行聚类合并,用聚类中心代替原始KV。这种方法比简单的截断保留了更多信息。
跨层KV共享
观察到相邻层的注意力模式高度相似,一些研究提出跨层共享KV Cache:
- YOCO(You Only Cache Once):只在模型底部计算一次KV,所有层共享
- CLA(Cross-Layer Attention):将层分为若干组,组内共享KV
这种方法可以将KV Cache减少50-80%,但需要特殊的预训练或微调来适应。
2026年的KV Cache前沿
多模态KV分离
在视觉-语言模型中,图像token的KV Cache占用远大于文本。最新的做法是对图像token和文本token使用不同的压缩策略——图像token可以用更激进的压缩(因为视觉信息有冗余)。
推测性KV预取
在推测解码中,draft模型预测的多个候选token的KV可以预先计算和缓存。如果验证通过则直接使用,否则丢弃。这减少了大模型的KV计算次数。
持久化KV存储
对于高频使用的系统提示和文档,将其KV Cache持久化到磁盘或SSD。新请求到来时直接加载,避免重新计算。一些推理引擎已经支持这种"KV Cache仓库"模式。
工程实践建议
- 基准测量:先测量你的模型在目标场景下的KV Cache大小,再决定优化策略
- GQA/MQA:使用Grouped-Query Attention或Multi-Query Attention可以从架构层面减少KV Cache
- 量化优先:INT8量化对性能几乎无损,应作为第一步优化
- 分页管理:PagedAttention或类似方案对高并发场景是刚需
- 监控碎片率:跟踪KV Cache的碎片率和利用率,动态调整块大小
结语
KV Cache是LLM推理系统中最为关键的工程组件之一。从PagedAttention的分页管理到量化压缩、从注意力驱动的驱逐到跨层共享,每一种技术都在为"在有限显存中服务更长上下文"这一核心目标服务。随着上下文长度从32K向1M迈进,KV Cache优化将变得更加重要。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。