写点什么

Slurm 集群调度策略详解(2)- 回填调度

作者:慕冰
  • 2023-02-17
    河南
  • 本文字数:12830 字

    阅读完需:约 42 分钟

1. slurm 集群调度系统简介

作业调度系统其实就是指按一定策略把并行程序的各子任务或者是面向多用户的一组作业按一定的选择策略使之与各计算节点的空闲资源相对应,让其在对应的资源上运行,所以作业调度系统是联系上层应用和下层资源的中枢。一个集群资源的利用是否合理,各计算节点的负载是否均衡,以及各用户的用户体验是否良好都与作业调度系统息息相关,集群的资源管理及作业调度系统可谓是集群发展的重中之重。

Slurm 是一个开源,高度可扩展的集群管理工具和作业调度系统,可以简单理解为一个多机的资源和任务管理系统。主要以下提供三种关键功能:

资源分配:在特定时间段内为用户分配计算资源,进行独占或非独占访问权限,以便他们可以执行作业。简单的说就是为用户作业提供对计算资源的授权和分配。

作业管理:它提供了对节点上的作业节进行启动、执行和监控作业的框架。

作业调度:通过管理待处理作业的队列来仲裁资源的争用。例如根据优先级或不同调度策略调整资源的分配顺序。

Slurm 调度系统中针对作业的调度主要有三种,主调度,回填调度,GANG 调度。在之前的《slurm 集群调度策略详解(1)- 主调度》文章中,介绍了主调度的一些相关知识,本篇文章主要针对 slurm 中的回填调度进行解析。

2. 回填调度

2.1 回填调度算法简介

常见的先来先服务作业调度方法,实现简单、实用性很强。然而,为了保证公平性原则,当需要调度一个资源需求较大而现有空闲资源不能满足的大作业时,往往需要为其预留资源,这就容易产生大量闲置的资源碎片。回填算法,其允许为大作业预留资源的同时,也会将预约过程中产生的资源碎片进行分配;就是说可以让作业等待队列中排在后面的小作业跳过预约资源的大作业先于该大作业被调度执行,这样既避免了大作业长时间得不到调度而产生饥饿现象又可以让资源碎片得到充分利用,其能够很大程度上提高作业的平均响应时间,增加系统集群的吞吐量以及资源利用率。

目前,回填策略有很多种实现模式,比较典型的有 Easy Backfilling 和 Conservative Backfilling 两种策略算法,其它基于回填的调度策略的研究基本上都是在这两种策略基础上的微调。

2.1.1 Easy Backfilling

EasyBackfilling 算法是一种比较“激进”的回填算法,它仅仅要求回填后的作业不会搁置等待队列第一个作业(触发资源预留的作业)的执行就行,而不去考虑在其他任何在等待队列中排在其前面的作业。假设每个作业都可以由一个二元组(cpu 核数需求,预计执行时间)来描述,则 Easy Backfilling 算法的整个执行过程如下步骤所述:

1.在资源时空图(横坐标为时间,纵坐标为 CPU 核数)中寻找作业的插入点以及对应的剩余 CPU 核数。

① 对于每个运行中的作业,都可以算出预计完成时间,根据这个截止时间对所有正在运行的作业进行排序。

② 在资源时空图中寻找剩余 CPU 核数可以满足等待队列中第一个作业的 CPU 核数需求的最早的一个时间点,这个时间点就是与该队列首作业 job1 对应的插入点。

③ 插入点对应的时刻,集群剩余 CPU 核数将更新(原值减去作业 job1 的需求核数)。


2.遍历等待队列中的作业,寻找一个可以被回填的作业。

① 对每一个作业进行检查,看其是否满足下列条件中的一个。

1)该作业需求的 CPU 核数小于当前时刻集群剩余的 CPU 核数,并且预计能够在插入点之前运行完毕(不延 迟作业 job1 的调度执行)。

2)该作业需求的 CPU 核数小于或等于 min(当前集群空闲的 CPU 核数,当前系统剩余的 CPU 核数)。

*注:集群的空闲 CPU 核数与系统剩余 CPU 核数是不一样的,空闲 CPU 核数是对应时刻集群真实的闲置的 CPU 核数,而剩余 CPU 核数是回填决策后资源时空图的剩余 CPU 核数。

② 选择第一个满足以上条件的作业作为回填作业。、3.随着时间实时更新资源时空图,重复以上两个步骤,不停进行回填决策。


这种算法前瞻性不足,回填决策时只考虑回填作业是否延误等待队列中第一个作业的执行,这就很容易导致作业执行的不可预测性,导致等待队列前面的一些作业响应时间非常长。

2.1.2 Conservative Backfilling

Conservative Backfilling 算法是相对于 Easy backfilling 来说非常“保守”的一种回填调度策略。这种策略当选取等待队列中某个作业进行回填时,该被选中的作业必须保证不能延迟所有在队列中排在其前面的作业的执行,这种回填策略的优点在于:如果作业预计的执行时间比较准确,那么队列中作业的开始执行时间都是可预见的,从而可以给每个作业所属用户一个公平性保证。

同样,Conservative Backfilling 算法的执行过程可以描述成如下几个步骤:

1.顺序地确定等待队列中各作业的插入点。

① 对当前的资源时空图进行遍历,找到满足该作业 CPU 核数需求的第一个时问点,该时间点即为该作业对应的插入点。

② 从该作业对应的插入点开始,遍历资源时空分布图,检查集群剩余 CPU 核在该作业执行完毕前的这段时内是否一直可以满足该作业的执行资源需求。

③ 如果在作业执行完之前有段时间剩余 CPU 核数不满足该作业资源需求,则继续在资源时空分布图中往后遍历,直到为该作业找到一个满足条件的作业插入点。

2.每当为一个作业确定插入点后,得实时更新资源时空分布图以反映该作业调度后的资源分配情况。3.每个作业在其对应的插入点时刻开始被调度执行。


通常 Conservative Backfilling 回填是更被广泛认可的,因为计算资源的有偿使用,计算平台在保证服务水平协议的情况下尽可能保证所有用户之间的公平性也是非常重要的。总而言之,在高性能计算环境中,回填调度是一种高效、易于实现且很重要的一类调度算法。在将新作业加入队列时,考虑以下两个因素:


  1. 当前队列中正在运行的作业;

  2. 队列中等待运行的作业。


回填调度算法的主要目的是通过利用系统的空闲资源来减少作业的等待时间,提高系统的资源利用率。它适用于需要提供快速响应时间的环境,例如,高性能计算和分布式系统等。

2.2 回填调度在 slurm 中的应用

回填调度在 slurm 中采用基于 Conservative Backfilling 的算法,通过插件的形式来提供服务。slurmctld 服务在启动时,默认或根据配置项 SchedulerType=sched/backfill 进行回填调度插件的加载。

在没有回填调度的情况下,每个分区都严格按照作业优先级顺序进行调度,一旦一个分区中的任何作业或作业数组任务因为资源不足处于排队状态时,该分区中的任何其他低优先级作业都不会被调度,这通常会导致资源利用率低和作业调度效率低等问题。回填调度充分利用大作业排队的时间间隙,在不会延迟任何较高优先级大作业预期开始时间的前提下,将优先调度低优先级的小作业启动运行。回填调度的使用,将提高小作业的响应时间,而且提高系统资源的利用率。

slurm 中主调度和回填调度对于相同作业的调度结果,如图 1 所示:



图 1 主调度和回填调度对于相同作业的调度结果

3. 回填调度参数

在 slurm 中,使用 SchedulerType 配置参数指定要使用的调度器插件:选项有 sched/backfill(执行回填调度)和 sched/builtin(试图在每个分区/队列中严格按照优先级顺序调度作业);使用 SchedulerParameters 配置参数,可以指定更多与调度规则相关的选项。按照参数功能,可以将回填调度相关的参数分为以下几类:


  1. 回填调度行为相关配置项

  2. 回填预留条件相关配置项

  3. 回填深度相关配置项

  4. 回填广度相关配置项

  5. 回填调度锁机制相关配置项


下面对于每类配置项进行详细的介绍。

3.1 回填调度行为相关配置项

在 slurm 中,回填调度是一个周期行为,每两次回填之间的间隔、每次回填考虑的时间窗口长度、回填考虑的时间分辨率、异构作业的处理等都是由回填调度行为相关配置项来决定的,具体配置项如下表所示。



3.2 回填预留条件相关配置项

回填调度主要是在没有空闲资源时,为高优先级的作业进行资源的回填预留,以保证高优先级作业能够在尽可能少的等待时间里得到资源去运行。作业能否被创建回填预留,与以下回填预留条件配置项相关:



3.3 回填深度相关配置项

每轮回填持续多久、回填多少作业之后退出等,是由回填深度相关配置项决定的,具体配置参数如下表所示:



3.4 回填广度相关配置项

每轮回填每个分区能够尝试多少作业、每个用户能够尝试多少个作业等,是由回填广度相关配置项决定的,具体配置参数如下表所示:


3.5 回填锁机制相关配置项

回填调度是一个很耗时的操作,在回填调度时会持有作业和节点的写锁;调度流程之外与作业和节点相关的操作都会被阻塞,调度系统的待处理事件会快速累积。因此,slurm 在进行回填调度时,会定期进行锁的释放,以便系统能处理其他请求。多久释放一次锁、每次释放多长时间、释放锁之后是否要继续之前的回填调度等,都是由以下表格中与锁机制相关配置项决定的。


对于每个配置项,slurm 都会根据经验给出默认值;实际使用时,需要根据集群大小、用户作业特征等情况,进行配置项的调整,以便得到最适合自己集群的回填调度效果。例如:集群节点较多、用户作业运行时间一般较长时,需要尽可能调大回填调度考虑的时间窗口 bf_window,才能使回填调度真正起作用。

4. 回填调度流程

Slurm 默认选择 sched/backfill 作为调度插件;为了提高调度效率,集群一般也都会配置 sched/backfill。回填调度的流程,主要涉及到插件的初始化、待调度作业列表的构建、作业的调度尝试、资源的时间表格构建、锁的释放等工作。

4.1 回填调度插件初始化流程

在 slurmctld 服务启动时,根据 SchedulerType 配置项,来进行调度插件的加载和初始化。如图 2 所示,为回填调度插件的加载流程:



图 2 回填调度插件的加载流程在回填调度插件的 init 函数中,进行回填调度线程的创建;大致的函数调用关系如下:


main // slurmctld - controller.c    slurm_sched_init // 调度插件初始化      plugin_context_create // 插件上下文的创建             plugin_load_and_link // 插件加载和链接                plugin_load_from_file // 从文件中加载插件                    init = dlsym(plug, "init") // 调用插件的初始化函数              slurm_thread_create(&backfill_thread, backfill_agent, NULL); // 创建回填调度的线程
复制代码


在回填调度线程函数 backfill_agent 中,以 while (!stop_backfill)的形式一直运行,直到 slurmctld 服务退出;在循环的内部,通过判断当前时间与上次回填调度结束时间的间隔是否大于 backfill_interval,来决定是否开启新的一轮回填调度。回填调度线程函数 backfill_agent 的大致执行流程如图 3 所示:



图 3 回填调度线程的创建和退出流程图

4.2 待调度作业列表的构建

在每轮回填调度_attempt_backfill 中,首先会进行待调度作业列表 job_queue 的构建。构建方法为:① 遍历全局的 job_list 列表(当前调度系统中所有状态的作业集合),将所有排队状态的作业的预期启动时间 start_time 进行清除;② 对于提交到多个分区的作业,会创建与指定分区数相同多的待调度作业;③ 添加待调度作业到 job_queue 中;④ 按照优先级从高到低的顺序,将 job_queue 中的作业进行排序;至此,完成待调度作业列表的构建。



图 4 待调度作业列表的构建流程

4.3 作业尝试回填调度

在作业回填调度之前,调度系统会对所有回填调度相关的参数进行解析;随后待调度作业会按照优先级从高到低的顺序从 job_queue 中出栈;出栈后的作业会经过调度参数和作业自身限制的筛选,最终进入节点选择和启动环节。回填调度的主流程如图 5 所示:



图 5 回填调度的主流程


Slurm 的回填调度程序会考虑每个正在运行的作业;然后按优先顺序考虑排队的作业,确定每个作业何时何地开始,同时考虑作业抢占的可能性、gang 调度、通用资源 (GRES) 要求、内存要求等。作业在提交时,会根据作业指定的 time limit 参数或者分区配置的 time limit 参数,来为作业设置时间限制 time limit。由于排队作业的预期开始时间取决于正在运行作业的预期完成时间,因此作业在提交时制定一个合理准确的时间限制(time limit)对于回填调度的正常运行非常重要。如果正在回填调度的作业立即启动而不会影响任何更高优先级作业的预期开始时间,则该作业会启动运行;否则,回填插件将为排队的作业设置预期开始时间,该作业所需的资源将在作业的预期执行时间内被预约。

4.4 节点资源的时间表格构建

在 slurm 的回填调度中,会为排队作业计算预期启动时间和结束时间;在合适的计算节点上对应预期时间段内,为作业进行资源预留。出于性能原因,即使作业不需要整个节点,回填调度也会为作业保留整个节点。相当于将计算节点资源按照时间顺序,进行表格的构建:在不同的时间段内,将节点预留给不同的作业。在回填调度最开始时候,所有可用的计算节点处在同一时间表格中,时间段的长度由配置项 bf_window 决定。


// 时间表格开始时间:当前轮次回填调度开始时间node_space[0].begin_time = sched_start;
// 由backfill_window配置项决定结束时间window_end = sched_start + backfill_window;node_space[0].end_time = window_end;
// 所有可用的节点,都将被考虑node_space[0].avail_bitmap = bit_copy(avail_node_bitmap);
复制代码


当前有包含 8 个可用节点(node[1-8])的集群,下面举例说明提交申请不同节点作业时候,对于节点资源时间表格的构建过程(基于 slurm20.11.8 版本)。(1)按顺序提交申请不同节点数量的作业


  a. sbatch -N 6 --exclusive -t 24:00:00 test.job  // 申请6个节点并独占, 时间限制为24小时  b. sbatch -N 3 --exclusive -t 12:00:00 test.job  // 申请3个节点并独占, 时间限制为12小时  c. sbatch -N 4 --exclusive -t 12:00:00 test.job  // 申请4个节点并独占, 时间限制为12小时  d. sbatch -N 2 --exclusive -t 10:00:00 test.job  // 申请2个节点并独占, 时间限制为10小时
复制代码


(2)主调度为 job1 分配资源


Allocate JobId=188287 NodeList=node[1-6]
复制代码



图 6 job1 资源分配示意图


(3)当前空闲资源不足,主调度无法为 job2 选出合适资源,job2 排队;空闲资源不满足 job2 运行,回填调度为 job2 预约资源;


// 构建节点资源时间表格 bf_window=43200 (30天)_dump_node_space_table: Begin:2023-02-08T11:33:35 End:2023-03-10T11:33:35 Nodes:node[1-8]
// 在当前时间段可用节点上,进行探测_dump_job_test: Test JobId=188288 at 2023-02-08T11:33:35 on node[1-8]
// 根据当前在运行作业的time limit,计算job2的开始时间;根据job2的time limit,计算结束时间 _dump_job_sched: JobId=188288 to start at 2023-02-09T11:33:24, end at 2023-02-09T23:33:00on nodes node[1-3] in partition debug
// 为所有可用节点重新划分时间表格,表示在以下时间段内,对应的节点未被回填调度预约:_dump_node_space_table: Begin:2023-02-08T11:33:35 End:2023-02-09T11:33:00 Nodes:node[1-8]_dump_node_space_table: Begin:2023-02-09T11:33:00 End:2023-02-09T23:33:00 Nodes:node[4-8]_dump_node_space_table: Begin:2023-02-09T23:33:00 End:2023-03-10T11:33:35 Nodes:node[1-8]
复制代码



图 7 job2 回填预留示意图


(4)当前空闲资源不足,主调度无法为 job3 选出合适资源,job3 排队;空闲资源不满足 job3 运行,回填调度为 job3 预约资源;


// 构建节点资源时间表格 bf_window=43200 (30天)_dump_node_space_table: Begin:2023-02-08T11:33:43 End:2023-03-10T11:33:43 Nodes:node[1-8]
// 在当前时间段可用节点上,进行探测_dump_job_test: Test JobId=188289 at 2023-02-09T11:33:24 on node[4-8]
// 根据当前在运行作业的time limit,计算job3的开始时间;根据job3的time limit,计算结束时间 _dump_job_sched: JobId=188289 to start at 2023-02-09T11:33:24, end at 2023-02-09T23:33:00on nodes node[4-7] in partition debug
// 为所有可用节点重新划分时间表格,表示在以下时间段内,对应的节点未被回填调度预约:_dump_node_space_table: Begin:2023-02-08T11:33:43 End:2023-02-09T11:33:00 Nodes:node[1-8]_dump_node_space_table: Begin:2023-02-09T11:33:00 End:2023-02-09T23:33:00 Nodes:node8_dump_node_space_table: Begin:2023-02-09T23:33:00 End:2023-03-10T11:33:43 Nodes:node[1-8]
复制代码



图 8 job3 回填预留示意图


(5)存在高优先级作业因为资源不足而排队,主调度无法为 job4 分配资源,job4 排队;当前空闲资源满足 job4 运行,且不影响 job2、job3 的启动时间,回填调度为 job4 分配资源并启动运行;


// 构建节点资源时间表格 bf_window=43200 (30天)_dump_node_space_table: Begin:2023-02-08T11:33:49 End:2023-03-10T11:33:49 Nodes:node[1-8]
// 在当前时间段可用节点上,进行探测_dump_job_test: Test JobId=188290 at 2023-02-08T11:33:49 on node[1-8]
// 发现可用的资源 _start_job: Started JobId=188290 in debug on node[7-8]
复制代码



图 9 job4 回填选择资源示意图

资源的时间表格构建,依赖于作业的预期启动时间和结束时间。回填调度构建待调度作业列表时,会将排队作业的预期启动时间清除,以便于在接下来的回填调度中,为其赋上重新计算得到的预期启动时间。

4.5 回填调度的节点选择

在对一个作业进行回填调度时,会先进行尝试调度;根据尝试调度的结果,分为三种情况来处理:


  1. 当前有足够的空闲资源运行该作业;申请对应的资源,启动作业立即运行;

  2. 当前没有足够空闲资源运行该作业,但在回填考虑的时间窗口内,有合适的资源可以创建回填预留;为该作业创建回填预留;

  3. 当前没有足够空闲资源运行该作业,在回填考虑的时间窗口内,没有合适的资源可以创建回填预留;不考虑为该作业创建回填预留;


回填调度的大致流程如图 10 所示:



图 10 回填调度的大致流程


在回填调度流程中,如果没有空闲资源能够立即启动作业,则在合适的节点上,根据时间表格,计算作业的预期启动时间和结束时间,并在相应的节点上进行回填预留的创建。若有空闲资源能够满足作业要求,通过 select_nodes 函数,在当前空闲资源中选择符合作业申请资源的最佳节点,并启动作业去运行。具体流程如图 11 所示。



图 11 回填调度节点选择流程示意图

流程解析:


  1. 构建具有必要配置的节点表(node_set_ptr:包含 slurm_node.conf 中所有节点);每个表格条目包括它们的权重、节点列表、特征等;

  2. 调用_pick_best_nodes,从满足作业规格的所有节点的权重顺序列表中,选择“最佳”以供使用;

  3. 如果指定了必需的节点列表,则确定隐式必需的处理器和节点数;

  4. 确定表示了多少不相交的必需“feature”(例如“FS1 | FS2 | FS3”);

  5. 对于每个 feature:查找匹配的节点表条目,识别可用的节点(空闲或共享),并将它们添加到全局的节点 bitmap 中;

  6. select_g_job_test(),根据拓扑和/或工作负载选择其中的“最佳”; 最佳”定义为连续节点的最小数量,或者如果共享资源,则使用类似大小的作业共享资源。

  7. 如果现在无法满足请求,请对存在于任何状态(DOWN DRAINED ALLOCATED)的节点列表执行 select_g_job_test(),以确定是否能够满足请求;

  8. 调用 allocate_nodes,执行实际分配节点;

4.6 回填调度的锁机制

回填调度是一项耗时的操作。在回填调度过程中,持有配置文件和分区的读锁、作业和节点的写锁。此时,调度流程之外与作业和节点相关的操作都会阻塞住。


    /* Read config and partitions; Write jobs and nodes */    slurmctld_lock_t all_locks = {        READ_LOCK, WRITE_LOCK, WRITE_LOCK, READ_LOCK, READ_LOCK };
lock_slurmctld(all_locks); (void) _attempt_backfill(); last_backfill_time = time(NULL); unlock_slurmctld(all_locks);
复制代码


默认情况下,锁每两秒短暂释放一次,以便可以处理其他选项,例如处理新的作业提交请求。回填调度可以选择在锁释放后继续执行并忽略新提交的作业 ( SchedulerParameters=bf_continue )。这样做将考虑原有待调度作业列表中更多作业被调度,但可能会导致新提交的作业被延迟调度。回填调度以 while (1) 循环运行,每次调度 1 个作业,直至满足退出的条件,while(1)循环被打破,回填调度结束;在调度作业之前,会进行判断是否要进行锁的释放。触发锁释放有两个条件:


  1. 系统当前累积的未处理 RPC 线程数大于 max_rpc_cnt(一般为 255);

  2. 回填调度持续的时间大于 yield_interval;


当以上任意一个条件满足时,进入_yield_locks(yield_sleep)函数;在该函数内首先进行锁的释放,随后等待当前系统累积未处理的 RPC 线程数小于 yield_rpc_cnt 时,再次持有锁,并退出该函数;回填调度流程此时根据是否配置 SchedulerParameters=bf_continue,来决定继续进行回填调度流程或者退出回填调度的 while(1)循环,结束本次回填调度。回填调度释放锁的机制流程,如图 12 所示:



图 12 回填调度释放锁的机制流程

流程图中提到的回填调度退出条件,主要与 3.3 节中提到的回填深度配置有关。回填调度退出条件如下所示:(1)待调度作业列表中的作业已全部被调度;(2)slurmctld 服务退出;(3)回填调度的时间超过了最大允许的调度时间 bf_max_time;(4)锁释放之后,没有配置 backfill_continue;(5)回填调度启动的作业数已超过 bf_max_job_start;(6)回填调度尝试的作业数已经大于 bf_max_job_test;(7)所有分区回填调度尝试的作业数大于 bf_max_job_part;(8)待调度作业列表中,回填调度为每个用户尝试的作业数大于 bf_max_job_user;(9)待调度作业列表中,回填调度为每个 assoc 尝试的作业数大于 bf_max_job_assoc;(10)待调度作业列表中,回填调度为每个分区每个用户尝试的作业数大于 bf_max_job_user_part;

5. 回填调度优化

5.1 实际场景中遇到的问题

slurm 使用回填调度算法,目的是提高碎片化资源的利用率,进而提高集群资源的利用率。但实际的使用场景中,从集群管理员、集群用户、调度系统开发者不同角色的角度来看,仍存在很多问题。

5.1.1 集群管理员角度

从集群管理员的角度,主要是希望能将回填调度的效果发挥出来,充分利用好集群的资源。但是面对回填调度的一堆配置参数,经常不知道怎么得到适合自己集群的配置项,例如:

(1)回填的时间窗口 bt_window 设置为多久比较合适?

(2)分区配置里的作业时间限制 DefaultTime、MaxTime、OverTimeLimit 应该怎么设置?

(3)回填调度深度相关的配置项应当怎么设置,才能保证对不同队列不同用户都比较公平?

(4)如何利用锁机制配置来均衡回填的吞吐量和系统的响应速度?

5.1.2 集群用户角度

当用户提交作业到集群上时,希望能够利用回填调度将自己的作业快速调度运行;那么在提交作业时,就需要为作业指定合适的时间限制 time limit 参数(-t, --time=)。应当如何为作业指定 time limit 参数呢?

5.1.3 调度系统开发者角度

回填调度过程中,会持有作业和节点的写锁,阻塞调度线程之外所有与作业和节点相关的操作,如作业提交、节点状态查询等;此时,调度系统将不能快速响应。从调度系统的开发者角度来看,希望回填调度的吞吐量和调度系统的响应速度之间达到一个均衡的状态。避免因为频繁的回填调度,导致调度系统卡死;或者为了调度系统的快速响应,将回填调度时间间隔设置的较大,导致仅有极少的作业能通过回填来分配到合适的资源。

从开发人员的角度,存在着这样一些疑问:(1)回填调度的效率是否可以进行优化?(2)怎样提高回填调度的资源利用率?

5.2 优化方案

针对当前回填调度中遇到的问题,提出以下优化方案。

5.2.1 回填调度相关配置参数的设置

回填调度充分利用大作业排队的时间间隙,在不会延迟任何较高优先级大作业预期开始时间的前提下,将优先调度低优先级的小作业启动运行。因此,启用回填调度,会导致实际运行作业的顺序,与排队作业优先级顺序不完全符合,低优先级的小作业可能会先于高优先级大作业运行。若集群要严格按照优先级顺序来调度作业,则不适合开启回填调度。回填调度各个配置参数生效位置如图 13 所示:



图 13 回填调度各个配置参数生效位置图


回填调度各个参数在调度中不同的位置发挥不同的作业,但是相互之间有着间接的联系,在不场景下可以通过不同的参数对调度进行限制,参数优化方案如下:


  1. 回填的时间窗口 bt_window 与回填分辨率 bf_resolution


bt_window 通常建议使用最高时间限制一样长的值 43200(30 天);bf_resolution 值越小,系统开销越大,应随着 bt_window 增大而增大;如集群节点较多,推荐 bf_resolution 设置为 3600(1 小时)。


  1. 兼顾回调调度的吞吐量和调度系统响应速度


回填调度期间会持有作业和节点写锁,频繁的回填调度、长时间的回填调度都会导致调度系统的卡顿;锁机制则时刻监控系统的繁忙程度,当回填持续时间达到释放锁的时间 bt_yield_interval 或系统过于繁忙,则暂停回填,释放锁去处理调度外部请求。一般的集群,可以按照持有锁的时间:释放锁的时间=4:1 的比例来配置,即 bf_yield_interval=2000000(2 秒),bf_yield_sleep=500000(0.5 秒),bf_continue。若集群比较繁忙,可以适当减小持有锁的时间:释放锁的时间比例为 2:1,例如 bf_yield_interval=1000000(1 秒),bf_yield_sleep=500000(0.5 秒)。保持 bf_yield_sleep 配置为 0.5 秒不变,根据集群的情况,通过调整 bf_yield_interval 和 bf_yield_sleep 的比例,来兼顾调度系统的响应速度和回填调度的吞吐量。


  1. 兼顾回填的整体深度和分区深度


每轮回填调度能探测/启动多少作业,是回填调度的整体深度 bf_max_job_test;每轮调度为每个分区探测/启动多少个作业是回填调度的分区深度 bf_max_job_part。整体深度太大,回填耗费的时间过长,会在达到 bf_max_time 时退出,整体深度配置不起作用;分区深度太大,会导致会提前调度的时间都花在某个分区中,其他分区作业得不到调度;因此,应当结合实际的情况,来进行分区深度和整体深度的设置。

一般对于较繁忙的集群,设置 bf_interval=120(2 分钟),max_rpc_cnt=500,bf_max_time=60(1 分钟),bf_yield_interval=1000000(1 秒),bf_yield_sleep=500000(0.5 秒),bf_continue。例如 3 个有 1200 节点的分区,完整回填一个作业耗费的时间是 0.02 秒,当作业因为配额不足而被 hold 或者资源不足时,所花时间仅为 0.002 秒。按照 1 个作业被回填处理的时间为(0.02+0.002)/2 = 0.011 秒,则估算:bf_max_job_test=( ((60/(1+0.5)*1 )/ 0.011) = 3636。则每个分区的探测作业数 bf_max_part_test = bf_max_job_test / 分区数量 = 3636/3 = 1212。如果分区之间的节点数量和作业数量差距较大,在设置 bf_max_part_test 时,可以稍微偏向于节点数更多、作业更多的分区情况。


  1. 分区的作业时间限制设置


在全局和分区的配置中,支持以下与作业时间限制相关的配置项:


  • DefaultTime - 默认作业时间限制(按分区配置)


用于为未指定 time limit 值的作业设置运行时间限制。如果未设置,则将使用 MaxTime。


  • MaxTime - 最大作业时间限制(按分区配置)


如果未设置,则为 UNLIMITED。


  • OverTimeLimit - 作业在取消之前可以超过其时间限制的分钟数(支持全局配置和按分区配置)


默认值为 0,不得超过 65533 分钟;还支持配置 UNLIMITED。通常,作业的时间限制被视为硬性限制,一旦达到该限制,工作将被终止。配置 OverTimeLimit 将导致作业的时间限制被视为软限制。当作业达到时间限制后,可再运行 OverTimeLimit 值对应的分钟数,随后作业若还未结束则将被取消。

当用户作业在集群上运行时,一般是按照核时来进行收费,应当避免出现因为分区的时间限制配置导致用户的作业超时被杀。因此,一般集群的分区 DefaultTime 设置的较长,例如 100 天,DefaultTime=100-00:00:00,MaxTime=UNLIMITED,OverTimeLimit=0。

此时,如果用户在提交作业时不指定 time limit,则集群的回填调度基本不会有效果。

5.2.2 作业 time limit 值的预测

从用户角度来说,对于作业预期执行时间的计算,大概分为两类方法:(1)根据同类历史作业的大致运行时间来设置经验值;(2)使用机器学习等方法来进行作业运行时间的预测。

方法 1:根据同类历史作业的大致运行时间来设置经验值

slurm 提供了 sacct 命令来查询作业的历史记录,作业运行结束后可用 sacct 命令查看作业执行时间 Elapsed 等情况,下次提交同类作业以此为参考选择合理的时间限制。


$ sacct -j 30790015 -o jobid,jobname,start,end,elapsed       JobID    JobName               Start                 End    Elapsed ------------ ---------- ------------------- ------------------- ---------- 30790015           vasp 2023-02-11T08:58:56 2023-02-11T14:33:47   05:34:51 30790015.ba+      batch 2023-02-11T08:58:56 2023-02-11T14:33:47   05:34:51 30790015.ex+     extern 2023-02-11T08:58:56 2023-02-11T14:33:47   05:34:51 30790015.0     vasp_std 2023-02-11T08:59:06 2023-02-11T14:33:47   05:34:41
复制代码


同样类型,同样资源请求的作业,可根据该类型历史作业的平均用时 Elapsed 来获取该类型作业的运行时间经验值,在作业提交时,设置作业的 time limit。


除 sacct 命令外,slurm 还提供了 seff 命令来进行历史作业资源使用情况和运行时间的统计。当下次提交同类作业以此为参考选择合理的资源和时间限制。


$ seff 10010Job ID: 10010Cluster: cluster_testUser/Group: slurmtest/slurmtestState: COMPLETED (exit code 0)Nodes: 2Cores per node: 16CPU Utilized: 1-15:14:19CPU Efficiency: 99.53% of 1-15:25:20 core-walltimeJob Wall-clock time: 01:13:55Memory Utilized: 1.05 GB (estimated maximum)Memory Efficiency: 3.37% of 31.25 GB (1000.00 MB/core)
复制代码


方法 2:使用机器学习等方法来进行作业运行时间的预测

当前有很多国内外的学者和研究人员使用机器学习等方法来进行作业运行时长的预测,例如:基于遗传算法的作业时长预测[1]、基于 BRBF 算法对于 vasp 作业时长的预测[2]和基于分类和实例学习相结合的作业运行时间预测算法[3]等。可以通过上述或者其他算法来对同类型的历史作业数据进行训练,得到作业运行时长的预测值,进而在提交作业时,为作业设置 time limit。

5.2.3 回填调度自身的优化

对于 slurm 中回填调度的设计,也存在着可以优化的地方。例如:对同一类资源请求的作业,进行重复调度导致的回填效率低;回填调度的粒度在节点级别,易造成资源的浪费等。从调度系统开发者的角度,对回填调度的优化思路如下所示:

(1)避免重复调度

当某个用户批量提交资源占用相同的一些作业时,若前面的作业因为资源不足不能在回填窗口内预约到合适资源,回填调度仍会去尝试该用户后续的作业,寻求合适的节点。显而易见,该用户后面的作业,也都不能在回填窗口内预约到合适资源。相当于回填调度在做一些重复而无用的工作。以上是一种简单且常见的场景,还有其它类似的场景会出现相同的现象。针对回填调度,增加避免重复调度的模块,当待调度队列前面的作业因资源不足不能在回填窗口内预约到合适资源时,待调度队列后面与之申请资源相同的作业,不必为其执行调度,直接跳过即可。


(2)回填调度的粒度细化

出于性能原因,即使作业不需要整个节点,回填调度也会为作业保留整个节点,这将导致回填调度的粒度较粗,集群资源得不到充分的利用。可以尝试将回填调度的粒度从节点级别细化到 CPU 级别,以此来提高回调调度的效果,提高集群资源的利用率。但是回填粒度的细化,必然将导致回填速率的下降。

6. 总结

  1. 主调度是 slurm 调度系统中的最主要调度策略,按照优先级顺序快速调度,保证了集群的响应速度、吞吐量、和调度速率。

  2. 回填调度作为主调度以外的辅助调度策略,主要提高资源利用率和作业调度效率。

  3. 合理准确的时间限制(time limit)对于回填调度的良好运行非常重要。没有合理的作业时间限制,回填调度的成功率是很低的。

7. 参考文献

[1]许伦凡,熊敏,肖永浩.基于调度历史数据在线预测作业执行时间[J].计算机应用研究,2020,37(03):763-767.DOI:10.19734/j.issn.1001-3695.2018.08.0624.[2]吴桂宝. 面向回填优化的 vasp 作此执行时间预测的研究[D].中国科学技术大学,2018.[3]肖永浩,许伦凡,熊敏.GA-Sim:一种基于分类和实例学习相结合的作业运行时间预测算法[J].计算机工程与科学,2019,41(06):987-992.


Slurm 调度系统是当前调度系统中少有的开源且成熟的调度系统,但是仍然有少量 bug 的存在,且不能完全覆盖所有的用户场景,如果您有问题请加入社区讨论。社区以 Slurm 为切入口讨论 HPC 相关问题,致力于守卫中国 HPC 集群稳定运行,推广国产调度器助力中国 HPC 进步。联系方式请见评论区。


发布于: 刚刚阅读数: 8
用户头像

慕冰

关注

还未添加个人签名 2022-10-17 加入

还未添加个人简介

评论

发布
暂无评论
Slurm集群调度策略详解(2)-回填调度_Slurm_慕冰_InfoQ写作社区