KV Cache是大模型推理中最关键的优化技术,也是最大的内存瓶颈。在70B模型、128K上下文的典型配置下,KV Cache占用超过40GB显存,远超模型权重本身。2026年,KV Cache管理已成为推理系统的核心竞争力。
1. KV Cache基础
1.1 为什么需要KV Cache
Transformer自回归生成时,每生成一个新token需要关注之前所有token的Key和Value。为了避免重复计算,将历史K,V缓存下来:
$$\text{KV Cache}_t = {(K_1, V_1), (K_2, V_2), …, (K_t, V_t)}$$
1.2 内存占用计算
KV Cache的显存占用公式:
$$\text{Memory}_{KV} = 2 \times L \times H \times S \times d_h \times P \times b$$
其中:
- $L$:层数
- $H$:注意力头数
- $S$:序列长度
- $d_h$:每头维度
- $P$:精度字节(fp16=2)
- $b$:batch size
以Llama-4-70B为例($L=80, H=64, d_h=128$),128K上下文、batch=1:
$$2 \times 80 \times 64 \times 131072 \times 128 \times 2 = 274\text{GB}$$
这个数字远超单GPU显存容量。
2. PagedAttention:分页KV Cache
2.1 核心思想
vLLM提出的PagedAttention借鉴了操作系统的虚拟内存管理,将KV Cache组织为固定大小的页(block),按需分配:
传统分配(连续):
┌──────────────────────────────────┐
│ Sequence A (预分配max_len) │ 大量浪费
└──────────────────────────────────┘
┌──────────────────────────────────┐
│ Sequence B (预分配max_len) │ 大量浪费
└──────────────────────────────────┘
PagedAttention(分页):
┌─────┬─────┬─────┬─────┬─────┐
│ A-1 │ A-2 │ B-1 │ A-3 │ B-2 │ 按需分配,无浪费
└─────┴─────┴─────┴─────┴─────┘
2.2 实现
class PagedKVCache:
def __init__(self, num_blocks, block_size, num_heads, head_dim, num_layers):
self.block_size = block_size # 通常为16
self.num_blocks = num_blocks
self.free_blocks = list(range(num_blocks))
self.block_tables = {} # seq_id -> [block_ids]
# 预分配连续显存
self.k_cache = torch.zeros(
num_layers, num_blocks, block_size, num_heads, head_dim
)
self.v_cache = torch.zeros_like(self.k_cache)
def allocate(self, seq_id, seq_len):
"""为序列分配KV Cache块"""
num_needed = (seq_len + self.block_size - 1) // self.block_size
blocks = []
for _ in range(num_needed):
if not self.free_blocks:
# 触发换出
self._evict()
blocks.append(self.free_blocks.pop())
self.block_tables[seq_id] = blocks
def append(self, seq_id, positions, new_k, new_v):
"""追加新的K,V到缓存"""
blocks = self.block_tables[seq_id]
for pos, k, v in zip(positions, new_k, new_v):
block_idx = pos // self.block_size
offset = pos % self.block_size
cache_block = blocks[block_idx]
self.k_cache[:, cache_block, offset] = k
self.v_cache[:, cache_block, offset] = v
2.3 效果对比
| 方案 | 显存利用率 | 最大并发 | 碎片率 |
|---|---|---|---|
| 连续分配 | 20-40% | 低 | 高 |
| PagedAttention | 90-95% | 高 | <5% |
3. KV Cache压缩
3.1 量化压缩
将KV Cache从fp16量化到INT8或INT4:
class QuantizedKVCache:
def __init__(self, num_bits=4):
self.num_bits = num_bits
if num_bits == 4:
self.cache_k = None # INT4 tensor
self.cache_v = None
self.scales_k = None # per-channel scale
self.scales_v = None
def quantize(self, tensor):
"""在线量化KV Cache"""
# 计算scale
scale = tensor.abs().max(dim=-1, keepdim=True).values / (2**(self.num_bits-1) - 1)
scale = scale.clamp(min=1e-8)
# 量化
quantized = torch.round(tensor / scale).clamp(
-(2**(self.num_bits-1)), 2**(self.num_bits-1) - 1
)
return quantized.to(torch.int8), scale
def dequantize(self, quantized, scale):
return quantized.float() * scale
不同量化方案的效果:
| 方案 | 压缩比 | 质量损失 | 延迟开销 |
|---|---|---|---|
| FP16 (baseline) | 1x | 0% | 0% |
| INT8 (per-token) | 2x | <0.5% | +3% |
| INT4 (per-channel) | 4x | ~2% | +5% |
| INT4 (per-token + 重要性) | 4x | <1% | +8% |
3.2 淘汰策略
对于超长上下文,不可能缓存所有token的KV。需要智能淘汰低价值token:
H2O (Heavy-Hitter Oracle):基于注意力分数识别重要token
def h2o_eviction(cache_k, cache_v, attention_scores, keep_ratio=0.3):
"""保留注意力分数最高的token"""
# attention_scores: [batch, heads, query_len, key_len]
# 计算每个key token的累积注意力分数
importance = attention_scores.sum(dim=(0, 1, 2)) # [key_len]
# 选择top-k保留
num_keep = int(len(importance) * keep_ratio)
_, keep_indices = torch.topk(importance, num_keep)
keep_indices = keep_indices.sort().values
return cache_k[keep_indices], cache_v[keep_indices]
StreamingLLM:保留attention sink(前几个token)+ 滑动窗口
def streaming_llm_cache(cache_k, cache_v, window_size=4096, sink_size=4):
"""Attention sink + 滑动窗口策略"""
# 保留前sink_size个token(attention sink)
sink_k, sink_v = cache_k[:sink_size], cache_v[:sink_size]
# 保留最近window_size个token
window_k = cache_k[-window_size:]
window_v = cache_v[-window_size:]
return torch.cat([sink_k, window_k]), torch.cat([sink_v, window_v])
4. 跨请求KV Cache共享
4.1 Prefix Caching
多个请求共享相同前缀时(如system prompt),可以缓存前缀的KV:
class PrefixCacheManager:
def __init__(self, max_cache_size=10 * 1e9):
self.cache = {} # hash -> (k_cache, v_cache)
self.max_size = max_cache_size
self.current_size = 0
def get_or_compute(self, prefix_tokens, model):
"""获取或计算前缀的KV Cache"""
prefix_hash = hash_tokens(prefix_tokens)
if prefix_hash in self.cache:
return self.cache[prefix_hash]
# 计算KV Cache
k_cache, v_cache = model.compute_kv(prefix_tokens)
# 存入缓存
cache_size = k_cache.nbytes + v_cache.nbytes
if self.current_size + cache_size > self.max_size:
self._evict(cache_size)
self.cache[prefix_hash] = (k_cache, v_cache)
self.current_size += cache_size
return k_cache, v_cache
4.2 Radix Tree缓存
2026年的前沿方案使用Radix Tree组织共享前缀,实现细粒度的缓存复用:
Radix Tree结构:
[System Prompt]
/ \
[User Query 1] [User Query 2]
/ \
[Context A] [Context B]
每个节点缓存对应前缀的KV Cache,新请求只需计算不匹配的部分。
5. KV Cache Offloading
5.1 GPU-CPU分层缓存
对于超长上下文,将KV Cache分层存储:
class LayeredKVCache:
def __init__(self, gpu_capacity, cpu_capacity):
self.gpu_cache = GPUCache(gpu_capacity) # 热数据
self.cpu_cache = CPUCache(cpu_capacity) # 温数据
self.disk_cache = DiskCache() # 冷数据
def get(self, layer, position):
# 优先从GPU获取
if self.gpu_cache.contains(layer, position):
return self.gpu_cache.get(layer, position)
# 从CPU获取并预取到GPU
if self.cpu_cache.contains(layer, position):
value = self.cpu_cache.get(layer, position)
self.gpu_cache.put(layer, position, value)
return value
# 从磁盘获取
value = self.disk_cache.get(layer, position)
self.cpu_cache.put(layer, position, value)
self.gpu_cache.put(layer, position, value)
return value
def prefetch(self, positions):
"""预取即将访问的KV到GPU"""
for pos in positions:
if not self.gpu_cache.contains(pos):
value = self.cpu_cache.get_async(pos)
self.gpu_cache.put_async(pos, value)
5.2 性能数据
| 方案 | 支持上下文 | 吞吐量 | 延迟 |
|---|---|---|---|
| 纯GPU | 32K | 100% | 1x |
| GPU+CPU Offload | 128K | 85% | 1.3x |
| 三层(GPU/CPU/Disk) | 1M+ | 60% | 2.1x |
| 智能预取 | 1M+ | 80% | 1.5x |
6. 2026年趋势
- 稀疏KV Cache:结合MoE思想,仅激活部分KV Cache
- 学习型压缩:使用小型网络学习最优KV Cache压缩策略
- 跨模型共享:不同模型间的KV Cache复用(需架构对齐)
- 硬件协同设计:专用KV Cache管理芯片
KV Cache管理已从单纯的工程优化发展为需要算法、系统和硬件协同设计的综合课题,是大模型推理效率提升的核心杠杆。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
