← 返回研究论文
RESEARCH PAPER 06INFERENCE SCHEDULING

吞吐量不等于响应速度:
连续批处理与推理调度

让新请求及时进入计算,让正在生成的回答保持连贯,需要怎样的调度预算?

原创技术研究2025.08.09原理分析与工程设计

摘要

推理服务可以每秒输出更多 token,同时让某位用户等得更久。原因在于请求长度不同、到达时间不同,且预填充与解码争用同一组资源。本文基于已有的迭代级调度思想,以原创四请求时间表解释连续批处理,再构造同时考虑 token 与缓存容量的调度草图。重点是建立可比较的吞吐和延迟口径。

01 / 两种阶段,共享一张设备

预填充处理一段输入,解码则反复为活跃请求生成后续 token。长文档预填充通常有较大的矩阵运算,单步解码往往更受权重及 KV Cache 读取影响;具体瓶颈还取决于模型、上下文和批量大小。

以研究助手为例,一位用户上传长报告时,另一位用户可能正在等待一句解释继续输出。如果长预填充独占一个大步骤,后者会看到生成间隔拉长;如果每轮都只照顾已开始生成的回答,新报告又会迟迟没有首个结果。调度面对的是两种等待,需要分别计量。

02 / 将调度边界缩小到迭代

请求级批处理在一批请求开始后,通常保持成员不变,直到这批任务结束。短请求先完成,其位置仍可能等待同批长请求释放。连续批处理在迭代边界重新选择成员:完成的退出,等待中的进入,下一轮运行新的组合。

Yu 等人在 Orca 论文中提出迭代级调度,并结合选择性批处理构建生成模型服务。本文沿用“每轮更新成员”的基本思想;实际执行还涉及注意力状态和算子组织,不能仅靠改变队列就获得相同系统效果。

Waiting & ActiveBudget PlannerOne IterationRetire & Refill
图 1:每次迭代结束后重新组织请求集合。循环的是调度过程,不要求每条请求使用相同长度。

03 / 四个请求的时间表

为了只观察成员替换,假设每轮固定耗时 1 个时间单位,最多并行处理两条请求,每条每轮生成 1 个 token。以下到达时间是已经完成预填充、可以加入解码的时刻。A、B 在 0 时刻到达,分别还需 4、2 个 token;C 在 1 时刻到达,还需 2 个;D 在 2 时刻到达,还需 1 个。等待请求按到达顺序进入。

时间区间固定成员批次连续批处理
[0, 1)A · BA · B
[1, 2)A · BA · B
[2, 3)A · 空位A · C
[3, 4)A · 空位A · C
[4, 5)C · DD · 空位
[5, 6)C · 空位已全部完成

固定成员方案中,B 在时刻 2 完成,但其空位到时刻 4 才能重新使用。连续方案在时刻 2 加入 C,因此 C 的完成时刻从 6 提前到 4;A、B、D 的完成时刻分别仍为 4、2、5。九个剩余 token 全部完成的时刻由 6 变为 5。

这个改善来自减少空位,没有让任意一次模型运算加速。D 仍需等待,说明局部效率提高不等于所有请求都更快。真实设备的一轮耗时会随批量、上下文和预填充份额变化,不能把此表换算成毫秒或声称固定的吞吐提升比例。

04 / 区分吞吐与等待

测量时先固定时间边界。本文从客户端发出请求到收到 token 计时;服务端排队和执行耗时另列。vLLM 的指标文档提供了请求、生成 token、缓存与延迟指标,可用于补充服务端观察。

指标本文定义说明
TTFT首 token 到达时间 − 请求发出时间包含排队、预填充及传输等待。
TPOT(末 token 时间 − 首 token 时间) / (输出 token 数 − 1)每请求的平均后续生成间隔;至少有两个输出 token。
p95 延迟指定延迟记录的第 95 百分位须说明取 TTFT、TPOT 还是总耗时,不能混为同一指标。
输出吞吐观测窗口内输出 token 总数 / 窗口秒数与请求数每秒、输入加输出 token 每秒分别报告。

平均 TPOT 会平滑回答中间的一次长停顿。若产品强调流式连贯性,应额外记录逐 token 间隔的分布。不同长度请求也应分组比较,否则长输出占比变化可能抬高吞吐,却掩盖短问答的首字等待。

05 / 给解码和预填充分配预算

分块预填充将长输入拆成若干段,使一轮内能够混合解码与输入计算。vLLM 的调优文档说明了先安排解码、再用剩余 token 预算处理预填充的方式。较小预算通常减少单轮预填充对生成间隔的影响,但新请求可能需要更多轮才得到首个 token;更大预算有助于推进输入,也可能延长本轮。

def select_iteration(active, waiting, budget, chunk_cap, kv):
    plan = []
    for request in active:
        if budget == 0:
            break
        if kv.reserve(request, 1):
            plan.append((request, "decode", 1))
            budget -= 1

    for request in waiting:
        count = min(request.prefill_left, chunk_cap, budget,
                    kv.available_token_slots(request))
        if count == 0:
            continue
        kv.reserve(request, count)
        plan.append((request, "prefill", count))
        budget -= count
    return plan

这里假定单个调度器顺序预留缓存,未启用推测解码。token 预算是工作量代理,并不保证每轮等时。应用可按等待时长调整候选顺序,并限制同时活跃的解码请求数量,为新输入留出进展空间。真正需要调的是“允许一轮做多少工作”,而不仅是“同时存在多少请求”。

06 / 用真实负载比较取舍

实验应固定模型、设备、推理精度、输出限制和请求数据,单独改变批量或 token 预算。记录输入长度、输出长度、前缀复用比例和到达过程;长短混合负载比全部等长请求更能暴露排队问题。预热后再统计稳定区间,冷启动时间另列。

容量曲线可使用预先给定的到达时间持续提交,逐步增加到达率;不要因为上一条变慢就自动减少发送量,否则会低估拥塞。固定并发压测适合模拟有限用户,也应注明请求完成后才补发的机制。两种测试回答的问题不同,需要分别展示。

每个配置同时报告 TTFT、TPOT 与总耗时的分位数、输出吞吐和积压请求数。最终选择应满足产品可接受的等待水平,再比较可服务负载。研究助手的长报告分析与即时交互可以采用不同预算,让调度策略对应真实的阅读和操作节奏。

参考文献