本节导读:批处理是vLLM高吞吐的根基。本节深入Continuous Batching的调度机制,逐个解析关键参数的含义、默认值与调优方法,并给出一套"压测→定位→调整"的标准调优流程。
max_num_batched_tokens、max_num_seqs 等核心参数的调优方法传统静态批处理要等一批请求全部生成完才能释放,短请求被长请求拖累,GPU大量空转。vLLM的Continuous Batching按iteration粒度调度:每个step重新组批,已完成的请求立刻退出、新请求立刻插入,配合PagedAttention的块级KV管理,实现接近理论峰值的利用率。
两者混跑时互相干扰:大prompt的prefill会阻塞decode,推高TPOT(每token延迟)。Chunked Prefill把长prefill切成小块分步执行,牺牲少量TTFT换取decode流畅,是延迟敏感场景的标配。
| 参数 | 默认值 | 作用 | 调优方向 |
|---|---|---|---|
max_num_batched_tokens |
2048(开chunked prefill时8192) | 每step最多处理的token数 | 吞吐不足→调大;TTFT超标→调小 |
max_num_seqs |
256 | 同时运行的序列数上限 | 并发场景调大,显存紧张调小 |
max_model_len |
模型上限 | 单请求最大长度 | 按业务真实分布下调,释放KV显存 |
enable_chunked_prefill |
近版本默认开 | 长prefill分块执行 | 在线服务保持开启 |
scheduler_delay_factor |
0.0 | 攒批延迟因子 | 纯吞吐场景可上调至0.3~0.6 |
调参前先有数据。用vLLM自带工具测出基线:
python -m vllm.entrypoints.cli.benchmark.serve \ --model Qwen/Qwen2.5-7B-Instruct \ --dataset-name sharegpt --dataset-path sharegpt.json \ --num-prompts 500 --request-rate 10
关注四个指标:TTFT(首token延迟)、TPOT/ITL(后续token延迟)、吞吐量(output tok/s)、P99。没有基线,后续任何调整都无法判断好坏。
curl -s http://localhost:8000/metrics | grep -E "num_requests_(running|waiting)"
num_requests_waiting 持续堆积、running打满 max_num_seqs → 并发上限瓶颈,优先调大 max_num_seqsmax_num_batched_tokensvllm serve Qwen/Qwen2.5-7B-Instruct \ --max-num-batched-tokens 16384 \ --max-num-seqs 512 \ --max-model-len 8192 \ --gpu-memory-utilization 0.92
每次只改一组参数,跑同样的压测脚本,记录指标变化。推荐用表格管理实验:
| 实验 | max_batched_tokens | max_num_seqs | TTFT P99 | 吞吐 |
|---|---|---|---|---|
| 基线 | 8192 | 256 | 1.8s | 1200 |
| +tokens | 16384 | 256 | 1.2s | 1650 |
| +seqs | 16384 | 512 | 1.3s | 1780 |
显存不足时调度器会preempt部分序列(丢弃KV Cache,之后重算),日志特征为Sequence group ... is preempted。频繁preemption意味着吞吐白白损失:
max_num_seqs,减少同时在跑的序列max_model_len 到业务真实需要的长度某RAG客服场景:输入约2000 token、输出约300 token、峰值并发300:
vllm serve Qwen/Qwen2.5-7B-Instruct \ --max-model-len 4096 \ --max-num-seqs 320 \ --max-num-batched-tokens 8192 \ --enable-chunked-prefill \ --gpu-memory-utilization 0.9
决策逻辑:输入长输出短→TTFT是主要矛盾→保持chunked prefill并给足token预算;并发已知峰值300→max_num_seqs 略高于峰值即可;max_model_len 按"输入+输出+余量"设4096,省下的显存全部换成KV Cache容量。
Q1:max_num_batched_tokens 是不是越大越好?
不是。它是每step的计算预算,调大能提高吞吐和降低TTFT,但超过GPU单step算力后收益递减,且会拉长单step耗时、恶化decode延迟。经验值:A100/H100上8192~32768之间扫参。
Q2:chunked prefill开了反而吞吐下降,为什么?
chunked prefill偏向降低TPOT,吞吐上本身有轻微开销。纯离线批处理场景(只要总吞吐不在乎延迟)可以关闭,换取更大的整段prefill。
Q3:max_num_seqs 设得远高于实际并发,有坏处吗?
有。它决定了调度器允许的排队在跑序列数,过高会让大量序列进入运行态争抢KV Cache,引发preemption连锁反应。按真实峰值并发×1.2设置即可。
Q4:离线批量推理该用什么参数?
离线场景用 LLM 类 + 大 max_num_seqs(如1024)+ 一次喂入全部prompt,让continuous batching自动组批;gpu_memory_utilization 可直接给到0.95,因为没有并发请求需要预留缓冲。
Q5:如何观测每个step的调度行为?vllm serve 加 --otlp-traces-endpoint 可输出trace;更简单的办法是周期抓取 /metrics 中 num_requests_running、num_requests_waiting、token_usage 三个指标画曲线。
max_model_len 设成模型上限(如128K)"以备不时之需"——KV Cache按最大长度预留会浪费大量显存,宁可按95分位长度设小;max_model_len → gpu_memory_utilization → max_num_seqs → max_num_batched_tokens",先释放显存再谈并发;本节解析了vLLM批处理调优的核心:理解prefill/decode的资源矛盾,用压测数据识别瓶颈类型,再对症调整 max_num_batched_tokens 与 max_num_seqs。记住"有基线、单变量、做记录"三原则。下一节把视角从单卡扩展到多GPU并行。