前言
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

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐