大模型推理加速硬核实践:从 KV Cache 到 Continuous Batching 的性能优化
前言
很多人接触大模型应用时,关注点通常放在 Prompt、RAG、Agent 或微调上。但当系统真正上线后,一个非常现实的问题会迅速暴露出来:
为什么模型回答这么慢?
为什么 GPU 利用率很低但请求还是排队?
为什么并发一上来,显存立刻爆掉?
为什么同样是 7B 模型,别人 QPS 比我高几倍?
这些问题背后对应的是一个非常具体的 AI 细分方向:大语言模型推理优化(LLM Inference Optimization)。
本文不讨论大模型训练,而聚焦于推理阶段,尤其是在线服务场景下的核心优化技术:KV Cache、PagedAttention、Continuous Batching、Prefill/Decode 分离、量化推理。这些技术是 vLLM、TensorRT-LLM、TGI 等推理框架的底层关键,也是企业部署大模型服务时必须理解的硬核内容。
一、大模型推理为什么慢?
大语言模型的生成过程和普通分类模型不同。分类模型通常是一次前向传播得到结果,而大模型生成文本是一个 自回归过程:
text
输入 Prompt -> 预测第 1 个 tokenPrompt + 第 1 个 token -> 预测第 2 个 tokenPrompt + 第 1、2 个 token -> 预测第 3 个 token...
也就是说,模型每生成一个 token,都要执行一次推理。
假设用户输入 1000 个 token,模型生成 500 个 token,那么整个请求可以拆成两个阶段:
- Prefill 阶段:处理输入 Prompt,计算所有输入 token 的上下文表示
- Decode 阶段:逐 token 生成,每次只生成一个 token
在实际服务中,Prefill 通常计算量大,Decode 通常访存压力大,并且 Decode 会持续占用 GPU 资源。很多线上推理瓶颈不是单纯的算力不足,而是计算、显存、访存和调度之间没有协调好。
二、KV Cache:避免重复计算注意力
Transformer 的核心是 Self-Attention。对于每一层,每个 token 都会生成 Query、Key、Value:
text
Q = XWqK = XWkV = XWv
在自回归生成中,新 token 只需要和历史 token 做注意力计算。问题是,如果每一步都重新计算历史 token 的 K/V,成本会非常高。
因此推理框架会使用 KV Cache:
把历史 token 在每一层的 Key 和 Value 缓存下来,后续生成时直接复用。
没有 KV Cache 时,生成第 t 个 token 需要重新计算前面所有 token 的 K/V;有了 KV Cache 后,只需要计算当前 token 的 K/V,然后和缓存中的历史 K/V 做 attention。
KV Cache 可以显著降低重复计算,但它也带来了新的问题:显存占用巨大。
三、KV Cache 显存到底有多大?
KV Cache 的显存消耗可以粗略估算:
text
KV Cache大小 ≈ batch_size × seq_len × num_layers × hidden_size × 2 × bytes
其中:
2表示 Key 和 Valuebytes表示数据类型大小,例如 FP16 是 2 字节seq_len包含输入 token 和已生成 tokennum_layers是模型层数
以一个 7B 模型为例,假设:
- 32 层
- hidden size 4096
- FP16
- batch size 16
- 每个请求上下文长度 4096
KV Cache 大小大约是:
text
16 × 4096 × 32 × 4096 × 2 × 2 bytes≈ 34GB
这还只是 KV Cache,不包括模型权重、激活、临时 buffer 等。
因此在大模型推理服务中,显存不是只被权重占满,KV Cache 往往才是并发能力的关键瓶颈。
四、传统 Batch 推理的问题
为了提高 GPU 利用率,推理服务通常会把多个请求合并成 batch。
普通 batch 的问题在于:不同请求长度不一样。
例如:
text
请求A:输入 100 token,生成 50 token请求B:输入 3000 token,生成 800 token请求C:输入 500 token,生成 100 token
如果简单 batch 到一起,会出现严重的 padding 浪费。短请求要等长请求,GPU 计算很多无效 token。
更严重的是,大模型 Decode 是逐 token 生成。一个 batch 中只要有请求还没生成完,batch 调度就会被拖住,导致吞吐下降、延迟上升。
五、Continuous Batching:动态批处理
为了解决传统 batch 的低效问题,现代推理框架引入了 Continuous Batching,也叫动态批处理或迭代级批处理。
它的核心思想是:
每一个 decode step 都可以动态加入新请求、移除已完成请求。
传统 batching 更像这样:
text
凑一批请求 -> 一起跑完 -> 再处理下一批
Continuous Batching 则是:
text
每一步 decode: 移除已完成请求 加入新等待请求 对当前活跃请求执行一次生成
这样做的优势非常明显:
- GPU 不会因为短请求结束而空转
- 新请求不用等整批老请求全部结束
- 吞吐量显著提升
- 请求延迟更加稳定
vLLM、TGI 等框架都大量使用了类似机制。
六、PagedAttention:像操作系统一样管理 KV Cache
KV Cache 最大的问题是连续显存分配困难。
传统方式通常为每个请求预留一整段连续 KV Cache 空间。但用户实际生成长度不可控,如果预留太少会不够,预留太多会浪费。
vLLM 提出的 PagedAttention 借鉴了操作系统虚拟内存分页思想:
- 把 KV Cache 切成固定大小的 block
- 每个请求不需要连续显存
- 通过 block table 维护逻辑 token 到物理 block 的映射
- 需要多少分配多少,释放也更灵活
这带来两个关键收益:
1. 降低显存碎片
多个请求的 KV Cache 可以分散存储,不要求连续空间,显存利用率更高。
2. 支持更高并发
由于不再为每个请求过度预留空间,同样显存可以容纳更多活跃请求。
简单理解:
text
传统KV Cache:每个请求占一大块连续空间PagedAttention:每个请求由多个小block组成
这就是为什么 vLLM 在很多场景下吞吐比传统 HuggingFace Transformers 推理高很多。
七、Prefill 和 Decode 的性能特征不同
LLM 推理可以分为两个阶段:
1. Prefill 阶段
Prefill 处理完整输入 Prompt。它的特点是:
- token 数较多
- 矩阵计算密集
- GPU 算力利用较高
- 更像训练中的前向传播
2. Decode 阶段
Decode 每次只生成一个 token。它的特点是:
- batch 中每个请求每步只算一个 token
- 计算粒度小
- 需要频繁读取 KV Cache
- 更容易受显存带宽限制
因此优化策略也不同:
- Prefill 要关注大矩阵计算效率
- Decode 要关注 KV Cache 访问效率和调度效率
一些高性能推理系统会进一步做 Prefill/Decode 分离,让不同 GPU 或不同执行队列分别处理两类任务,以提高资源利用率。
八、量化推理:降低权重和 KV Cache 成本
量化是推理优化中最常用的手段之一。常见量化方式包括:
- FP16 / BF16
- INT8
- INT4
- GPTQ
- AWQ
- SmoothQuant
- FP8
量化的核心目标是:
- 减少模型权重显存占用
- 提高推理吞吐
- 降低部署成本
例如一个 7B 模型:
- FP16 权重大约 14GB
- INT8 大约 7GB
- INT4 大约 3.5GB
但要注意,量化不一定总是带来线性加速。原因包括:
- GPU 对某些低比特算子支持不够好
- 反量化开销可能抵消收益
- 量化会影响模型精度
- KV Cache 仍可能是瓶颈
在长上下文、高并发场景中,即使权重量化到 INT4,KV Cache 仍然可能占用大量显存。因此更高级的系统还会考虑 KV Cache 量化,例如把 KV Cache 从 FP16 降到 INT8 或 FP8。
九、吞吐和延迟:两个经常冲突的指标
部署大模型服务时,常见指标包括:
- TTFT:Time To First Token,首 token 延迟
- TPOT:Time Per Output Token,每个输出 token 的平均耗时
- Throughput:吞吐量,tokens/s 或 requests/s
- GPU Utilization:GPU 利用率
- Max Concurrency:最大并发数
这里有个典型矛盾:
batch 越大,吞吐通常越高,但单请求延迟可能越高。
如果系统为了凑大 batch 等待太久,TTFT 会变差;如果 batch 太小,GPU 利用率又上不去。
所以线上服务通常需要设置:
text
max_batch_sizemax_num_batched_tokensmax_waiting_timemax_model_lengpu_memory_utilization
这些参数没有绝对最优值,必须结合业务场景压测。
十、一个实际部署建议
如果你要部署一个企业内部大模型推理服务,可以按如下路线:
1. 小规模验证
先用 vLLM 或 TGI 部署模型:
bash
python -m vllm.entrypoints.openai.api_server \ --model /models/Qwen2.5-7B-Instruct \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.9 \ --max-model-len 8192
2. 做压测
重点测试:
- 并发 1、4、8、16、32 下的 TTFT
- 平均输出速度 tokens/s
- GPU 显存占用
- 不同 prompt 长度下的吞吐
- 不同生成长度下的稳定性
3. 优化参数
根据结果调整:
max_num_batched_tokensmax_num_seqsmax_model_len- 量化方式
- tensor parallel
- 是否启用 prefix cache
4. 上线监控
至少监控:
- 请求排队时间
- 首 token 延迟
- 输出 token 速度
- OOM 次数
- GPU 显存利用率
- 每分钟 tokens 数
十一、总结
大模型推理优化是 AI 工程化中非常硬核的细分领域。它不是简单地“把模型加载起来然后开接口”,而是涉及 Transformer 结构、显存管理、GPU 调度、batch 策略、量化算子和在线服务稳定性。
核心结论可以总结为:
- KV Cache 是大模型推理的关键优化,也是显存瓶颈来源
- Continuous Batching 可以显著提高在线服务吞吐
- PagedAttention 通过分页机制提升 KV Cache 管理效率
- Prefill 和 Decode 性能特征不同,需要区别优化
- 量化能降低部署成本,但不一定解决所有瓶颈
- 线上系统必须关注 TTFT、TPOT、吞吐、并发和显存稳定性
如果说 RAG 解决的是“模型如何接入知识”,那么推理优化解决的是“模型如何低成本、高并发、稳定地服务用户”。对于想深入 AI 基础设施方向的开发者来说,LLM 推理优化绝对是一个值得长期投入的硬核赛道。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)