跳转到主要内容

LLM 排队论:为什么你的负载均衡器按请求思考,而你的 GPU 按 Token 思考

阅读需 4 分钟Tian PanTian Pan

你的负载均衡器将请求均匀地分配到你的 GPU 集群中。每个实例接收到的并发请求数量大致相同。一切看起来都很均衡。然而,一个实例的运行速度缓慢,仅为每秒 40 个 token,而另一个实例却能稳定在 200 个。仪表板显示请求数相等,但你的用户体验到的延迟却天差地别。

问题的根源在于:传统的负载均衡在请求层面运行,但 LLM 推理成本是随 token 数量扩展的。一个要求生成 4,000 个 token 文章的请求所消耗的 GPU 时间,是一个生成 80 个 token 分类结果请求的 50 倍。将它们视为同等单位,就像高速公路收费站只计算车辆数量而不区分摩托车和 18 轮大卡车一样。

这种请求层面的思维与 token 层面的现实之间的不匹配,正是古典排队论面临的最有趣的现代挑战。

利特尔法则不在乎你的 Token(直到它开始在乎)

利特尔法则 (Little's Law) —— L = λW,即平均队列长度等于到达率乘以平均等待时间 —— 是排队论的基石。无论到达分布或服务策略如何,它对任何稳定系统都适用。但将其应用于 LLM 推理需要重新定义你实际测量的对象。

在传统的 Web 服务中,“工作单位”是一个请求。服务时间大致是可预测的:数据库查询需要 5-50 毫秒,API 调用需要 100-500 毫秒。你可以将容量建模为每秒请求数,并据此进行规划。

LLM 推理通过以下三种方式打破了这一假设:

  • 双峰处理 (Bimodal processing):每个请求都有一个 prefill 阶段(处理输入 prompt,可并行化)和一个 decode 阶段(按顺序生成 token,每次前向传递生成一个)。这两者具有截然不同的计算特性。
  • 可变的输出长度:当请求到达时,你不知道它的服务时间。一个请求可能生成 10 个 token,也可能生成 4,000 个。服务时间的差异可能跨越两个数量级。
  • 内存受限的扩展:每个活动请求都持有一个键值 (KV) 缓存,该缓存随生成的每个 token 而增长。GPU 内存(而非计算能力)往往成为核心限制因素。

实际意义在于:你需要将利特尔法则应用于 token 层面,而不是请求层面。系统的吞吐量容量是以每秒 token 数来衡量的,你需要管理的“队列”是总的 token 工作负载 —— 包括等待 prefill 的输入 token 以及所有活动序列中正在生成的输出 token。

当研究人员将 LLM 推理建模为一个离散时间排队系统,且每个时间槽对应一次 GPU 前向传递时,其稳定性条件变为:

λ(m_prefill + m_decode) < B / t_step

其中 λ 是请求到达率,m_prefill 和 m_decode 是平均 token 计数,B 是每步 token 预算,t_step 是每次前向传递的时间。一旦超过这个阈值,你的队列就会无限增长 —— 无论你的调度器有多聪明。

为什么请求层面的负载均衡会失败

考虑一个 GPU 实例,每次前向传递的 token 预算为 512。以下是两个请求数完全相同的场景:

场景 A:10 个并发请求,每个生成约 50 个 token。每步总的活动 decode token 数:约 10。Prefill 很快,decode 步骤很轻量。GPU 利用率不足。

场景 B:10 个并发请求,每个生成约 2,000 个 token。所有序列的 KV 缓存:巨大。GPU 在处理到 6 个并发序列时内存耗尽,迫使 4 个请求进入等待队列。有效吞吐量骤降。

请求层面的负载均衡器在两种情况下都看到“10 个请求”,并认为它们是平衡的。而感知 token 的系统会看到实际 GPU 工作负载存在 40 倍的差异。

这就是为什么 N+1 查询问题在 LLM 服务中也有类似的情况:负载均衡器在不知道每个决策实际成本的情况下做出了 N 个路由决策。它所需要的信息 —— 输出 token 计数 —— 在做出路由决策时还不存在。

实际的解决办法包括:

  • 基于 Prompt 长度加权的路由:使用输入 token 计数作为总成本的代理。虽然不完全准确,但较长的 prompt 通常与较长的输出相关。
  • 基于活动 Token 计数的路由:将路由指向处理中总 token 数(prefill + decode)最少的实例,而不是请求数最少的实例。
  • 感知 KV 缓存的路由:根据可用 GPU 内存而非请求数进行路由。一些系统(如 NVIDIA Dynamo)通过将内存利用率暴露为路由信号来实现这一点。

这些方法都不能完全解决预测问题,但它们将差异从 100 倍降低到大约 3-5 倍 —— 足以让尾部延迟保持在可控范围内。

真正至关重要的调度策略

古典排队论提供了一系列调度策略:FIFO(先进先出)、SJF(短作业优先)、优先级队列、公平队列。对于 LLM 推理,最重要的选择不是下一个服务哪个 请求 —— 而是如何用 token 填满每个 GPU 迭代。

最近的研究将此形式化为“工作守恒 (work-conserving)”属性:如果调度器在有足够 token 可用时,总能将每次迭代的 token 预算填满,那么它就是工作守恒的。核心见解是:在同一个 batch 中混合 prefill 和 decode token 对于实现吞吐量最优至关重要

原因如下。在一个仅包含 decode 的 batch 中,你可能有 8 个活动序列,每个序列每步贡献 1 个 token = 每次前向传递处理 8 个 token,而预算是 512。这只有 1.5% 的利用率。工作守恒调度器会将等待请求中的 prefill token 填充到剩余的 504 个 token 槽中,从而显著提高每步的 GPU 利用率。

实际验证结果非常显著:

  • Sarathi-Serve 和 Orca:经证明是吞吐量最优的。两者都通过分块 prefill (chunked prefill) 在同一个 batch 中混合 prefill 和 decode token。
  • FasterTransformer:并非吞吐量最优。它将 prefill 和 decode 分离到不同的 batch 中,导致 GPU 周期被浪费。
  • 原生 vLLM (在支持 chunked prefill 之前):在其原始形式下并非吞吐量最优。没有混合的 prefill 优先调度可能会在某些到达模式下导致 decode token 饥饿。

结论:如果你的服务基础设施将 prefill 和 decode 分离为无法共享 batch 的不同阶段,那么你将白白浪费 30-70% 的 GPU 吞吐量。支持 chunked prefill 的持续批处理 (Continuous batching) 不仅仅是一项优化 —— 它是高负载下稳定服务的正确性要求。

优先级队列:三层模式

并非所有推理请求都值得同等对待。在生产系统中出现的标准模式使用三个优先级层级:

第一层 — 交互式(延迟敏感):聊天回复、实时补全、流式 UI。目标:首个 token 生成时间(TTFT)在 500ms 以下。这些请求应抢占低优先级任务。

第二层 — 标准(平衡):具有合理 SLA 的 API 调用、后台功能生成、搜索增强。目标:端到端完成时间在 10 秒以内。可以容忍短暂排队。

第三层 — 批处理(吞吐量优化):批量分类、数据集标注、离线摘要。目标:最大化单位成本的 token 产出。可以等待几分钟或几小时。

实现的挑战在于抢占(preemption)。当第一层请求到达且 GPU 正全力处理第三层任务时,你需要逐出(evict)低优先级的序列。这意味着保存它们的 KV 缓存状态(存入 CPU 内存或丢弃以供稍后重新计算),并立即开始高优先级的预填充(prefill)。

vLLM 0.9+ 支持连续优先级编号,高优先级请求可以抢占活动批次中的低优先级请求。但仅靠调度系统是不够的 —— 你还需要一个外部准入控制器(admission controller),用于:

会员专享

余下内容仅对会员开放。

会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。

  • 完整文章,包含未公开存档的部分
  • 可落地的工作框架,附带权衡与决策依据
  • 新文章抢先看,先于公开发布

随时取消 · 一次订阅,畅读全部

参考资料

保持联系,关注我获取更多内容

阅读需 11 分钟

Agent 循环容量计算:为什么你的预置吞吐量只有你想象的一半

基于用户 QPS 估算的预置吞吐量往往会因为循环扇出因子而导致 Agent 产品资源配置不足。应改为基于模型调用率、循环深度和突发尾部延迟进行规划。

insider
agents
阅读需 10 分钟

持续批处理:LLM 服务中提升 GPU 利用率的最关键技术

深入探讨迭代级调度如何取代静态批处理,在生产环境的 LLM 服务中实现 4–8 倍的 GPU 吞吐量提升,以及在高并发场景下出现的失效模式。

insider
llm-inference
阅读需 9 分钟

GPU 调度是一个排队问题,而不是资源配置问题

LLM 终端响应慢通常是排队失败,而非硬件短缺。了解连续批处理、KV 缓存限制以及 Prefill/Decode 调度如何决定你的尾部延迟 —— 以及为什么增加 GPU 无法解决此问题。

insider
llm-inference
阅读需 10 分钟

当需求是悬崖而非曲线时,如何进行 GPU 产能规划

Agent 工作负载打破了平滑曲线的产能规划。请以 Token 为单位进行规划,将扇出视为一级指标,并针对预见性的“悬崖式”需求预留产能。

insider
gpu-capacity
阅读需 9 分钟

GPU 饥饿:某个租户的推理提示词如何导致你的共享推理端点停滞

一个推理提示词就能拖慢共享推理端点上所有其他请求的 p99 延迟。本文将探讨为什么连续批处理和 KV 缓存钉选会导致队头阻塞,分析鲜有人关注的诊断信号,并介绍四种缓解方案 —— 分块预填充、优先级调度、每租户 Token 上限以及请求类别隔离 —— 按其侵入性由低到高排序。

insider
llm-inference