大模型推理批处理窗口与尾延迟权衡优化落地

日期:2026-09-22

一、项目背景

我们给一个内部推理服务做吞吐优化,最初思路很直接:把请求攒成一批再进模型,GPU 利用率上去了,单卡能扛的量翻了一倍多,成本看着很美。可上线没两天就被业务方投诉,说长尾请求偶尔要等很久才返回,超时告警刷屏。我们查监控才发现,为了攒够大批次,调度器把后面来的请求硬排队,等批满了才一起推理,排在队尾的请求等批时间远超预期,有时等批就耗掉好几秒。

问题本质是吞吐和尾延迟拧着劲。连续批处理(continuous batching)确实能提吞吐,但窗口策略不对,收益全压在尾部请求头上。我们后来复盘,平均延迟看着漂亮,但业务方体感的是 P99,那一刀切的大窗口把最慢的那批人坑惨了。

二、落地场景

这套推理服务主要接两类流量:一类是在线问答,对首字和尾延迟都敏感,用户盯着屏幕等;一类是离线批处理,只要总吞吐够,慢点无所谓。我们采用的私有化底座把连续批处理作为基础能力,但批处理窗口和排队策略可调。落地场景包括:在线请求走小窗口加优先级队列,尽量不等批;离线请求走大窗口,吃满算力;尾延迟超阈时自动缩小窗口并提升该批优先级;按模型实例分别配置,避免一刀切。调优的目标是让 P99 延迟可控,同时 GPU 利用率不掉太多,两者之间找平衡点。

三、关键技术挑战与解决思路

第一关是窗口大小的设定。窗口太大尾延迟爆炸,太小吞吐上不去,我们没拍脑袋,而是按历史请求间隔分布反推一个动态窗口:闲时放大、忙时收紧,忙时窗口收到在线请求不至于久等。第二关是优先级,长尾请求不能一直等,我们给队列加了优先级档位,高优先请求可以插队触发提前推理,哪怕批没满也先跑,这样尾部不被饿死。第三关是尾延迟可观测,之前只有平均延迟指标,根本发现不了长尾,我们补齐了 P95、P99 分位和等待耗时分布,把"等批耗时"单独打了埋点。

早期我们贪心把窗口拉到最大想冲吞吐,结果 P99 从八百毫秒飙到六秒,业务直接顶不住,那次回滚得很狼狈,也让我们记住平均延迟会骗人。

四、效果数据

调优后在线请求的 P99 延迟从峰值约六秒压回到一秒以内,超时率从百分之五降到不足千分之五,同时 GPU 利用率仍维持在七成以上,吞吐收益大部分保住了。离线大窗口那路吞吐基本没损失,两路流量各取所需。长尾请求的优先通道启用后,尾部等待耗时下降约七成,业务方投诉归零。我们顺手做了个压测对比,同样负载下新旧策略的尾延迟差了一个数量级。文中数据为项目复盘口径,已做脱敏。

五、可复用经验总结

批处理不是窗口越大越好,尾延迟和吞吐必须一起看,只看平均延迟会骗自己,业务方体感的是分位不是均值。长尾请求得有优先通道,否则它们永远排在批尾吃亏,这点对在线服务是底线。分位监控要补齐,P99 这种指标不上线,长尾问题你根本看不见,等看见了已经超时被投诉了。说到底,推理调度是取舍活,不是参数越大越猛,平衡点得用数据找。

这次调优让我重新理解了吞吐和延迟的关系,它们不是简单的此消彼长,而是要看清代价由谁承担。把窗口开大,省下来的是 GPU 的算力账单,代价却悄悄转嫁给了排在批尾的那批用户,而这批人的体验崩了,业务方才不会管你省了多少卡。后来我们做容量规划时,把尾延迟的预算单独列出来,和算力预算放在一张表上比,决策反而清楚了。另外提醒一句,动态窗口的阈值别设成固定值就不管了,请求特征会随业务演化,我们每季度回看一次分位分布,该收紧收紧、该放宽放宽,调度策略也是要养的。

顺便记一笔,连续批处理本身是个好东西,我们没否掉它,只是给它加了约束。有些团队一看到尾延迟问题就退回逐请求推理,吞吐掉一大截,其实没必要,关键是把窗口和优先级这两只手同时用上。我们还做了一件后来很值的事,把不同模型的批处理特性建了档案,有的模型吃大窗口收益高,有的模型小窗口就饱和,按模型实例给定制参数,比全局一套强太多。这背后是个朴素道理,推理调度没有银弹,得看模型、看流量形状、看业务对延迟的容忍度,三者凑在一起才能定参数。我们那个动态窗口的调参记录现在还留着,新模型接入时直接拿来当起点,省了不少试错时间。

最后补一句,尾延迟这件事建议直接写进服务的核心指标看板,当成和吞吐并列的一等公民来盯。很多团队只盯平均延迟,等 P99 爆了才后知后觉,那种被动最伤业务信任。我们把尾延迟和等批耗时都打成了实时曲线,谁都能随时看,反而没人再为超时被叫起来了。

案例片段(已脱敏): 动态批处理窗口的一段配置:batching:  mode: continuous  window_dynamic: true  window_idle: 64  window_busy: 16  priority_queue: true  tail_latency_sla_ms: 1000一次回滚前的延迟告警摘要:[latency] avg=820ms, p95=1900ms, p99=6020ms [alert] tail_timeout_rate=5.1%, sla_breached=true [action] rollback_to_window=16, reverted_in=3min调优后的压测对比:[bench] old: p99=6020ms, gpu=82%, timeouts=5.1% [bench] new: p99=940ms,  gpu=74%, timeouts=0.4%