连续批处理(Continuous Batching)
一句话定义
连续批处理把调度粒度从"一个请求"细化到"一次前向迭代":每生成一步都允许新请求加入、已完成请求退出,从而让 GPU 永远跑在"当前能装下的最大有效 batch"上,是服务端吞吐的第一支柱。
为什么重要
静态批处理(凑满一批、跑完最长的才散场)在 LLM 负载下灾难性低效——同批请求输出长度差异可达 10 倍,短请求陪跑浪费全部算力。连续批处理配合分页 KV(kp-014)构成现代引擎 2–4 倍吞吐优势的两个来源,Orca 论文(2022)是这一思想的原点。
前置知识
- kp-003(decode 访存密集、batch 内权重读取可共享——批的"免费"区间)。
- kp-013(KV Cache 占用决定一个请求能否被接纳)。
核心概念
- 静态批(static batching):攒 N 个请求 → 一起 prefill → 一起 decode 到最长的结束。短板效应:批次时长由最长请求决定,padding 与陪跑浪费严重。
- 连续批 / 迭代级调度(in-flight batching):每一步 decode 后检查队列:新请求插队加入(做自己的 prefill),完成者退出腾出 KV 空间——batch 成员随时更替。
- 准入控制:能否接纳新请求由 KV 显存余量决定(分页后按块计费),显存不足时排队或抢占(preemption:换出/重算)。
- 与 TPOT 的交换:batch 越大吞吐越高,但每步时间被拉长,TPOT 上升——SLO 是 batch 上限的依据(kp-002)。
原理与机制
连续批处理把调度粒度从"请求生命周期"细化到"单次前向迭代":每步 decode 后重新评估批成员,完成者退出、就绪者加入。由于 decode 单步时间在小 batch 区间几乎与 batch 大小无关(瓶颈是全体共享的权重读取),频繁换血几乎不增加单步耗时,却让 GPU 始终运行在当前 KV 余量允许的最大有效 batch 上——这就是吞吐倍增而单步代价近零的机制。
公式与模型
批内 decode 单步时间模型(近似):
第一项与 B 无关(权重只读一次)——这就是批处理"近免费"提吞吐的数学根源;B 增大后第二、三项接管。数值例子:7B FP16 在 A100 上 B=1 与 B=32 的单步时间通常仅差 20–50%,而系统吞吐差 30 倍。
图示
静态批: [req1 ████][req2 ████████████][req3 ██░░] ← req3 早已完成却陪跑到 req2 结束
连续批: t1: {A B C} t2: {A B C D} t3: {B C D E} t4: {D E F}
↑C 完成,新请求即时补位, GPU 永不空转
直观类比
静态批是"旅游团必须全员到齐才发车、全员逛完才解散";连续批是地铁——每站都有人上下车,车厢始终接近满载。
实例或案例
压测口径下的典型对比(7B、并发 32、输入 512/输出 256):静态批系统吞吐约 X,同硬件连续批达 2–4X;输出长度方差越大的负载(闲聊 vs 分类)差距越大。这也解释了为什么"引擎升级吞吐翻倍"的新闻里,一半功劳属于调度而非 kernel。
常见误区
- "batch 越大越好":越过算力拐点后 TPOT 恶化、排队变长;正确做法是在 SLO 约束下最大化并发(kp-002 的 goodput)。
- "有了连续批就不需要分页":两者互补——连续批解决"谁在批里",分页解决"KV 放哪/还能进谁";没有分页,接纳决策就做不细。
- "preemption 是故障":显存压力下的抢占-重算/换出是设计内行为,但频繁抢占说明容量不足(kp-027 排错项)。
自测题
- 为什么批内 decode 单步时间在 B 较小时几乎不随 B 变化?
要点:权重读取(一次、全体共享)主导耗时,计算量虽 ×B 仍未触及算力上限。
- 静态批的最大浪费来自哪两个机制?
要点:最长请求决定批次时长(短板效应)+ 短请求的 padding 陪跑。
- 连续批与 PagedAttention 如何配合?
要点:分页提供按块的 KV 显存记账,使"每步准入/退出"的细粒度调度成为可能。
与其他知识点的关系
依赖 kp-003 的瓶颈分析与 kp-013 的 KV 账目;参数化落在 kp-022 的 max-num-seqs;规模化后由 kp-023 跨副本接力;kp-024 是它在"两阶段冲突"下的演化终点。
延伸阅读
《Orca》(Yu 等,OSDI 2022)第 2 节首创"迭代级调度 + 选择性批"表述,是本页一切概念的原典。