大模型推理 Batch Size 如何调优?
简化版
大模型推理没有一个固定的最佳 Batch Size。增大 Batch 能摊薄权重读取和 Kernel 启动,提高吞吐;但也会增加凑批等待、单轮执行时间、KV Cache 占用和尾延迟,最终还可能 OOM。
调优时应先定 TTFT、TPOT、P99 和显存水位等 SLO,再用真实输入/输出长度分布逐级提高并发与 Token 预算,选择“仍满足 SLO 的最高有效吞吐点”。在线生成优先使用连续批处理,并按 Token 而不是只按请求数限制批次。
详细版
Batch Size 在 Prefill 和 Decode 中含义不同。Prefill 一次处理的总输入 Token 决定计算规模;Decode 更关心同时活跃的序列数及其 KV 长度。一个 batch=8 可能是八条 128 Token,也可能是八条 16K Token,资源差异巨大。
建议同时调四类上限:单轮 Prefill Token、活跃序列数、总缓存 Token/KV 块和调度等待窗口。压测时从低并发逐档提高,记录 Requests/s、输入/输出 Tokens/s、TTFT、TPOT、P95/P99、OOM、拒绝率与单位成功成本。
最终配置应位于吞吐—延迟曲线的拐点之前,并为流量波动、长尾请求和单卡故障留余量。聊天、离线批量与混合流量应分别设定配置,不能把离线峰值吞吐参数直接用于交互服务。
完整版教学
1. Batch Size 在 LLM 推理中是什么
传统模型常把 Batch Size 理解为一次前向的样本数。LLM 在线服务中,请求长度和生成进度不同,连续批处理会在每个迭代动态加入或移出序列,因此它更像一组资源预算。
batch controls = active sequences
+ prefill tokens per iteration
+ cached tokens / KV blocks
+ scheduling wait
只汇报 max_batch_size=64 无法说明服务实际负载。
2. 为什么增大 Batch 通常提高吞吐
单请求 Decode 每步计算很小,却要读取模型权重。多个序列一起计算,可以让同一次权重搬运服务更多 Token,并形成更适合 GPU 的矩阵形状。
在低 Batch 区间,吞吐通常随 Batch 快速增加;硬件逐渐饱和后,收益变小。此后继续加 Batch 只会扩大 KV 读取、单轮耗时和排队。
3. 为什么延迟会随 Batch 恶化
系统可能等待请求凑批,这段时间直接计入 TTFT。批次更大时单轮 Kernel 也更久,Decode 中每个请求要更久才能轮到下一 Token,TPOT 随之上升。
TTFT = queue_wait + prefill_time + first_token_transfer
TPOT ≈ decode_iteration_time(batch, kv_lengths)
因此吞吐最佳点与延迟最佳点通常不是同一个配置。
4. Prefill 批次该看什么
Prefill 计算受总 Token 和各序列长度影响。Padding 实现还会浪费到批内最大长度,长度分桶或无 Padding Kernel 能改善利用率。
应设置 max_prefill_tokens_per_batch,避免一组超长 Prompt 独占一次调度。必要时用 Chunked Prefill,把长输入拆开并与在线 Decode 穿插。
5. Decode 批次该看什么
Decode 每个活跃序列通常贡献一个新 Token,但历史 KV 长度各不相同。更大活跃批次能提高权重复用,却也增加 KV 带宽和容量压力。
除了序列数,还要限制总缓存 Token 和每轮预计计算时间。短会话与长会话混合时,单一序列数上限会产生不稳定 TPOT。
6. 为什么要采用连续批处理
静态批次必须等最长输出结束,已完成槽位会空闲。连续批处理在迭代边界移出完成请求并补入新请求,使有效 Batch 更稳定。
iteration 1: A B C D
iteration 2: A B C D
iteration 3: A E C D # B 完成,E 加入
调优对象因此不只是“批大小”,还包括补入策略、优先级和每轮 Token 预算。
7. 显存约束如何估算
可用显存先扣除模型权重、运行时工作区、CUDA Graph 和安全余量,剩余部分用于 KV。每条序列的 KV 与当前 Token 数近似线性相关。
KV bytes ≈ 2 × layers × kv_heads × head_dim
× cached_tokens × bytes_per_element
最大 Batch 应通过最坏或高分位长度组合估计,而不能用平均长度把缓存需求压低。
8. 调参实验怎样设计
固定模型、量化、硬件和请求回放,选择一组 Batch/Token 预算阶梯,例如活跃序列 4、8、16、32、64。每档先预热,再持续运行到指标稳定。
| 指标 | 用途 |
|---|---|
| Requests/s | 业务完成能力 |
| Output Tokens/s | Decode 总吞吐 |
| TTFT P95/P99 | 首次响应体验 |
| TPOT P95/P99 | 流式生成流畅度 |
| KV 水位/OOM | 容量安全 |
| 成功任务成本 | 综合效率 |
不要把不同输出长度的 Requests/s 直接比较,应保持同一流量样本或同时报告 Token 指标。
9. 如何识别吞吐—延迟拐点
随着 Batch 增加,吞吐先上升,随后趋于平台;尾延迟通常先缓慢、再快速恶化。吞吐边际收益明显下降且 P99 开始陡升的位置,就是饱和区入口。
线上配置应选在入口之前。例如 Batch 从 32 增到 48 只提升 4% 吞吐,却让 P99 TTFT 增长 40%,通常不值得。
10. 不同业务为何要分配置
实时聊天强调低 TTFT 和稳定 TPOT,适合较小等待窗口与保守水位;离线摘要可接受排队,适合大 Token Batch;高风险 API 还可能按截止时间和优先级调度。
混合业务最好分实例池。若必须共享,应设置优先级、租户配额和最低服务份额,防止离线大批次挤压在线请求。
11. 调度等待窗口怎么选
等待几毫秒可以收集更多请求,但低流量时可能白白增加延迟。固定窗口难以覆盖全天流量,通常根据队列深度和 SLO 自适应。
高峰时请求自然形成批次,可少等;低峰时若交互延迟重要,应立即执行。离线队列则可以积累到 Token 阈值或最长等待时间再发车。
12. CUDA Graph 与固定形状的影响
CUDA Graph 能减少 CPU 调度和 Kernel 启动开销,但常需要为若干批次形状预先捕获并保留内存。形状过多会增加图缓存和维护成本,形状过少则产生 Padding 或回退到 eager。
调参时要确认目标 Batch 是否命中已捕获形状,并把图内存算入预算。否则 Benchmark 与线上形状分布不同,会得出错误结论。
13. 上线如何持续调整
记录队列 Token、活跃序列、KV 使用率、TTFT/TPOT 分位数、取消与拒绝率。自动调节只能在预先验证的安全区间内进行,并设冷却时间避免参数来回震荡。
模型、量化或硬件变更后必须重新标定。旧模型的最优 Batch 不能直接复制到新模型,因为 KV 头数、算子效率和权重占用可能都不同。
Batch 调优不是寻找一个最大的整数,而是为具体流量和 SLO 找到可长期维持的资源工作点。
14. 常见误区与追问
- 误区:Batch 越大 GPU 利用率越高,所以越好。 饱和后吞吐收益有限,尾延迟和 OOM 风险继续增加。
- 误区:只限制请求数即可。 LLM 请求长度差异使 Token 和 KV 预算更关键。
- 误区:Prefill 与 Decode 使用同一批次逻辑。 两个阶段的计算形态和约束不同。
- 误区:平均长度足够做显存规划。 长尾组合决定 OOM 与 P99。
- 误区:离线峰值吞吐就是线上容量。 在线服务还受排队、取消和延迟 SLO 约束。
- 追问:低流量为什么不该等大 Batch? 很可能凑不满,等待只会增加 TTFT。
- 追问:何时停止增加 Batch? 吞吐边际收益变小、P99 陡升或安全水位逼近时。
15. 加强记忆
- 先记目标:SLO 内最大化有效吞吐。
- 再分阶段:Prefill 看输入 Token,Decode 看活跃序列和 KV。
- 再记收益:大 Batch 摊薄权重读取和启动开销。
- 再记代价:凑批等待、单轮变长、显存上涨。
- 再记实验:真实长度回放,逐档加压,观察拐点。
- 再记配置:序列、Prefill Token、KV 和等待窗口四类上限。
- 最后记上线:留容量余量,按业务分池,版本变化重新标定。