KV Cache 压缩:把 7B 模型的显存需求砍掉 60%
前言
Qwen2.5-7B,seq=8192,batch=16,KV Cache 占 12GB 显存。910B 单卡 64GB 显存,跑完模型只剩 8GB 给激活值。开 KV Cache 量化后显存降到 4.8GB,能跑 batch=32,吞吐涨了 104%。
KV Cache 优化不是"省显存",真正的收益是省出的显存能跑更大 batch,更大 batch 吞吐更高。
KV Cache 的显存占用计算
每个 token 的 KV Cache 大小:
KV_per_token = 2 × layers × hidden_dim × dtype_size
Qwen2.5-7B 为例:
- layers = 28
- hidden_dim = 3584
- dtype_size = 2(FP16)
KV_per_token = 2 × 28 × 3584 × 2 = 401KB
seq=8192 时,单个请求的 KV Cache:
KV_cache = 401KB × 8192 = 3.2GB
batch=16 时:
总 KV Cache = 3.2GB × 16 = 51GB
51GB 光 KV Cache 就吃掉 80% 显存,只剩 13GB 给模型权重、激活值、运行时开销。
工程经验:很多人以为 KV Cache 优化就是省显存。其实真正的收益是:省出的显存允许跑更大的 batch。Qwen2.5-7B 在 910B 单卡,FP16 batch=8 吞吐 72 tokens/s;开 KV Cache 量化 + PagedAttention 后 batch 开到 32,吞吐 147 tokens/s。算力没变,显存够用了。
INT8 量化:显存省一半
INT8 量化是最简单的 KV Cache 压缩方案。
原理:
FP16 的 KV Cache 转成 INT8:
KV_int8 = round(KV_fp16 / scale)
scale 是量化参数,可以按层、按头、按 token 三种粒度。
显存收益:
| 量化方案 | KV Cache 大小 | 节省 |
|---|---|---|
| FP16(基准) | 12GB | - |
| INT8(按层量化) | 6.2GB | -48% |
| INT8(按头量化) | 6.0GB | -50% |
精度损失:
| 量化方案 | PPL 变化 | 下游任务精度损失 |
|---|---|---|
| 按层量化 | +0.02 | < 0.3% |
| 按头量化 | +0.05 | < 0.5% |
实现方式:
ops-transformer 提供 KVCacheQuantize 算子:
from ops_transformer import KVCacheQuantize
# 初始化量化器
quantizer = KVCacheQuantize(
quant_mode="per_head", # 按头量化
scale_type="dynamic" # 动态计算 scale
)
# 量化 KV Cache
k_int8, v_int8, k_scale, v_scale = quantizer.quantize(k_fp16, v_fp16)
# 反量化(用于 Attention 计算)
k_fp16, v_fp16 = quantizer.dequantize(k_int8, v_int8, k_scale, v_scale)
工程经验:前 4 层 KV Cache 量化影响精度大。原因:前几层捕获基础特征,量化误差会放大到后面所有层。我们的做法:前 4 层用 FP16,后面用 INT8。混合精度下精度损失 < 0.2%,显存省 45%。
PagedAttention:消碎片
KV Cache 的显存碎片是个大问题。
问题场景:
batch=8,每个请求的 seq 长度不同:
- 请求 1:seq=1024,KV Cache = 400MB
- 请求 2:seq=4096,KV Cache = 1.6GB
- 请求 3:seq=512,KV Cache = 200MB
- …
分配连续内存给每个请求,会产生大量碎片。请求 2 结束后释放 1.6GB,但请求 4(seq=2048)要 800MB 连续空间,可能分配失败(碎片化导致没有足够大的连续块)。
PagedAttention 方案:
把 KV Cache 分成固定大小的 block(例如每个 block 存 16 个 token)。按需分配 block,不要求连续。
Block 0: token 0-15 的 KV
Block 1: token 16-31 的 KV
Block 2: token 32-47 的 KV
...
请求的 KV Cache 用链表串起来:
请求 1: Block 0 → Block 1 → Block 2
请求 2: Block 3 → Block 4 → Block 5 → Block 6
请求 3: Block 7
Block 可以不连续,消碎片。
显存利用率对比:
| 方案 | 显存利用率 | 碎片率 |
|---|---|---|
| 连续分配 | 62% | 38% |
| PagedAttention | 95% | < 5% |
实现方式:
MindIE 推理引擎内置 PagedAttention:
from mindie import LLMEngine
engine = LLMEngine(
model="Qwen/Qwen2.5-7B",
kv_cache_config={
"enable_paged_attention": True,
"block_size": 16 # 每个 block 存 16 个 token
}
)
工程经验:block_size 选 16 最优。block_size=8 时管理开销大,block_size=32 时碎片多。实测:block_size=16 时碎片率 < 5%,吞吐最高。
CSA/HCA 压缩:DeepSeek-V4 的 128 倍压缩
DeepSeek-V4 用 CSA(Compressed Sparse Attention,4 倍压缩)和 HCA(Hierarchical Compressed Attention,128 倍压缩)交替使用,实现长序列推理。
原理:
HCA 用"压缩器"把 KV Cache 压到原来的 1/128:
KV_compressed = Compressor(KV_original) # 128 倍压缩
压缩后的 KV Cache 用于 Attention 计算:
Q × Compressed_K^T → Attention Score
128K 序列的显存收益:
| 方案 | KV Cache 大小 | TPUT |
|---|---|---|
| Full Attention | 7.3GB | > 80ms |
| CSA(4 倍压缩) | 1.8GB | 25ms |
| HCA(128 倍压缩) | 57MB | < 10ms |
128K 序列下,HCA 把 KV Cache 从 7.3GB 压到 57MB,TPOT < 10ms。
精度影响:
| 压缩方案 | PPL 变化 | 长文档任务精度损失 |
|---|---|---|
| CSA(4 倍) | +0.08 | < 1% |
| HCA(128 倍) | +0.35 | < 2% |
实现方式:
ops-transformer 提供压缩算子:
from ops_transformer import HCACompressor
compressor = HCACompressor(
compression_ratio=128,
num_levels=3 # 3 级层次压缩
)
# 压缩 KV Cache
k_compressed, v_compressed = compressor.compress(k, v)
# Attention 时解压
k_decompressed, v_decompressed = compressor.decompress(k_compressed, v_compressed)
工程经验:HCA 短序列(< 4K)比 Full Attention 慢 12%。原因:Compressor 本身有计算开销,短序列时开销比省的时间还大。我们的做法:前两层用 Window Attention(sliding_window=128),中间层按序列长度动态选 CSA 或 HCA。
三条路径对比
| 方案 | 显存节省 | 精度损失 | 适用场景 |
|---|---|---|---|
| INT8 量化 | 50% | < 0.5% | 通用场景 |
| PagedAttention | 提升利用率 33pp | 0% | 多请求、变长序列 |
| CSA/HCA 压缩 | 85%-99% | < 2% | 超长序列(> 32K) |
组合使用:
INT8 量化 + PagedAttention:显存省 50%,碎片率 < 5%
INT8 + PagedAttention + HCA:128K 序列 KV Cache 57MB
实测性能数据
Qwen2.5-7B,910B 单卡:
| 优化方案 | KV Cache | batch | 吞吐 (tokens/s) |
|---|---|---|---|
| FP16(基准) | 12GB | 8 | 72 |
| INT8 量化 | 6GB | 16 | 112 |
| INT8 + PagedAttention | 6GB | 32 | 147 |
| INT8 + PagedAttention + batch=48 | 9GB | 48 | 138(swap) |
batch=32 时吞吐最高。batch=48 时 KV Cache 开始 swap 到 Host 内存,吞吐反而掉。
DeepSeek-V4 Flash,950DT(16 卡,128K 序列):
| 方案 | KV Cache | TPOT |
|---|---|---|
| Full Attention | 7.3GB | > 80ms |
| CSA(4 倍压缩) | 1.8GB | 25ms |
| HCA(128 倍压缩) | 57MB | < 10ms |
踩坑实录
坑 1:前 4 层量化影响精度
前几层捕获基础特征,量化误差会放大。解决:前 4 层用 FP16,后面用 INT8。
坑 2:PagedAttention 的 block_size 选错
block_size=8 管理开销大,block_size=32 碎片多。最优值:16。
坑 3:HCA 短序列反而慢
seq < 4K 时,Compressor 开销比省的时间大。解决:短序列用 Full Attention 或 Window Attention。
坑 4:batch 开太大导致 swap
batch=48 时 KV Cache swap 到 Host 内存,HBM 带宽利用率掉到 40%。解决:监控显存,batch 不超过 32。
https://atomgit.com/cann/ops-transformer
https://atomgit.com/cann/ascend-transformer-boost
https://atomgit.com/cann/cann-recipes-infer
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)