全部术语

GLOSSARY ENTRY

Continuous Batching 与核心指标

  • Continuous Batching
  • Iteration-Level Scheduling
  • In-Flight Batching

在每个 decode iteration 重新组成 active batch,让新请求进入、完成请求立即退出并复用 KV/计算槽位;它提高利用率的同时改变 queue、TTFT、TPOT、吞吐与公平性。

与静态 batch 的差别

静态 batch 往往等一组请求凑齐,按最大 prompt/output 长度一起运行;短请求完成后仍可能留下 padding 或等待 batch barrier。Continuous batching(iteration-level scheduling)在每个模型 iteration 重新选择工作:

  1. 让 active decode 请求各前进一步;
  2. 回收已完成/取消请求的 KV blocks 和 slots;
  3. 在 token、sequence、KV 容量和优先级预算内接纳新 prefill/chunk;
  4. 形成下一次模型执行 batch。

它是服务 scheduler 机制,不改变 Prefill/Decode 的模型数学,也不等同动态 shape 的普通 DataLoader batching。

Benchmark 方法与容量模型进一步定义 warmup、同步、统计口径、active KV token capacity 和 SLO goodput;调度对比应复用同一 arrival trace。

同一 trace 的 worked example

请求记为 (arrival, prompt tokens, output tokens)

A = (0, 4, 5)
B = (0, 2, 2)
C = (3, 3, 4)
token-work budget = 8 / tick

教学模型假设每个 prompt/decode token-work 成本相等。静态 batch 在 t=0 把 A/B prompt padding 到 4,B 完成后仍留下 3 个 decode padding slots;直到 A 在 t=6 完成才接纳 C。Continuous scheduler 在 t=3 让 A decode 的同时 prefill C,并立即复用 B 的空槽。

INTERACTIVE EXPLAINER

请求进入、逐轮推进、完成移除与槽位复用

同一 A/B/C trace;逐步观察 B 完成后 C 如何在 A 尚未完成时进入 active set。

same request trace · iteration-level scheduler · token budget 8/tickP=prefill · Dn=nth emitted token state · slot becomes reusable immediately after completionRequest Aarr=0 · P=4 · O=5Pactive slot / scheduler stateRequest Barr=0 · P=2 · O=2Pactive slot / scheduler stateRequest Carr=3 · P=3 · O=4waitingactive slot / scheduler statecurrent iteration decisionprefill A(4)+B(2) · unused budget 2continuous: makespan 8 · C TTFT 2 · waste 0static: makespan 11 · C TTFT 5 · waste 5simulation assumptionone token-work has equal cost; real prefill/decode do notproduction scheduler also tracks KV blocks, max sequences/tokens, priorities, preemption, graph buckets and SLOs
STEP 01 / 05t=0:A、B 到达并 prefill

token budget=8;A prompt 4、B prompt 2 同轮进入。prefill 后二者从下一 iteration 开始 decode。

图中把 token-work 设成等成本单位,省略 attention 长度、GEMM shape、chunked prefill、CUDA Graph buckets、KV block 分配、priority、preemption 与设备执行重叠。真实 prefill token 和 decode token 的成本不能直接相加,模拟器只验证调度语义和指标计算。

核心指标边界

  • Queue time:请求可执行前等待 scheduler/容量的时间。
  • TTFT:arrival 到首个输出 token;包括 queue、tokenization/template、prefill、sampling、streaming。
  • TPOT / inter-token latency:首 token 后输出 token 间隔;可按请求均值或 step 分布报告。
  • Throughput:单位时间完成的 output tokens/requests;未必满足每个请求 SLO。
  • Goodput:满足指定 TTFT/TPOT/端到端 SLO 的有效请求或 tokens。
  • Makespan:给定 trace 全部完成所需时间,适合离线调度对比。

同一 workload、同一 arrival trace、同一模型/硬件/输出语义才可公平 A/B。吞吐实验还要说明 warmup、请求长度分布、并发上限、scheduler budgets、采样和 streaming 是否计入。

可运行离散事件模拟

from dataclasses import dataclass,field
import platform,statistics

@dataclass
class Request:
    name:str; arrival:int; prompt:int; output:int
    emitted:list[int]=field(default_factory=list); completed:int|None=None
    def clone(self): return Request(self.name,self.arrival,self.prompt,self.output)

TRACE=[Request("A",0,4,5),Request("B",0,2,2),Request("C",3,3,4)]
CAPACITY=8

def metrics(reqs,makespan,waste):
    total=sum(r.output for r in reqs)
    return {"makespan":makespan,"padding_waste":waste,
            "throughput":total/makespan,
            "ttft":{r.name:r.emitted[0]-r.arrival for r in reqs},
            "tpot":{r.name:(statistics.mean(b-a for a,b in zip(r.emitted,r.emitted[1:]))
                    if len(r.emitted)>1 else 0.) for r in reqs},
            "completion":{r.name:r.completed for r in reqs}}

def static_batching(trace):
    reqs=[r.clone() for r in trace]; time=waste=0; pending=reqs[:]
    while pending:
        ready=[r for r in pending if r.arrival<=time]
        if not ready:
            time=min(r.arrival for r in pending); ready=[r for r in pending if r.arrival<=time]
        batch=ready
        for r in batch: pending.remove(r)
        padded=len(batch)*max(r.prompt for r in batch)
        waste+=padded-sum(r.prompt for r in batch)
        time+=(padded+CAPACITY-1)//CAPACITY
        remaining={r.name:r.output for r in batch}
        while any(remaining.values()):
            for r in batch:
                if remaining[r.name]>0:
                    remaining[r.name]-=1; r.emitted.append(time+1)
                    if remaining[r.name]==0: r.completed=time+1
                else: waste+=1
            time+=1
    return reqs,metrics(reqs,time,waste)

def continuous_batching(trace):
    reqs=[r.clone() for r in trace]; time=0; pending=reqs[:]; active=[]
    while pending or active:
        budget=CAPACITY
        for r in active[:]:
            if budget==0: break
            r.emitted.append(time+1); budget-=1
            if len(r.emitted)==r.output: r.completed=time+1; active.remove(r)
        for r in pending[:]:
            if r.arrival<=time and r.prompt<=budget:
                budget-=r.prompt; pending.remove(r); active.append(r)
        time+=1
    return reqs,metrics(reqs,time,0)

def main():
    static_req,static=static_batching(TRACE)
    cont_req,cont=continuous_batching(TRACE)
    assert static["completion"]=={"A":6,"B":3,"C":11}
    assert cont["completion"]=={"A":6,"B":3,"C":8}
    assert static["padding_waste"]==5 and cont["padding_waste"]==0
    assert sum(r.output for r in static_req)==sum(r.output for r in cont_req)==11
    print(f"python={platform.python_version()} capacity={CAPACITY} token-work/tick")
    print("trace="+str([(r.name,r.arrival,r.prompt,r.output) for r in TRACE]))
    print(f"static metrics={static}")
    print(f"continuous metrics={cont}")

if __name__=="__main__": main()

完整文件位于 examples/continuous_batching_simulator.py。实际结果:静态 makespan 11、padding waste 5、throughput 1.0 output token/tick、C TTFT 5;continuous makespan 8、waste 0、throughput 1.375、C TTFT 2;本例所有请求 TPOT 都为 1。

模拟器没有 GPU 性能含义,不证明生产吞吐提高 37.5%。它只在明确假设下证明“每轮重组 + 槽位复用”如何改变 trace。生产验证必须用真实 prefill/decode costs、KV 容量、batch kernel 和 arrival distribution。

Scheduler 真正还要解决什么

Scheduler Budget 与 Admission Control用可运行 block reservation 模型进一步区分本轮 token budget、长期 KV capacity、保守 reservation 与乐观 overcommit。

  • Token budget:一轮允许多少 prefill/decode tokens,避免长 prompt 垄断。
  • KV block budget:是否有足够 PagedAttention blocks;不足时 reject、queue、preempt、swap 或 recompute。
  • Prefill/decode interference:大 prefill 可能拉长同轮 decode 的 TPOT;chunked prefill 用更多轮换公平性。
  • CUDA Graph buckets:active batch 改变时选哪个固定 graph,padding 多少,还是 eager fallback。
  • Fairness/priority:FIFO、deadline、priority、aging 与多租户配额。
  • Cancellation:停止请求要及时回收 KV、取消 streaming,同时保持 collective/order 安全。

上下游关系

  • Tokenizer/Chat Template决定 prompt token 数,进入 admission 和 prefill budget。
  • Prefill/Decode定义每个请求的阶段与 TTFT/TPOT;本页决定多个请求如何交错。
  • PagedAttention提供动态 KV block 分配与回收,是 slot reuse 的物理基础。
  • CUDA Graph要求静态 shape/address;scheduler 常用 batch buckets 和固定 slots 调和动态 active set。
  • 投机解码让一个请求每轮可能提交多个 token,会改变 token budget 与 verification scheduling。
  • MoE Router让同样数量的 active tokens 因路由分布不同形成不同 expert batch 与 All-to-All split,scheduler 的 batch size 不能单独预测 MoE iteration latency。
  • Request Lifecycle定义 admission 前后、cancel/timeout 与 streaming backpressure;scheduler 必须在安全边界移除终止请求并回收资源。
  • 服务可靠性、Backpressure 与 Observability把有界输出 credits 作为请求是否继续推进的条件,并用 generated/sent waste、finish reasons 与 SLO goodput 检查调度结果。

参考资料