flowchart TD
subgraph QuotaPool["集群总资源配额池 (1000 GPUs)"]
TeamA["部门 A 保证配额 (Guaranteed: 400 GPUs)"]
TeamB["部门 B 保证配额 (Guaranteed: 400 GPUs)"]
Shared["共享空闲与突发池 (Oversubscription: 200 GPUs)"]
end
subgraph DRF["主导资源公平调度 (Dominant Resource Fairness)"]
Dominant["根据 CPU、GPU、内存中占比最高的主导份额实现均衡分配"]
end
QuotaPool --> DRF
第 6 课:容量、配额、公平与 Backfill
参考答案版:包含完整代码实现与问答题深度参考解析。建议先独立完成练习题后再展开核对。
本课目标与完成标准
本课产出:实现不延误保留作业的 backfill 选择,并解释利用率、公平性与碎片的冲突。
通过要求:唯一代码填空题通过给定检查;Q1~Q3 都给出因果链;能指出一个正确性边界和一个性能/运营取舍;总分至少 8/10。
与既有六条主线的边界
上一课解决单个 gang 放置;本课处理多租户队列、配额、优先级、reservation 与空洞利用。
前置:Linux/网络基础、runtime 补充线、train 分布式章节。本课只补 AI Infra 缺口,不重复已经完成的 kernel 或并行算法推导。
核心心智模型
1. 它是什么,解决什么问题
在多团队共享的大规模企业级 AI 算力中心(通常汇聚数百至数千张高端 GPU,单卡硬件与能耗成本极高),平台运营与调度系统面临着三大核心诉求的激烈博弈:
多租户保底配额与商业公平性(Guaranteed Quota & Tenancy Isolation):
团队 A 采购了 400 张 GPU,团队 B 采购了 400 张 GPU。平台必须确保任何团队随时都能兑现其购买的“保证配额(Guaranteed Quota)”,杜绝因某一团队恶意突发提交上千任务而将其他团队完全挤占;同时,在某些团队配额闲置时,应允许其他团队弹性突发超售(Elastic Bursting / Oversubscription)借用,极大化集群资产的投资回报率(ROI,如图 6-1 所示)。多维异构资源的瓶颈均衡(Multi-Resource Fair Allocation):
大模型端到端工作流不仅需要 GPU,还重度消耗 CPU 核心(负责数据解压、Tokenization 与分词)、Host 内存(用于显存溢出与 Page Cache)、以及 RDMA 网络带宽。传统单一维度的配额无法解决“CPU 被打满而 GPU 闲置”的资源倾斜,需要引入主导资源公平调度算法(Dominant Resource Fairness, DRF)。高优先级大作业等待与算力空洞(The Reservation vs. Utilization Dilemma):
当一个千亿大模型训练任务(如申请 64 卡或 512 卡)到达队首时,当前集群仅有部分卡空闲(例如 8 张卡空闲,其余 56 张卡需等待 30 分钟后正在运行的前序任务释放)。若为了等待该大任务而将这 8 张卡完全闲置 30 分钟,集群利用率将发生断崖式下跌;若直接把这 8 张卡分配给一个运行 10 小时的普通长任务,大任务的启动时刻将被严重延误!
回填调度(Backfill Scheduling)通过计算确定性的“保留时间窗口(Reservation Window)”,利用时间与空间维度上的缝隙,精准调度短小作业填充算力空洞(如图 6-2 所示),在零推迟大作业启动的前提下最大化集群利用率。
2. 它如何工作
(1) 多租户配额模型与主导资源公平(DRF,如图 6-1 所示)
图 6-1:多租户配额保障、弹性突发超售与 DRF 资源公平模型
分层配额池设计(Hierarchical Quota Pools):
平台将物理卡池划分为“保证配额(Guaranteed)”与“共享突发池(Shared Elastic Pool)”。租户在保证限额内提交的任务享有最高优先级;超出保证限额的任务只能作为“可抢占突发任务(Preemptible Burst Jobs)”在共享池中运行。一旦保证配额的合法持有者提交新作业,突发任务将被优雅回收退让。主导资源公平算法(Dominant Resource Fairness, DRF):
设集群总资源向量为 。
对于租户 ,其当前已占用的资源向量为 。
租户 的主导资源份额(Dominant Share)定义为其占全集群比例最高的那项资源的份额: 调度器在每一轮调度中,始终优先为当前主导份额 最小的租户分发资源,从而在多维异构环境下严格实现帕累托最优与无嫉妒性(Envy-Freeness)。
(2) 回填调度算法与保留时间窗口填充(Backfill Scheduling,如图 6-2 所示)
flowchart LR
LargeJob["高优先级大作业等待 64 卡 (预计 30 分钟后到位)"] --> LockTime["调度器计算预留时间窗口 (Reservation Window)"]
LockTime --> ScanSmall["扫描等待队列中的碎片小作业 (如 4 卡、运行 15 分钟)"]
ScanSmall --> BackfillCheck{"小作业是否能在预留时间前结束?"}
BackfillCheck -->|是| RunNow["立即回填调度 (Backfill) 启动小作业,极大化集群利用率"]
BackfillCheck -->|否| WaitSmall["小作业保持等待,确保绝不推迟大作业启动时刻"]
图 6-2:回填调度 (Backfill Scheduling) 与保留时间窗口填充工作流
如图 6-2 所示,Backfill 调度的核心执行流水线分为三步:
建立大作业未来预留点(Reservation Window):
队首高优先级大任务因资源不足无法立即启动。调度器依据当前所有运行中作业声明的截止时间(Walltime),向前模拟时间线,推算出该大任务最早能够集齐全部卡数的未来时间点 。调度器将该大作业锁定在该时间点,并计算出当前的空闲卡数 与可用时间空洞 ;回填候选任务可行性过滤(Feasibility Filter):
调度器跳过队首,遍历排队队列中的低优先级或短小任务。候选作业 允许被“回填(Backfill)插队”的充要条件是: 这一约束保证了该作业必然在 之前运行完毕并释放资源,绝对不推迟队首大作业的预定启动时刻。价值最大化排序目标(GPU-Time Maximization):
在所有满足可行性约束的候选作业中,调度器优先挑选能够最大化填充物理算力面积的作业,即最大化 。平局时通过作业名称字典序实现确定性裁决。
3. 正确性条件与常见误区
误区一:按“申请 GPU 数量最少”作为回填贪心策略
许多人直觉认为“卡数越小的作业越容易填洞,因此应该最先调度卡数最少的小作业”。然而,极小作业(如 1 卡运行 2 分钟)产生的 GPU-Time 极低(仅 2 GPU·min),频繁调度它们会留下大量无法拼凑出稍大规格的微观碎片,反而降低了整体算力吸收率。回填必须以最大化“GPU-Time 吞吐总面积()”为优化目标。误区二:无条件信任用户填写的申请运行时间(Walltime)
Backfill 的正确性前提是“回填作业绝不推迟大作业的启动时间”。然而在工程实践中,用户填写的时长往往极不准确(例如申报运行 10 分钟,实际由于收敛慢跑了 2 小时)。如果调度器未设置硬性强制超时杀任务(Hard Walltime Enforcement),超时的回填任务将直接霸占资源,导致大作业的预留点被严重延误!系统必须建立基于历史运行数据的运行时预测校准,并在超时时自动触发 Checkpoint 保存或强制终止。误区三:高估高优先级抢占(Preemption)的零成本性
平台不能因为支持硬抢占,就肆无忌惮地随意终止低优先级任务。杀掉一个运行了 6 小时的分布式任务,若其未能及时落盘 Checkpoint,意味着白白浪费了数十卡时昂贵算力与电力,甚至造成数据集读写管道损坏。抢占必须将 Checkpoint 转储成本、网络抖动与级联重试开销全面纳入综合收益模型。
4. 性能与工程取舍
- 保守回填(Conservative Backfill)vs. 激进回填(EASY Backfill):
- 保守回填:为队列中每一个排队作业都计算并锁定一个严格的未来预留点,极度公平且高度可预测,但会产生过多保守的保留空洞,导致集群平均利用率偏低;
- EASY 回填:仅为队首的第 1 个大作业维护单一预留点,后续所有小作业只要不推迟这唯一的队首作业即可任意插队抢跑。EASY 回填极大提升了集群吞吐与利用率,是目前高性能超算平台(如 Slurm、Volcano)的主流工程实现。
- 配额超售(Oversubscription)与排队体验的权衡:
过高的弹性突发超售比(如 200% 超售)能让集群时刻处于打满状态,但一旦多个部门在月末集中提交突发实验,会引发大面积的抢占排队风暴,导致任务反复失败重跑。工程上通常将弹性超售比例控制在总池的 15%~30% 之间。
具体演示
设当前集群空闲 free_gpus = 8,距离队首大作业的保留时刻还有 gap_minutes = 30 分钟:
- 候选作业列表为:
a(4 卡,20 分钟)、b(8 卡,40 分钟)、c(2 卡,25 分钟)。
分析过程如下:
- 可行性筛选:
- 作业
a: 且 ,可行,贡献算力面积为 ; - 作业
b:运行时间 ,若启动将推迟大作业,属于不可行候选,被剔除; - 作业
c: 且 ,可行,贡献算力面积为 。
- 作业
- 最优选择: 作业
a的算力面积(80)显著高于作业c(50),算法确定性选择作业a立即执行回填启动,高效利用空洞。
请先独立复述“输入 → 状态变化 → 输出/指标”,再开始填空。
实践任务:唯一代码填空题
补齐确定性 backfill:选择可完成候选中 GPU-time 最大者。
只能修改 TODO/______ 位置,不得删除断言或放宽通过条件。
Code
def choose_backfill(candidates, free_gpus, gap_minutes):
"""candidate=(name, gpus, minutes)"""
feasible = [job for job in candidates
if job[1] <= free_gpus and job[2] <= gap_minutes]
if not feasible:
return None
# TODO:最大化 gpus*minutes;平局按名称最小。
return ______
jobs = [("a", 4, 20), ("b", 8, 40), ("c", 2, 25)]
assert choose_backfill(jobs, 8, 30) == ("a", 4, 20)
assert choose_backfill(jobs, 1, 30) is None检查方法
运行本单元格,所有断言必须通过;再补一个边界输入并解释预期。
Q1
为什么按 GPU 数最少优先不一定提高整体利用率?
你的答案:
Q2
用户低估作业时长,backfill 会造成什么?如何防护?
你的答案:
Q3
高优先级抢占低优先级作业前应计算哪些成本?
你的答案:
评分规则
- 代码 4 分:正常输入 2 分、边界输入 1 分、解释 1 分;
- Q1~Q3 各 2 分;
- 一票否决:混淆测量与推测、忽略租户/请求隔离、用平均值掩盖尾延迟、删除失败路径。