【词汇专栏】KV Cache:大模型推理的加速引擎
·
KV Cache:大模型推理的加速引擎
一句话理解
KV Cache 是大模型推理时的"缓存加速器"——它把已经计算过的注意力键值保存下来,避免重复计算,让生成速度飙升。
没有KV Cache:每次生成一个Token,都要重新计算之前所有Token的注意力 → 慢!
有KV Cache:只计算新Token的注意力,之前的结果直接复用 → 快!
为什么需要KV Cache?
大模型推理分为两个阶段:
1. Prefill阶段(首次计算)
输入:"今天天气"
↓
计算每个Token的K(键)和V(值)
↓
生成第一个输出Token
2. Decode阶段(逐个生成)
生成"很" → 需要关注"今天天气很"
生成"好" → 需要关注"今天天气很好"
生成"。" → 需要关注"今天天气很好。"
问题来了:每次生成新Token,都要重新计算之前所有Token的注意力,开销巨大!
Token序列长度: 1 → 2 → 3 → 4 → 5 → ... → N
计算复杂度: O(1) → O(2) → O(3) → O(4) → O(5) → ... → O(N)
O(N²)的复杂度,这就是"Context越长,推理越慢"的原因。
KV Cache的工作原理
┌─────────────────────────────────────────────────────────┐
│ 没有KV Cache
│
│ Token 1 → 计算 K1, V1
│ ↓
│ Token 2 → 计算 K2, V2 + 重新计算 K1, V1 的注意力
│ ↓
│ Token 3 → 计算 K3, V3 + 重新计算 K1, K2, V1, V2 的注意力
│ ↓
│ ... 重复计算,浪费资源!
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 有KV Cache
│
│ Token 1 → 计算 K1, V1,存入Cache
│ ↓
│ Token 2 → 计算 K2, V2,复用Cache中的K1, V1
│ ↓
│ Token 3 → 计算 K3, V3,复用Cache中的K1, K2, V1, V2
│ ↓
│ ... 只计算新Token,充分利用缓存!
└─────────────────────────────────────────────────────────┘
数学原理
Transformer注意力计算
Attention(Q, K, V) = softmax(QK^T / √d) × V
其中:
- Q(Query):当前位置的查询
- K(Key):所有位置的键
- V(Value):所有位置的值
Decode阶段的优化
# 原始做法:每次都重新计算所有注意力
def generate_token_naive(tokens):
for i in range(max_length):
# 每次都用所有历史tokens计算
hidden_states = model(torch.cat(tokens)) # O(N) 的复杂度
new_token = sample(hidden_states[-1])
tokens.append(new_token)
# 使用KV Cache:只计算新增Token的注意力
def generate_token_with_kv_cache(tokens, kv_cache):
# 只计算新token的Q
q_new = compute_query(tokens[-1])
# 从缓存获取历史的K和V
k_all = torch.cat([kv_cache.k, q_new.k]) # O(1) 复用
v_all = torch.cat([kv_cache.v, q_new.v]) # O(1) 复用
# 计算注意力
attention = softmax(q_new @ k_all.T / √d) @ v_all
# 更新缓存
kv_cache.update(k_new, v_new)
return new_token
KV Cache的代价
KV Cache快是真快,但也有代价:
1. 显存占用巨大
公式:显存 ≈ 2 × 层数 × 隐藏维度 × BatchSize × SequenceLength × 2(Keys+Values)
以LLaMA 7B为例:
- 32层
- 隐藏维度4096
- 半精度(2字节)
- 上下文长度2048
每1万个Token需要:32 × 4096 × 2 × 10000 × 2 ≈ 5GB显存!
2. 显存碎片化
问题:预留太多 → 浪费
预留太少 → 无法处理长序列
┌────────────────────────────────────────┐
│ GPU显存
│ ┌──────────┐ ┌──────────┐
│ │ KV Cache │ KV Cache ...
│ │ 请求1 │ 请求2
│ │ 1000 tokens │ 500 tokens
│ └──────────┘ └──────────┘
│ ↑
│ 大小不一,碎片化严重
└────────────────────────────────────────┘
2026年最新优化技术
1. PagedAttention(vLLM的核心技术)
受操作系统分页启发,按块管理KV Cache:
┌─────────────────────────────────────────────────┐
│
│ 传统方式:请求 → 整个分配(要么全要,要么不要)
│
│ PagedAttention:分块管理(按需分配,灵活高效)
│
│ KV Cache:| Block 0 | Block 1 | Block 2 | ...
│ | AIGC... | ,今天... | ,天气... │ ← 按块存储
│
│ 优势:
│ ├─ 显存利用率提升 60%+
│ ├─ 支持更多并发请求
│ └─ 处理更长上下文成为可能
│
└─────────────────────────────────────────────────┘
2. ChunkKV(长上下文优化)
针对超长上下文(100K+ tokens)的KV Cache压缩:
原始KV Cache:[K1, K2, K3, ..., K100K]
↓ 语义压缩
压缩后KV Cache:[K1', K3', K7', ..., K100K'] ← 保留关键块
3. ThinKV(推理模型优化)
专门为Reasoning Model设计的KV Cache压缩:
Reasoning Model特点:CoT过程长,生成几百个Token很正常
问题:KV Cache爆炸性增长
ThinKV方案:
1. 检测思考过程中的"关键决策点"
2. 只保留这些关键点的KV
3. 中间过程用隐式压缩代替显式存储
4. LongSpec(长上下文投机解码)
让投机解码(Speculative Decoding)也能处理长上下文:
投机解码:用一个"小模型"猜测多个Token,大模型验证
问题:长上下文下,验证速度也变慢
LongSpec解决方案:
- 分段验证,避免全量计算
- 局部验证 + 全局采样
KV Cache实战配置
vLLM配置示例
from vllm import LLM, SamplingParams
# 初始化模型,自动启用PagedAttention
llm = LLM(
model="meta-llama/Llama-3.1-8B-Instruct",
gpu_memory_utilization=0.9, # 90%显存用于KV Cache
max_model_len=32768, # 最大上下文长度
)
sampling_params = SamplingParams(
temperature=0.7,
max_tokens=1024,
)
# 请求会自动使用KV Cache加速
outputs = llm.generate(prompts, sampling_params)
Hugging Face配置示例
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
model_id = "meta-llama/Llama-3.1-8B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
model_id,
torch_dtype=torch.float16,
device_map="auto",
)
# 启用KV Cache(默认启用)
inputs = tokenizer("今天天气", return_tensors="pt").to(model.device)
# 第一次生成
output = model.generate(**inputs, max_new_tokens=100, use_cache=True)
不同框架的KV Cache支持
| 框架 | KV Cache | 分页管理 | 备注 |
|---|---|---|---|
| vLLM | ✅ | ✅ PagedAttention | 生产级推荐 |
| TensorRT-LLM | ✅ | ✅ | NVIDIA官方优化 |
| Hugging Face | ✅ | ❌ | 简单场景够用 |
| LightLLM | ✅ | ✅ | 国产高性能框架 |
| Text generation inference | ✅ | ✅ | HuggingFace官方 |
KV Cache的质量指标
命中率(Hit Rate)
命中率 = Cache命中次数 / 总访问次数
理想情况:首次 Prefill 后,所有 Decode 请求都命中 Cache
实际情况:多个并发请求会竞争 Cache 空间
吞吐量和延迟
没有KV Cache:
- 吞吐量:10 tokens/s
- 延迟:100ms per token
有KV Cache(短上下文):
- 吞吐量:50 tokens/s (↑5倍)
- 延迟:20ms per token (↓5倍)
有KV Cache(长上下文 + PagedAttention):
- 吞吐量:100+ tokens/s (↑10倍)
- 延迟:10ms per token (↓10倍)
常见问题
Q1:KV Cache和系统缓存是一回事吗?
不是! 是两个完全不同的概念:
| 类型 | 位置 | 作用 |
|---|---|---|
| 系统缓存 | 磁盘/内存 | 避免重复读取磁盘文件 |
| KV Cache | GPU显存 | 避免重复计算Transformer注意力 |
Q2:Batch Size越大越好吗?
不是,Batch Size增大的代价:
- 每个请求都需要独立的KV Cache
- 显存总容量固定
- Batch大 → 每个请求的Cache少 → 反而可能变慢
最佳策略:
- 长序列 + 小Batch
- 短序列 + 大Batch
- 动态调整,根据实际吞吐选最优
Q3:推理时显存不够怎么办?
# 方法1:减少上下文长度
model.generate(..., max_context_length=2048) # 截断长输入
# 方法2:使用量化
model = AutoModelForCausalLM.from_pretrained(
model_id,
load_in_8bit=True # 量化到8位,减少KV Cache占用
)
# 方法3:使用Streaming + 滑动窗口
# 只保留最近N个Token的KV Cache
总结
┌────────────────────────────────────────────────────────┐
│
│ KV Cache的核心价值:
│
│ ├─ 时间换空间:用显存换速度
│ ├─ 避免重复计算:O(N²) → O(N)
│ ├─ 吞吐提升:5-10倍甚至更高
│ └─ 是大模型推理引擎的标配优化
│
│ 2026年进化:
│ ├─ PagedAttention:显存管理革新
│ ├─ ChunkKV/ThinKV:长上下文压缩
│ └─ LongSpec:超长推理加速
│
│ 没有KV Cache,就没有大模型的实时响应体验!
│
└────────────────────────────────────────────────────────┘
延伸阅读
| 相关文章 | 说明 |
|---|---|
| W20 Speculative Decoding | 另一种加速推理的技术 |
| W13 Transformer | 了解注意力机制的基础 |
| W07 上下文窗口 | 上下文与KV Cache的关系 |
本文收录于「AI词汇专栏」
相关阅读:W07 上下文窗口 · W20 Speculative Decoding
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)