批处理调度里有一个被低估的变量:同一个 batch 内序列长度的离散程度。连续批处理把请求攒到一批一起走 forward,看起来 GPU 一直在忙,但如果一批里混着 128 token 的短问答和 8K token 的长文档摘要,等待的那一方就会被拖住。长度分桶解决的就是这个问题:把长度接近的请求放在同一批,别让长请求和短请求互相拖。
问题在哪儿:批内填充与完成时刻不对齐
传统静态批处理需要把不同长度的序列 pad 到批内最大长度,padding 部分的计算纯属浪费。阿里云开发者社区的推理优化文章明确指出,自适应分组的目标是把相似长度的序列分到同一批次,减少填充带来的额外计算。这是最直观的收益,但线上真正痛的地方不止 padding。
连续批处理下,每个 iteration 都会重新组批,新请求可以随时插入。问题是一个 batch 里如果存在不同 prefill 长度混合的情况,CUDA kernel 的 tile 利用率会随着最长那一条走:短请求本可以在 200 微秒内结束,却要和长请求一起等到更重的计算完成,P99 立刻被拉高。《聊聊大模型推理服务中的优化问题》里给了一个判断:如果 token 序列太长,最好拆到不同的 batch 中。换句话说,一批可以大,但不要同时太长,太长的部分另行处理。
主流做法在做什么
学界和工程界的方向大致分三层。
- 批内切分:Dynamic SplitFuse 的思路就是限制一个批的最大 token 长度,超出的拆分出去,剩余处理单元长度一致,调度更简单。代价是拆分后的请求要在一个或多个 forward 中分次完成,long prefill 的 TTFT 变差,需要靠批的吞吐把平均延迟拉回来。
- 请求分桶:小米 MiMo-V2.5 系列推理全链路优化记录里给出了三级长度分桶策略,划分为 0–64K、64K–256K、256K–1M,把负载特征相近的请求聚合到同一桶做计算,线上 prefill 平均吞吐有明显提升。文档同时提到,他们正在探索更细粒度、更灵活的分桶机制来适应负载动态变化,也就是说三级切法已经是取得收益的版本,但粒度还不足。
- 实例层按桶路由:CN121724158A 这个专利的思路更彻底,请求分桶控制器按输入长度和预测输出长度自适应分桶,每个桶生成独立的推理配置,再由全局调度器的请求路由器按推理组维护的输入长度区间做路由选择,多个候选就估计总完成时间取最优。等于把分桶从批级别上升到部署级别,从工程复杂度来看代价也最高。
分桶之后收益从哪来,代价在哪儿
先算收益。分桶减少 fill 浪费是一个方面,更关键的是让 overlap 成立。MiMo 那篇文档提到,SWA 层只需要 prefetch 极少量 KVCache,layerwise 粒度的调度接近零成本;这意味着一批里请求的结构越接近,Host 到 Device 的拉取越容易和计算重叠,桶切得准,缓存命中长度也更容易复用。KVCache 命中率受益于分桶这一点容易被忽视,但在多轮对话或长 system prompt 共享场景下,同桶意味着同前缀长度分布,命中率稳定比单纯调容量有效。
再说代价,三块。
第一,排队头阻塞从批内转到了批间。按输入长度分桶之后,短请求聚集在短桶,一旦某条长请求独占一个桶、而桶内并发不够,GPU 利用率直接掉下来。MIMO 只用三级分桶,一个桶跨度可能是百K token 级别,命中率是按粗粒度换来的。切得过细,每个桶内请求数不足,batch 又变得很小。分桶是在 batch 大与 batch 均匀之间找一个折点。
第二,输出长度不可控。分桶应当按输入长度做,但输出的长度分布往往呈长尾。输入都以 8K 开头的两条请求,输出可能是 50 token 也可能是 4000 token。网关无法预先准确预估输出长度,分桶只能落得一个期望意义上的接近,别把这件事当成解决了。
第三,离散桶边界会造成迁移问题。当桶边界设在 64K 的整数倍、而线上聊天请求量又大量堆在接近边界的位置时,要么被迫溢出到更高一档,要么吞不到足够请求,调度器还要维护跨桶回填逻辑,实现复杂度上升不亚于重写一次调度器。
落地建议
我们的实践是输入长度先量再定,别照抄别人的阈值。动态分桶门槛应该在该部署下统计出的长度直方图上取谷底值:两个峰值之间的密度低谷,切在哪都不伤主流量。MIMO 采用 0–64K / 64K–256K / 256K–1M 这种粗切口,是这个场景下流量的经验密度产物,搬到别的工作负载多半不合适。
对于输出长度,稳定场景下可以直接做输出预估并发给分桶器,这是 CN121724158A 里按输入长度和估计输出长度共同分桶的部分思路;当预估值不可靠时,只作参考不强行改桶。实测更划算的做法是把分桶和优先级调度结合,短请求放在短桶优先级队列的前端,只在上限上给桶内设定接近的出发期,短边照样会等但短请求的等待被约束住。
上线后要盯紧三个指标,而不是只盯吞吐——单桶内时延 p99、分桶边界两侧请求的溢出率,以及整个 warmup buffer 中桶之间的排队时长差。只要批内方差没降而整体队列长度变长,说明桶设置不是在帮助请求,而是在稀释批次,应该收缩桶的粒度重新标定一次。
一个需要提前说清的边界
Dynamic SplitFuse 起家的位置实际上是 prefill/decode 的混排。decode 阶段 batch 大小和序列长度累加关系密切,而 prefill 阶段更看 GPU 计算边界。同一个桶里如果混了 chat、代码、长文档摘要,其计算密度差异可能远大于长度差异本身。分桶能解决填充与收尾不对齐,却解决不了异构请求之间计算形态的不匹配,别指望它一步到位。
说白了,长度分桶是一笔更接近工程配置优化而不是算法创新的账。它可以省 cap 费、可以压低 p99,但应该被放在连续批处理已经稳定、前缀缓存也未出现明显退化之后。桶不合理时,整个推理还停留在调度器而不是模型容量的层面,往上再做任何优化都会更难看。
转载请注明出处: 皮哈皮阿
本文的链接地址: https://blog.phpat.com/ai-engineering/674.html
微信或手机浏览器扫一扫,随时随地随心阅读与分享
-
推理成本下降之后:模型路由、KV Cache 与端侧推理
上个月一个团队拿着账单来找我:模型单价降了,费用却涨了三倍。看完调用链就明白了,问题不在单价,在用法。单位 token 便宜,不等于每次任务便宜。
2026/10/10
-
2026 年 AI 应用评测:从单轮打分到多轮轨迹评估
一个 Agent 的评测分数从 72% 涨到 81%,团队很高兴,直到发现它学会了在失败时直接结束任务,把“没做完”算成了“无需回答”。评测设计错了,指标越好看越危险。
2026/10/19
-
长度惩罚参数调优:常见参数名字容易混淆
长度惩罚在主流 API 里并不存在。真正管长度的是 max_tokens 和 stop sequences,frequency、presence、repetition 惩罚管的是用词重复和新词探索。把这些旋钮分开,能避免输出被截断、术语被替换成同义词两类故障。
2026/10/23
-
动态拼批不是开个开关:线上拼批的取舍与门禁
在线服务里的动态拼批和离线批量推理是两码事。窗口时长、KV Cache 水位、批内参数对齐共同决定拼接能否成立。上线前该盯的是 p99 和显存峰值,而不只是吞吐。
2026/10/25
-
推理模型的流式输出:thinking 块怎么流
推理模型的流式输出不只是逐字渲染:思考块、正文、工具调用参数混杂在 SSE 流里,客户端要按字段分流、独立缓冲、支持中途中断。记录接通义千问与智谱过程中的判断和踩坑。
2026/10/24
-
推理服务排队延迟的分层观测:从请求数到Token账本
盯 QPS 和 P99 看不到排队延迟长在哪。把这件事拆成入口→上游准入队列→后端等待→batch 解码四层,用 vLLM 暴露的排队与 KV 缓存指标做层级归因,讨论分层之后仍然看不见的部分与取舍。
2026/10/27
京公网安备11011402053654 
@muffin:链接已更新,分类图像功能...
AI网址导航模版图标库更换指南
作者大大,图标库邀请链接失效了,麻烦更新...
AI网址导航模版图标库更换指南
太好看了,快点更新!
友情链接
@戴墨镜的刘备:哈喽,路过点赞。
vue.js组件之手写签名板vue-signature-simple
太好看了,快点更新!太棒了!👍真的很赞!
vue.js组件之手写签名板vue-signature-simple
你写得非常清晰明了,让我很容易理解你的观...
Emlog插件RSS订阅器发布