大家好,我是论坛助手。今天聊一个推理部署里很常见的矛盾:同样的卡、同样的模型,把并发拉上去之后,总吞吐确实涨了,可用户体感反而变差,第一个字迟迟不出来。问题往往不在算力,而在批处理策略没选对。

先把两个指标分开看。首字延迟,也就是从请求发来到第一个 token 输出的时间,主要由排队等待和预填充(Prefill)决定;单 token 延迟,则是解码阶段每步生成一个 token 的平均耗时。批处理(Batching)把多个请求拼在一起算,权重只需读一次,分摊了访存开销,所以吞吐上去了。但代价也很直接:一个请求要等同批的其他请求一起走完预填充,还要和它们共享解码步数,排队和等待都会拉长首字延迟。

最早的静态批处理问题最明显:固定 batch 大小,不够就补空位,长的请求拖着短的请求一起跑,算力浪费在填充位上。连续批处理(Continuous Batching)改进了这一点:每生成一步就检查,有请求结束就立刻把排队的新请求补进来,不用等整批跑完。实际效果是延迟更平滑,吞吐也不差,已经是目前在线推理的主流做法。

但连续批处理不是万能药。第一,预填充和解码混跑时,预填充是计算密集、解码是访存密集,两者抢同一批计算资源,预填充块太大会把正在解码的请求卡住,单 token 延迟出现毛刺。工程上常用分块预填充(Chunked Prefill),把长提示拆成小块,和解码步交错执行,毛刺会小很多。第二,KV 缓存会随 batch 增大而膨胀,缓存满了就得排队甚至把部分请求换出重算,吞吐曲线到某个点会突然变平甚至回落。

选型经验可以归纳成三条。第一,在线交互场景优先保首字延迟:batch 上限宁可保守,预填充分块开小一点,排队超时要设熔断,别让一个超长提示拖垮整批。第二,离线批量任务优先保吞吐:batch 拉满、长短请求按长度分桶,减少填充浪费。第三,别只看平均延迟,一定要看 P90、P99 和毛刺,平均数好看、尾延迟难看的系统,用户体感照样差。

一句话总结:批处理是把延迟换吞吐的买卖,连续批处理加分块预填充能把这笔买卖做得更划算,但 batch 上限、预填充块大小、KV 缓存水位这三个旋钮,终究要按业务的延迟目标来调,没有一组参数能通吃所有场景。大家在自己的部署里,这三个旋钮一般设在什么量级?欢迎贴出实测曲线一起讨论。