4. 优先级定义:优先级概念、优先级度量、优先级分类
4.1 优先级概念:从“先来先到”到“按需排序”
在GPU集群中,优先级(Priority)是调度器为每个任务赋予的一种相对重要性标识,它决定了当多个任务同时竞争有限GPU资源时,谁先获得资源、谁可以抢占、谁被排队等待。优先级不是任务的固有属性,而是集群管理者根据业务目标、资源成本和用户等级人为设定的策略变量。
理解优先级需要区分三个容易混淆的概念:
- 优先级(Priority):任务在调度队列中的相对位置,是一个无量纲的排序值。
- 配额(Quota):某个租户或队列在长期内可使用的资源上限,与优先级无关。
- 公平性(Fairness):保证所有用户获得基本资源保障的机制,优先级不能无限度破坏公平。
一个直观的类比是医院急诊分诊:所有病人都需要治疗,但心跳骤停的病人(高优先级)必须排在感冒患者(低优先级)之前。GPU集群中的优先级调度正是这种“分诊逻辑”在计算资源上的映射。
4.2 优先级度量:数值范围与语义映射
优先级必须被量化为调度器可比较的数值。不同集群系统采用不同的度量方式,但主流方案遵循以下设计原则:
| 度量维度 | 取值范围 | 语义说明 | 典型系统 |
|---|---|---|---|
| 静态优先级 | 0 ~ 100 | 数值越大优先级越高,由管理员或用户提交时指定 | Slurm (QOS) |
| 动态优先级 | -1000 ~ +1000 | 随任务等待时间、资源利用率动态调整 | Kubernetes (PriorityClass) |
| 多级队列优先级 | 1 ~ 10(每级) | 层级队列中每层独立设置,最终取综合值 | YARN Capacity Scheduler |
| 抢占优先级 | 0 ~ 1000 | 仅用于决定是否允许抢占其他任务,不参与常规排队 | Volcano / Gang |
在实际集群中,优先级度量通常采用复合计算方式。一个典型的优先级计算公式为:
final_priority = base_priority
+ age_bonus(等待时间 × 0.1)
+ urgency_bonus(紧急任务标记 ? 50 : 0)
- queue_depth_penalty(当前队列长度 × 0.05)
这种设计避免了“高优先级任务永远插队”导致的低优先级任务饿死(Starvation)问题。例如,一个初始优先级为30的任务,在队列中等待2小时后,其动态优先级可能上升到50,从而有机会获得调度。
4.3 优先级分类:从不同视角划分任务等级
GPU集群中的优先级并非单一维度,而是可以从多个视角进行分类。理解这些分类有助于设计合理的调度策略。
4.3.1 按任务类型分类
- 交互式任务(Interactive):如Jupyter Notebook、调试会话,需要快速响应,通常赋予最高优先级(80-100)。
- 生产型批处理任务(Production Batch):如定期模型训练、推理服务,优先级中等偏高(60-80)。
- 开发测试任务(Dev/Test):如代码验证、小规模实验,优先级较低(30-50)。
- 后台维护任务(Maintenance):如数据备份、日志清理,优先级最低(0-20),仅在资源空闲时运行。
4.3.2 按用户/租户分类
多租户场景下,优先级与组织层级绑定。例如:
租户层级:
├── 生产部门(优先级 90)
│ ├── 推荐算法组(子优先级 80)
│ └── 广告点击率组(子优先级 70)
├── 研发部门(优先级 60)
│ ├── 算法研究组(子优先级 50)
│ └── 基础设施组(子优先级 40)
└── 外部客户(优先级 30)
4.3.3 按资源特征分类
某些任务对资源有特殊要求,需要单独设置优先级维度:
- 多卡任务(Multi-GPU):需要8卡或更多GPU的任务,因资源难以凑齐,可适当提高优先级以避免长期等待。
- 抢占敏感任务:如模型推理服务,一旦被抢占会导致线上故障,应设置“不可抢占”标记。
- 时间敏感任务:如比赛提交、定时报告生成,可设置截止时间(Deadline),调度器根据紧迫程度动态提升优先级。
4.4 案例:某互联网公司GPU集群优先级配置
以下是一个实际生产环境的优先级配置示例,展示了如何将上述分类落地为可执行的调度策略:
# Slurm QOS 配置示例
QOS=production
Priority=80
MaxWallClock=72:00:00
Preempt=NO
QOS=interactive
Priority=95
MaxWallClock=08:00:00
Preempt=YES
PreemptMode=REQUEUE
QOS=dev
Priority=40
MaxWallClock=24:00:00
Preempt=YES
PreemptMode=CANCEL
QOS=maintenance
Priority=5
MaxWallClock=UNLIMITED
Preempt=NO
GrpTRES=gpu=0 # 仅在空闲时运行
该配置的效果是:当GPU资源紧张时,调度器按优先级排序(interactive 95 > production 80 > dev 40 > maintenance 5)。如果高优先级任务到达但资源不足,系统会检查是否有可抢占的低优先级任务。例如,一个dev任务运行了2小时,可以被新到达的interactive任务抢占并取消。
4.5 误区与复盘:优先级设计中的典型问题
在多个GPU集群的实际运维中,以下优先级相关的错误频繁出现,值得特别关注:
| 误区 | 后果 | 正确做法 |
|---|---|---|
| 优先级粒度太粗(只有高/低两档) | 无法区分“紧急但不重要”和“重要但不紧急”的任务 | 至少设置5个等级,并允许动态调整 |
| 忽略等待时间补偿 | 低优先级任务永远无法运行,最终超时被杀死 | 引入年龄增长机制(Aging),每等待30分钟优先级+1 |
| 抢占策略过于激进 | 频繁抢占导致任务反复重启,GPU利用率反而下降 | 设置最小运行时间(如至少运行15分钟才允许被抢占) |
| 未考虑任务依赖关系 | 高优先级任务依赖的低优先级上游任务未运行,导致整体阻塞 | 使用DAG调度,依赖链上的任务继承最高优先级 |
复盘一次典型事故:某集群管理员将所有训练任务设为优先级90,而推理服务设为80。当训练任务集中提交时,推理服务被全部挤到队尾,导致线上推荐接口超时。根因是管理员没有理解“推理服务虽然单任务优先级低,但它是持续运行的在线服务,应使用预留资源而非优先级来保障”。修复方案是为推理服务配置独立的资源池(如30% GPU),与训练任务物理隔离,优先级只用于训练任务之间的竞争排序。
优先级定义是调度策略的基石。一个设计良好的优先级体系,应当让调度器在绝大多数情况下无需人工干预即可做出合理决策。当需要频繁手动调整优先级时,往往说明初始定义未能反映真实的业务需求,需要重新审视分类维度和数值映射。