深色模式
推理性能调优:batch / 并发 / 吞吐
摘要:推理性能不是"越大越好",而是吞吐(tokens/s、req/s)与延迟(TTFT、TPOT、P99)之间的工程权衡。本文给出 vLLM 的关键旋钮(
max-num-seqs、max-num-batched-tokens、gpu-memory-utilization、max-model-len、chunked prefill)、压测方法,以及如何读指标判断瓶颈。适用版本:vLLM 0.8.x–0.9.x,参数以官方文档为准。
适用版本与前提
- 引擎:vLLM(示例命令);TGI/SGLang 思路相通
- 指标:TTFT、TPOT/ITL、吞吐、P50/P99、KV 利用率、抢占次数
- 读者已了解连续批处理与 KV Cache(见本目录对应文章)
核心概念:吞吐与延迟为什么打架
自回归生成分两个阶段:
- Prefill:一次性处理整段输入 prompt,算力密集,决定 TTFT。
- Decode:逐 token 生成,内存带宽密集,决定 TPOT/体感速度。
增大 batch(同时服务的请求数)能:① 把 decode 阶段的计算/带宽拼满,提升吞吐;② 但让每个请求的 TTFT 变长、TPOT 变慢。所以调优本质是按场景定目标:聊天看 P99 延迟,离线批量看总吞吐。
关键旋钮(以 vLLM 为例)
| 参数 | 作用 | 调大/调小的影响 |
|---|---|---|
--max-num-seqs | 同时运行的最大请求数 | 大→吞吐高、延迟升;小→延迟低、吞吐降 |
--max-num-batched-tokens | 单步 batch 内 token 上限(含 prefill+decode) | 限制单步规模,影响 TTFT 与步耗时 |
--gpu-memory-utilization | KV Cache 可用显存比例 | 大→并发上限高;过大会挤压权重/碎片致 OOM |
--max-model-len | 上下文长度上限 | 决定 KV 池理论峰值,过大浪费、过小截断 |
| chunked prefill | 把长 prefill 分块融入 decode 步 | 降低长 prompt 对 TTFT 的尖刺 [版本相关] |
| 量化(FP8/AWQ) | 减小权重与 KV 精度 | 省显存→更大 KV 池→更高并发 |
先定位瓶颈再调
看 vLLM 指标 gpu_cache_usage_sys:① 长期 <50% 且 GPU 利用率低 → 并发/请求量不够,加流量或调大 batch;② 接近 100% 且频繁 preempted → KV 池满,需要量化、降 --max-model-len 或加卡;③ TTFT 高但吞吐低 → 看 prefill 是否被长请求阻塞,开 chunked prefill。
生产调优步骤
bash
# 基线:固定一个配置先跑通
docker run --gpus all --ipc=host -p 8000:8000 \
-v /mnt/models:/models \
vllm/vllm-openai:latest \
--model /models/Qwen2.5-7B-Instruct \
--max-model-len 32768 \
--gpu-memory-utilization 0.90 \
--max-num-seqs 256 \
--max-num-batched-tokens 8192
# [版本相关] 以上为 0.8.x–0.9.x 区间常见参数名;请以官方文档核对1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
bash
# 压测:用官方 benchmark(或自写并发客户端)
# 方式一:vLLM 自带 benchmark_serving
python benchmarks/benchmark_serving.py \
--model /models/Qwen2.5-7B-Instruct \
--backend openai \
--dataset-name random \
--num-prompts 1000 \
--request-rate 32 \
--shared-results-dir ./results
# 方式二:固定 QPS 的并发请求,记录 TTFT/P99
# [未实测] benchmark 脚本路径与参数随版本演进,请查 vLLM 仓库 benchmarks 目录1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
bash
# 推荐:逐步扫描 request-rate,画出"吞吐-延迟"曲线
for r in 8 16 32 64 128; do
python benchmarks/benchmark_serving.py \
--request-rate $r --num-prompts 1000 \
--save-result --result-dir ./sweep
done
# 解读:吞吐随 rate 上升→平台期;P99 延迟在拐点处开始陡增,拐点即"舒适并发"1
2
3
4
5
6
7
2
3
4
5
6
7
读指标判断该调什么
验证
- 同一份模型、同一份流量分布,固定其它参数只动一个旋钮,记录 P50/P99 TTFT、TPOT、吞吐(tokens/s)、GPU 利用率、KV 利用率。
- 用 Grafana 看线上真实曲线,而非只看压测。
- 量化后务必做质量回测(accuracy/困惑度/业务样例),确认精度损失可接受。
回滚与清理
调参也要灰度
max-num-seqs / gpu-memory-utilization 调大可能触发 OOM 或长尾恶化。变更前记录旧值,网关灰度 5%–10% 流量观察,异常回退旧值。不要在高峰期一次性推全量。
故障排查
| 现象 | 原因 | 处理 |
|---|---|---|
| 吞吐上不去、GPU 空闲 | 并发不足 / batch 太小 | 提高 --max-num-seqs、加压测流量 |
| P99 延迟陡增 | 超过舒适并发 | 降并发上限或限流 |
| OOM | 显存利用率过高 | 降 --gpu-memory-utilization 或 --max-model-len |
| TTFT 尖刺 | 长 prompt 阻塞 | 开 chunked prefill、限制单请求输入长度 |
| 量化后质量掉 | 量化误差 | 换更高质量量化(如 Q4_K_M→Q5)、或回退 fp16 |
安全与合规
- 成本失控:把并发/
max_tokens/输入长度上限放在网关强制,而不是信任客户端。无限制 + 长输出 = GPU 被单用户占满。 - 越权:API 前置鉴权,禁止直连。
- 公平性:多租户用命名空间/独立副本 + 配额,避免大请求饿死小请求。
成本与性能(示意)
以 Qwen2.5-7B-Instruct(fp16,单卡 A100 80GB)为例:
| 策略 | GPU | 估算单价[未实测] | 并发 | 说明 |
|---|---|---|---|---|
| 仅 fp16、默认 | A100 80GB ×1 | ~¥8–15/h | 数十 | 基线 |
| 量化 AWQ/FP8 | A100 80GB ×1 | ~¥8–15/h | 数十~上百 | KV 池更大,吞吐更高 |
| 拉满 batch 离线 | A100 80GB ×1 | ~¥8–15/h | 高 | TPOT 升但单位 token 成本最低 |
成本优化顺序(不要先加卡)
- 量化省显存;2) 把 GPU 利用率/KV 利用率拉到 80%+(调 batch/并发);3) 压测定舒适并发并限流;4) 最后才加卡或加副本。多数成本浪费在"卡买了但利用率只有 30%"。