loading

Loading

探索与发现

首页 AI 工程化

动态拼批不是开个开关:线上拼批的取舍与门禁

分类:AI 工程化
字数: (2045)
阅读: (13)
0
摘要:在线服务里的动态拼批和离线批量推理是两码事。窗口时长、KV Cache 水位、批内参数对齐共同决定拼接能否成立。上线前该盯的是 p99 和显存峰值,而不只是吞吐。

“开启动态批处理,吞吐翻倍”——这句话在推理论坛和框架文档里出现得太频繁,以至于很多团队把它当成一个配置项。真实情况是:批处理策略的收益和风险都埋在对流量的假设里,上线那天崩的往往不是 GPU,而是尾延迟和显存。

这里得先把两个词分开:动态拼批(dynamic batching)和连续批处理(continuous batching)。不少文档把它们混着说,但工程上是两套逻辑。动态批处理是“凑一批再发车”:请求进来先进队列,队列满或者从第一个请求算起等够了时间窗口,就打包推给模型。连续批处理是迭代级调度:每处理一个 token 就重新算一遍批,老请求结束腾出显存,新请求立刻补位。前者按请求拼,后者按 token 拼。对生成式 LLM 来说,后者通常更合适;对图像分类、embedding 这类单次前向就出结果的模型,前者简单够用。

静态批次为什么会挨打

静态批处理是一次性凑固定数量的请求,然后等这一批全部跑完才收下一批。问题很直观:如果批里有三条请求生成三十个 token、一条生成两千个 token,GPU 在前三十个 token 之后就一直在等最长那条。短请求的用户早就拿到了想要的答案,却要等长请求跑完批才被释放,尾延迟就这么被拉长。文档里把它称为传统方案最致命的缺陷,不算夸张。

连续批处理解决的就是这个问题,但它也有代价。每步调度都意味着一次决策开销,超长请求会长期占用一个槽位,导致有效并发下降。实际系统里常见做法是给单条请求加最大生成长度或超时上限,防止个别“钉子户”请求把批的整体效率拖垮。

时间窗口是一个可调的赌注

动态拼批里,等待窗口几乎决定了全部体验。假设设成 20ms:正常情况下可以多凑几条请求,吞吐提升明显;但低流量时段,比如凌晨两点的客服机器人,第一批请求只有一条,也得等满 20ms 再走,钱是省了,用户的体感就是“这个模型好慢”。反过来设成 2ms,窗口几乎没有意义,队列里的请求数少,拼批拼接效果趋近于零。

更合理的做法是让窗口随负载自适应:队列里已有请求数达到阈值就立刻发车,不满足才启用兜底等待。同时对不同优先级的接口做队列隔离,内部批处理任务可以等,前台对话接口不该被同一套参数迁就。

显存水位比吞吐数字更值得盯

KV Cache 的碎片是另一个容易被忽而略的点。分块管理的方案会把显存切成固定大小的块,拆批之后新请求能利用碎片补齐,但块大小和预分配策略本身会决定水位。上线抽查清单里至少要包含:峰值并发下的显存占用、是否触发过重算或预占、调度循环在满负荷时单步耗时的分位数。等线上真的 OOM 再回头补门禁,就是一次故障的时间成本。

另一件常被忘记的事是拼批对齐。同一个批次里的请求是否使用相同模型、相同采样参数、相同的思考模式,直接影响能否合并做同一次前向。把温度、top_p 甚至是是否开启思考模式的请求硬归到一批,结果往往是要么回退到逐条跑,要么采样行为变得不可控。

两套批量体系别搞混

很多团队说的“批量推理”其实是另一个东西:异步离线作业。Gemini 的批量推理允许单个作业最多包含 200,000 个请求,异步处理,大多数作业在 24 小时内完成,过期后在队列中最长可保留 72 小时;容量饱和时请求排队,没有预定配额。阿里云百炼的批量推理则是提交 JSONL 文件,单文件不超过 50,000 个请求且不超过 500 MB,单行不超过 1 MB,异步处理,价格是实时推理的 50%,适用于模型评测、标注这类不需要立刻返回的作业。Amazon Bedrock 的批量推理同样走 S3 输入输出,并且明确不支持工具调用和结构化输出。

这些是“离线批量”,和在线服务里实时的动态拼批不是同一个优化目标。前者拿延迟换成本和配额;后者是在线流量的聚合调度。把离线作业的省钱结论套到在线服务头上,就会得到“多拼一批就好”的错觉。

上线门禁与观测

我倾向于把批次问题当成可以被量化和回滚的工程约束,而不是调参。至少需要观察三类指标:吞吐(每秒完成请求数、每秒生成 token 数)、尾延迟(p95 和 p99 的端到端时延、单步调度耗时)、以及资源(显存峰值、重算比例、等待队列长度)。三个维度任一出现波动,回退到更低风险的窗口和批上限是必须能一键完成的。

还有一个容易被忽视的取舍:动态拼批会增加请求在队列里的可等待时间,这对“看起来即时”的产品交互是消耗。如果前端没有排队感知,用户等三秒和五秒的差别就会被感知为“这个功能很卡”。工程上通常的做法是在结果分片前吐首 token,或者只对非首发请求做更大胆的拼接。

拼批本身不生产吞吐,它只是把闲置的算力捡起来。GPU 利用率、窗口时长和尾延迟之间的缺口,才是真正需要权衡的地方。

如果有稳定的低优先级流量,比如摘要、分类、数据清洗,可以大胆把拼接窗口放大、批次上限拉高;对前台对话接口,宁可少拼一些,也要把 p99 压住。两套参数、两套队列,比找一个“全站通用”的拼接设置靠谱得多。

转载请注明出处: 皮哈皮阿

本文的链接地址: https://blog.phpat.com/ai-engineering/622.html

手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

您可能对以下文章感兴趣
1 / 1
预览图