LLM 推理性能优化:从 vLLM 到投机解码的工程实践
LLM 推理性能优化:从 vLLM 到投机解码的工程实践
关键词:LLM 推理、vLLM、PagedAttention、量化、投机解码、KV Cache
1. 为什么要关心推理性能
训练 LLM 是少数大公司的游戏,但推理(Inference)是每一个落地团队都绕不开的成本中心。一个真实的数字:
- 一张 A100-80G,跑 13B 模型 FP16,朴素 HuggingFace pipeline 大约只能稳定支撑5~10 QPS。
- 同一张卡,用 vLLM + 量化 + 投机解码,可以稳定到40~60 QPS。
5~6 倍的差距,全部来自工程优化。本文系统梳理这些手段。
2. 先看瓶颈:LLM 推理为什么慢
LLM 推理分两阶段:
| 阶段 | 计算特性 | 瓶颈 |
|---|---|---|
| Prefill(处理 prompt) | 计算密集 | 算力(FLOPs) |
| Decode(逐 token 生成) | 访存密集 | 显存带宽 |
绝大多数线上场景,输入长、输出短,但总耗时 70%+ 花在 Decode 上。Decode 阶段每生成 1 个 token,都要把整个模型权重从显存搬一遍——这就是为什么 LLM 推理是带宽 bound而不是算力 bound。
理解这一点,所有优化手段都能归类:
- 降低权重大小→ 量化
- 减少搬运次数→ Batching、KV Cache 优化
- 减少生成步数→ 投机解码、并行解码
3. KV Cache:理解一切优化的基石
Transformer 自回归生成时,每一步只新增 1 个 token,但需要它和前面所有 token 做 Attention。如果每次都重算,复杂度是 O(n²)。
KV Cache把历史 token 的 Key 和 Value 缓存下来,每步只算新 token 的 K/V,复杂度降到 O(n)。
但 KV Cache 本身特别吃显存:
KV_Cache_Size = 2 (K和V) × num_layers × num_heads × head_dim × seq_len × batch × dtype_size以 LLaMA-13B、seq_len=2048、batch=16、FP16 为例,KV Cache 单独占用约26 GB——比模型权重还大。
显存利用率往往不是被模型撑爆的,而是被 KV Cache 撑爆的。
4. vLLM 与 PagedAttention:把显存当虚拟内存管
传统框架给每个序列预分配max_seq_len大小的连续显存——一旦短请求多,显存碎片严重,利用率不到 30%。
PagedAttention(vLLM 的核心创新)借鉴 OS 虚拟内存:
- KV Cache 切成固定大小的block(如 16 个 token)。
- 每个序列维护一张block table,逻辑连续物理离散。
- 不同序列可以共享 block(如 prefix caching、beam search)。
收益:
- 显存利用率从 ~30% 提升到>90%。
- 同一张卡能容纳的并发请求数翻倍以上。
- 系统 prompt 相同时,prefix block 可全局复用,prefill 时间几乎为 0。
实践建议:除非你有特殊原因,否则 LLM 服务直接用 vLLM 起步,不要自己写 generate 循环。
5. Continuous Batching:吞吐的秘诀
朴素的 batching(Static Batching):把 N 个请求凑齐再一起跑,跑完一起返回。问题:
- 短请求要等长请求一起结束(队头阻塞)。
- 凑批等待引入额外延迟。
Continuous Batching(也叫 in-flight batching):
- 每个 decode step 之后,已完成的序列立刻退出 batch。
- 新到达的请求随时插入空位,立即开始 prefill。
效果:吞吐提升 2~3 倍,尾延迟显著下降。vLLM、TGI、TensorRT-LLM 都内置了这一机制。
6. 量化:用精度换带宽
量化把 FP16 权重压成 INT8 / INT4,访存量直接减半甚至 1/4。
6.1 主流方案对比
| 方案 | 精度 | 性能 | 部署难度 | 适用 |
|---|---|---|---|---|
| GPTQ | INT4 | 快 | 中 | 通用,社区生态成熟 |
| AWQ | INT4 | 快 | 中 | 比 GPTQ 精度损失更小 |
| SmoothQuant | INT8 | 中 | 低 | 精度敏感场景 |
| FP8 (H100) | FP8 | 极快 | 低 | 有 H100 直接用 |
6.2 实操经验
- 7B~13B 模型:INT4(AWQ)几乎无损,强烈推荐。
- 70B 以上:INT4 也能接受,但建议先做评测。
- 微调过的模型:量化前一定要重新跑一次业务评测集,不能只看 PPL。
6.3 KV Cache 量化
很多人忘记 KV Cache 也能量化。FP8 / INT8 KV Cache 在长上下文场景下,显存能再省 50%,对 32K+ context 应用是刚需。
7. 投机解码(Speculative Decoding):减少生成步数
Decode 阶段每步只产 1 个 token,无论模型多大都得跑一遍前向。投机解码用一个小模型先"猜"K 个 token,再用大模型并行验证:
小模型快速生成: t1', t2', t3', t4' 大模型一次性验证(一次前向,输出 5 个位置的 logits): - t1' 接受? → t2' 接受? → ... - 第一个被拒绝的位置开始重新生成由于大模型的一次前向能并行验证多个位置(计算密集而非访存密集),整体步数从 N 降到 ~N/3。
收益:通常2~3 倍 latency 提升,且输出与原大模型完全一致(采样意义下等价)。
变种:
- Medusa:不用小模型,给主模型加几个并行预测头。
- EAGLE:在特征空间投机,比 Medusa 接受率更高。
- Lookahead Decoding:完全无 draft 模型的并行解码。
实践中,对话类应用上 Medusa / EAGLE 性价比最高。
8. 系统层优化清单
除了模型层,系统层也有大量可挖空间:
- CUDA Graph:消除每个 step 的 kernel launch 开销,decode 阶段提速 10%~20%。
- FlashAttention-2/3:注意力算子重写,prefill 阶段性能翻倍。
- Tensor Parallel:单卡放不下时按列切分权重,跨 NVLink 通信。
- Pipeline Parallel:多机部署时按层切分,配合 micro-batch 隐藏通信。
- PD 分离(Prefill/Decode Disaggregation):把 prefill 和 decode 部署到不同节点,避免互相干扰,是 2024 年大型推理集群的趋势。
9. 一个真实的优化路径
我们对一个内部 13B 客服模型做的优化(A100-80G 单卡):
| 阶段 | 配置 | QPS | TTFT(ms) | 显存 |
|---|---|---|---|---|
| baseline | HF transformers FP16 | 6 | 800 | 28G |
| + vLLM | PagedAttention + ContBatch | 22 | 350 | 60G |
| + AWQ INT4 | 权重量化 | 38 | 280 | 22G |
| + FP8 KVCache | KV 量化 | 45 | 270 | 16G |
| + Medusa | 投机解码 | 62 | 270 | 18G |
10 倍提升,硬件没换。这就是工程优化的力量。
10. 选型建议
不同规模团队的推荐路径:
- 小团队 / 起步:直接用 vLLM 或 TGI,开 AWQ 量化,足够支撑前期 99% 的需求。
- 中型团队 / 自有模型:vLLM + 自定义调度 + Medusa head 微调,吞吐再提一档。
- 大型集群 / 高 SLA:TensorRT-LLM + PD 分离 + 多级缓存,配合自研调度系统。
11. 结语
LLM 推理优化的本质是“让显存带宽花在刀刃上”。所有手段——量化、Paging、投机解码——都是在回答同一个问题:
如何在每次访存中产出更多 token?
理解这一点,再面对铺天盖地的新论文与新框架,你就不会迷失在术语里。先用 vLLM 和量化拿到 5 倍提升,再考虑投机解码和 PD 分离,工程上够用很久了。
至此,《AI 应用工程化》三部曲完结:
- RAG:让模型有知识
- Agent:让模型能做事
- Inference:让模型跑得起、跑得快
希望对正在落地 AI 应用的你有所帮助。
