4. 优先级定义:优先级概念、优先级度量、优先级分类

4.1 优先级概念:从“先来先到”到“按需排序”

在GPU集群中,优先级(Priority)是调度器为每个任务赋予的一种相对重要性标识,它决定了当多个任务同时竞争有限GPU资源时,谁先获得资源、谁可以抢占、谁被排队等待。优先级不是任务的固有属性,而是集群管理者根据业务目标、资源成本和用户等级人为设定的策略变量。

理解优先级需要区分三个容易混淆的概念:

  • 优先级(Priority):任务在调度队列中的相对位置,是一个无量纲的排序值。
  • 配额(Quota):某个租户或队列在长期内可使用的资源上限,与优先级无关。
  • 公平性(Fairness):保证所有用户获得基本资源保障的机制,优先级不能无限度破坏公平。

一个直观的类比是医院急诊分诊:所有病人都需要治疗,但心跳骤停的病人(高优先级)必须排在感冒患者(低优先级)之前。GPU集群中的优先级调度正是这种“分诊逻辑”在计算资源上的映射。

核心要点:优先级解决的是“资源不够时谁先用”的问题,而不是“谁拥有资源”的问题。没有竞争时,所有任务都能运行;只有资源紧张时,优先级才真正发挥作用。
GPU集群优先级调度核心概念 任务提交 任务A:模型训练 优先级=100(最高) 任务B:批量推理 优先级=50(中) 任务C:数据预处理 优先级=10(低) 调度器 比较优先级 检查资源可用性 决定执行顺序 处理抢占/排队 GPU资源池 GPU节点1 空闲 占用 GPU节点2 空闲 占用 GPU节点3 空闲 空闲 GPU节点4 占用 空闲 分配 低优先级任务排队等待

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,从而有机会获得调度。

实践建议:在设置优先级数值时,应预留足够的间隔(如10的倍数),以便后续插入新的优先级等级而不需要重新编号。同时,避免使用极端值(如0或100),因为极端值通常被系统保留用于特殊场景(如系统任务或紧急运维任务)。

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任务抢占并取消。

常见误区:不要将所有任务都设置为高优先级。如果所有任务优先级都是90,那么优先级机制就失去了区分度,调度器只能退化为先来先服务。合理的做法是:高优先级任务占比不超过20%,确保优先级能真正反映业务重要性差异。

4.5 误区与复盘:优先级设计中的典型问题

在多个GPU集群的实际运维中,以下优先级相关的错误频繁出现,值得特别关注:

误区 后果 正确做法
优先级粒度太粗(只有高/低两档) 无法区分“紧急但不重要”和“重要但不紧急”的任务 至少设置5个等级,并允许动态调整
忽略等待时间补偿 低优先级任务永远无法运行,最终超时被杀死 引入年龄增长机制(Aging),每等待30分钟优先级+1
抢占策略过于激进 频繁抢占导致任务反复重启,GPU利用率反而下降 设置最小运行时间(如至少运行15分钟才允许被抢占)
未考虑任务依赖关系 高优先级任务依赖的低优先级上游任务未运行,导致整体阻塞 使用DAG调度,依赖链上的任务继承最高优先级

复盘一次典型事故:某集群管理员将所有训练任务设为优先级90,而推理服务设为80。当训练任务集中提交时,推理服务被全部挤到队尾,导致线上推荐接口超时。根因是管理员没有理解“推理服务虽然单任务优先级低,但它是持续运行的在线服务,应使用预留资源而非优先级来保障”。修复方案是为推理服务配置独立的资源池(如30% GPU),与训练任务物理隔离,优先级只用于训练任务之间的竞争排序。

优先级定义是调度策略的基石。一个设计良好的优先级体系,应当让调度器在绝大多数情况下无需人工干预即可做出合理决策。当需要频繁手动调整优先级时,往往说明初始定义未能反映真实的业务需求,需要重新审视分类维度和数值映射。