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

日期:2026-09-11

一、项目背景

某客户在我们 AI 私有化底座上跑客服问答推理,一开始为了吞吐开了连续批处理,QPS 上来了,GPU 利用率也好看。但用户侧开始有反馈,说有时候回答明显卡一下,尤其并发高的时候。我们拉监控发现,P99 延迟比 P50 高出一倍多,长请求把短请求摁在队列里一起批,短请求也得等长请求算完才返回。客服场景用户对首字延迟敏感,这种尾延迟抖动直接拉低体验评分。更要命的是,这个客户是按调用量付费的,他们不在意平均快慢,只在意"最慢那次是不是又卡了",所以我们必须把尾延迟当成头等指标,而不是只盯吞吐报表上那个好看的数字。

二、落地场景

这个项目的目标是在不牺牲太多吞吐的前提下,把尾延迟压到业务能接受的范围。我们把请求按预估长度做了粗分,短问答走一个批处理窗口小、优先级高的队列,长文生成走另一个窗口大、允许排队的队列。网关侧按 SLA 给不同业务打标记,底座据此路由。运营能在一个看板上看 P50、P90、P99 和吞吐的实时曲线,调参不再靠拍脑袋。我们还把尾延迟达标率做成了告警线,一旦 P99 连续五分钟超标就自动通知值班,避免"报表好看、用户骂街"的脱节。

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

核心矛盾是批处理窗口大小。窗口太小,批次凑不满,GPU 利用率掉、吞吐降;窗口太大,请求在窗口里等得久,尾延迟炸。我们做了阶梯式窗口:平时窗口短,突发流量时自动拉长,但设了硬上限,超过就新开一批,不让单批无限拖。另一个点是长短请求隔离,早期混批,长请求一进来短请求全陪跑,拆队列后短请求基本不等长请求。还有优先级抢占,高 SLA 请求到了可以插到当前批的下一个位置,代价是牺牲一点批内规整,但用户体验换回来了。第四个问题是预估长度不准,短长分错会把长请求塞进短队列,我们用历史实际长度校准预估模型,分错率从两成降到不到五个点。

案例片段(已脱敏): 批处理窗口与队列配置片段: 短队列 max_batch_window_ms=8,长队列 max_batch_window_ms=64;突发时短队列窗口自动放宽到 16ms,超过则 flush_now=true 强制出批。 压测对比(脱敏):混批时 P99=2.4s、吞吐 1800 tok/s;拆队列后 P99=1.1s、吞吐 1650 tok/s,尾延迟降 54%,吞吐仅降 8%。

四、效果数据

拆队列加阶梯窗口后,客服场景 P99 从 2.4 秒降到 1.1 秒左右,尾延迟达标率(小于等于 1.5s)从 72% 提到 95%,超标自动告警后再没出现过长时间无人知的情况。吞吐从 1800 tok/s 微降到 1650 tok/s,业务侧认为这 8% 的让步完全值得,毕竟尾延迟才是用户体感。短请求平均首字延迟从 600ms 降到 280ms,客服坐席反馈"回答跟手了"。分错率校准后,长请求误入短队列导致的偶发卡顿也基本消失。文中数据为项目复盘口径,已做脱敏。

这个项目之后,我们把尾延迟达标率写进了对外 SLA,成为底座的硬指标而不是参考曲线。后续接新业务时,网关会先问清楚对方的尾延迟容忍线,再决定要不要拆队列、要不要开抢占,参数不再是通用一套。我们还把批处理窗口的自动伸缩接了预测,按历史流量波形提前放宽,避免突发来了才被动拉长。底座团队现在聊吞吐必聊尾延迟,这个习惯是那次体验评分打脸之后才真正扎根的。调参也不再靠发版,配合配置中心热加载,窗口参数当天就能试完好几轮,最佳值找得快多了。

长短请求拆队列之后,我们发现高 SLA 业务其实只占一小部分,却贡献了大部分体感投诉,所以把抢占也限在这类请求上,普通请求不插队,整体吞吐基本没再掉。我们还把批处理状态做了可视化,运维一眼能看当前每批里卡了哪些长请求、短请求等了多久,排查尾延迟不再靠猜。之前那次尾延迟炸,我们花了半天翻日志才定位到是长请求混批,现在看板上一眼就明白。底座的尾延迟治理从救火变成了日常盯盘,这种转变比参数本身更值钱,运维也不用再半夜被叫起来看曲线。

尾延迟这个指标后来成了我们给客服类业务的标配承诺,签 SLA 时直接写进条款,倒逼底座始终把体验放在第一位,吞吐和尾延迟两头都要盯。

五、可复用经验总结

连续批处理不是开了就完事,窗口参数是和业务 SLA 谈出来的,没有 universal 的最优值,我们反复压测才找到一个不痛不吐的点,这活没法一次调到位。长短请求一定要隔离,让短请求陪长请求批处理是最典型的为了吞吐牺牲体验,客服场景里这口锅最后还是研发背。尾延迟比平均延迟更该盯,用户记不住平均,只记得那次卡了三秒,体验评分就是被那几次拖垮的。预估长度别信拍脑袋,用历史实际校准,分错一类请求整个队列都抖。说实话,调这个参数我当时有点过度追求吞吐,后来被客服体验评分打脸才回头,体验优先这条到现在都管用。