跳到主要内容

混合 LLM 工作负载的 GPU 调度:那个没人解决好的装箱问题

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数运行 LLM 推理的 GPU 集群正在浪费 30% 到 50% 的可用算力。这并非因为工程师粗心,而是因为调度问题本身极为困难——而大多数团队首先想到的工具根本不是为此设计的。

标准做法是搭建 Kubernetes,为每个 Pod 申请完整的 GPU,然后让调度器自行处理。这对训练任务运行良好。但对于处理异构模型集合的推理场景,这种方式会悄悄摧毁利用率。一个运行三个不同 7B 模型且流量稀疏的集群,每个 GPU 的实际繁忙时间可能不足 15%,同时却处于完全"已分配"状态,拒绝调度任何新任务。

根本原因在于 Kubernetes 理解 GPU 的方式与 LLM 推理实际需求之间的错配。

Kubernetes 为何在这里失效

Kubernetes 将 GPU 视为原子单元。一个申请 nvidia.com/gpu: 1 的 Pod 会获得一整块物理 GPU——A100 上 40GB 的 HBM,H100 上 80GB——无论工作负载实际需要多少。这在工作负载是训练任务或批量推理作业、确实需要占用整个设备时是合理的设计决策。但在混合服务场景下,这种设计彻底失效。

具体失效方式如下:

无法感知 KV 缓存。 Kubernetes 调度器对运行中 Pod 的 KV 缓存占用率毫无感知。在 LLM 推理中,KV 缓存占用率是运行时最重要的资源约束——它决定实例能处理多少并发请求,以及新请求是进入队列还是直接失败。将一个长上下文请求路由到缓存利用率 90% 的 Pod,与路由到 10% 利用率的 Pod,会产生截然不同的延迟。调度器根本看不到这种区别。

模型加载延迟不可见。 启动一个新的推理 Pod 并非即时完成。仅容器镜像拉取就可能需要 2–5 分钟,因为镜像包含数 GB 的模型权重。之后还有 CPU 上的权重反序列化、传输到 GPU 内存,以及预热前向传播。生产环境中的冷启动总时间通常为 2–5 分钟。Kubernetes 会毫不犹豫地将 Pod 调度到一个让用户等待四分钟才能得到响应的节点上,因为它没有模型加载状态的概念。

批次合并不可能实现。 水平 Pod 自动扩缩(HPA)依据 CPU 和内存指标触发。而在 LLM 推理最需要扩容的时刻——队列深度积压、请求等待 token 生成——内存利用率可能只有 30%(空闲推理服务器的正常水平),CPU 利用率也很平稳。系统看起来一切正常,而用户却在经历无限制的队列延迟。

结果是:团队大量过度配置,为每个服务的模型保持热实例,用持续的 GPU 闲置成本来规避冷启动惩罚。这不是配置问题,而是 Kubernetes 所暴露的能力与 LLM 调度所需能力之间的根本性鸿沟。

内存碎片化问题

即使在请求到来之前,混合模型集群就已经因碎片化损失了大量容量。

一个服务 8B 模型的 vLLM 实例可能预分配 60GB GPU 内存,而在典型负载下只使用 35–40GB。这在传统意义上并非浪费——考虑到潜在的最坏情况上下文长度,这种分配是正确的——但这意味着其他模型无法共享该设备,即便它处于低利用率状态。

单看 KV 缓存需求,数字更加触目惊心。一个 100,000 token 上下文的 7B 模型大约需要 50GB 用于 KV 缓存,而模型权重本身只需 14GB。四分之三的内存消耗是瞬态上下文状态,而非模型本身。传统静态分配将这部分视为永久占用,阻断了其他用途对该容量的使用。

在 PagedAttention(vLLM 的核心创新)出现之前,系统通过碎片化浪费了 60–80% 的已分配 KV 缓存内存:不连续分配、最坏情况预分配、请求间的内部碎片。PagedAttention 将操作系统用于虚拟内存的分页技术应用于此,将 KV 缓存浪费降至 4% 以下。这在同等硬件上实现了 2–4 倍的吞吐量提升——不是通过增加 GPU,而是更好地利用现有 GPU。

量化提供了另一个杠杆。将 KV 缓存从 FP16 迁移到 FP8 可在质量影响极小的情况下将缓存大小减半;NVFP4 进一步压缩,在同等设备上支持更大的批次或更长的上下文。但量化本身并不能解决调度问题——它只是改变了调度器需要追踪的数字。

真正能回收闲置容量的方法

以下三种策略切实有效,按实现复杂度大致递增排序。

时间切片与 MIG:分区 GPU

当需要在单块大型 GPU 上运行多个小模型时,分区是最直接的方法。

时间切片(所有 NVIDIA 架构均支持)在工作负载之间使用轮询上下文切换,每个任务占用 1–2ms 后让出。它启用简单,普遍适用,但上下文切换开销是真实存在的,在持续负载下会累积。更适合突发性、对延迟容忍度高的工作负载,而非持续推理。

多实例 GPU(MIG)(仅 Ampere 及更新架构)在硬件层面对物理 GPU 进行分区,为每个实例提供隔离的计算核心、专用内存和独立的 L2 缓存。无上下文切换开销,保证资源隔离。代价是灵活性不足:MIG 分区大小在配置时固定,分区大小与工作负载大小的任何错配都会浪费容量。

混合方式——MIG 分区加上每个分区内的时间切片——结合了两者的优势。一项基准测试显示,通用工作负载吞吐量提升 6.2 倍、能耗节省近 6 倍;LLM 特定工作负载显示出更适中的 1.4 倍吞吐量提升。这个范围反映了这些数字对工作负载特性的高度敏感性。

MIG 和时间切片都不涉及决定哪个请求去哪个 Pod 的调度决策。它们是资源配置策略,而非路由智能。

连续批处理:消除队头阻塞

静态批处理——等待固定数量的请求后再开始批次——是从传统 ML 推理继承的默认行为。它会产生队头阻塞:一个提示词很长的早期请求会阻塞整个批次直到完成,无论后续请求的大小如何。

连续批处理通过迭代级调度消除了这个问题。每当一个序列完成一个解码步骤,批次就可以从队列中吸收一个新请求。不会有请求等待另一个请求完成全部生成。GPU 保持忙碌,短请求不会在长请求后面排队,吞吐量随负载扩展,而不受批次配置限制。

对于生产 LLM 服务,这不是可选项,而是基本门槛。vLLM 早期就实现了它。在同等硬件上,静态批处理与连续批处理在中等并发度下的差距是 2–4 倍吞吐量。

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates