目录
针对更多资源进行抢占(PREEMPTABLE_RESOURCES)
之前介绍了LSF的抢占(preemption)策略,也就是当集群内的资源不够用时,通过将低优先级队列正在运行的作业暂停,为高优先级队列里更加紧急的作业腾出资源来运行。(可以根据链接跳转进行回顾:LSF实践专题(15):LSF队列优先级以及抢占功能初探)
同时,为了满足更加多样性的需求,LSF还提供了很多与抢占相关的进阶选项。这些参数都定义在$LSF_TOP/conf/lsbatch/<clustername>/configdir/lsb.params中,下面我们来一一介绍。
如何选择抢占哪些作业(PREEMPT_FOR)
在最基本的preemption策略里面,低优先级里最晚开始运行的作业会被优先选择,作为抢占的目标。但是随着用户各种需求的增加,我们可能希望更加多样性的选择方案,对此,LSF提供了多种不同的被抢占作业的选取策略。
即PREEMPT_FOR有多个值,分别对应了不同的选取策略,我们看一些比较常用的抢占选取规则:
LEAST_RUN_TIME:
优先抢占运行时间最短的作业,由于作业被抢占会造成作业进程中断,当作业恢复的时候,再将这些进程恢复,但是对于一些计算量很大的作业,很多中间过程数据并不会记录在文件中,如果这个作业已经运算了很长时间,突然因为被抢占而被打断,在恢复时需要重新计算抢占前已经完成的部分,造成过多的资源浪费,因此,这个选项优先抢占运行时间最短的作业,让造成这种资源浪费的可能性降到最低,或者尽量避免。
这个也是默认的策略,即使不配置PREEMPT_FOR,LSF默认也是按照这个规则选取作业进行抢占。
我们每隔2分钟向lowQ队列提交一个需要8个slots的作业。
当这些作业都运行之后,集群内已经没有空闲的slots,而各个作业已经运行的时间如下:
这时我们向可以抢占lowQ队列的highQ队列提交一个作业<2758>,需要2个slots。
我们看到,因为已经没有可用的slots,highQ队列的作业触发抢占,而运行时间最短的作业job4被抢占,这个就是LEAST_RUN_TIME的作用。
MINI_JOB:
抢占最小规模的作业,抢占时基于触发抢占的作业需要的slots数量,选择最符合的slots数量的作业,尽量减少被suspend的作业的slots数量,使抢占行为尽可能少地影响有作业运行的slots。
例如,一个高优先级队列的作业需要10个slots,并且触发了抢占,而此时可以抢占的低优先级队列中有3个作业,其中2个作业各占用6个slots,第三个作业占用了20个slots,如果配置了PREEMPT_FOR=MINI_JOB,LSF会优先抢占两个占用了6个slots的作业,因为这样只会影响12个slots,而如果抢占第三个作业,会影响20个slots。
我们看到,2个占用6个slots的作业被抢占,而占用20个slots的作业未被抢占,这种策略更有利于保持大型的并行作业,因为并行程度越高,往往启动作业的开销越大,启动时间也相对越长,抢占这类作业有时候会导致集群整体的吞吐率明显下降,无法最高效地使用计算资源。
OPTIMAL_MINI_JOB:
名字与MINI_JOB很像,但是作用正好相反,这个选项是选择抢占尽可能少的作业数量,使抢占影响的作业数量尽可能少,而不是关注slots数量。
以上面的相同场景为例,如果配置了PREEMPT_FOR=OPTIMAL_MINI_JOB,LSF会优先抢占第三个作业,因为这样只造成1个作业被抢占,如果选择两个占用6个slots的作业,会造成2个作业被抢占影响进度。
如果集群内各个作业的并行程度差别不是很大,那么选择OPTIMAL_MINI_JOB会使因抢占而受到打扰的作业数量最小。是否采用这种策略,主要取决于每个集群内的作业特点和想要优先保障哪种作业类型。
特殊作业类型的抢占(PREEMPT_JOBTYPE)
在LSF中,有一些特殊类型的作业,默认情况下是不能被抢占的,这种类型有EXCLUSIVE和BACKFILL。
EXCLUSIVE是独占类型的作业,需要提前在队列里配置EXCLUSIVE=Y,并且在提交作业时指定bsub -x来使当前提交的作业成为exclusive独占作业。这类作业不能调度到已经有其他作业运行的节点上,只能使用一个没有作业的节点作为执行节点,同时,这个独占作业开始运行后,LSF也不会调度其他作业到exlusive作业的执行节点上。简单来说,就是这类作业会独占一个执行节点。在默认情况下,这类作业是不能被抢占的。如果用户想改变这个规则,允许抢占独占作业,就可以在PREEMPT_JOBTYPE中配置EXCLUSIVE。
例如:我们先向低优先级队列提交一个exclusive作业<3068>。
该作业运行在节点上之后,这个节点的状态会变成closed_Excl,其他作业也就不能再调度到这个节点了。
在没有配置PREEMPT_JOBTYPE时,我们向高优先级队列提交一个作业<3069>尝试触发抢占。
我们看到,在这个时候,如果slots是被低优先级队列的exclusive作业<3068>占用的,那么高优先级队列的作业<3069>是无法抢占的。
我们配置PREEMPT_JOBTYPE=exclusive:
在设置了PREEMPT_JOBTYPE=exclusive之后,低优先级队列的exclusive作业<3068>就可以被抢占了。
BACKFILL是另一类特殊作业,这类作业可以利用其他作业的预留(reservation)资源来运行,并在预留资源的作业预计启动前结束。和EXCLUSIVE作业一样,这类作业默认情况下也是不会被抢占的,我们同样可以通过配置PREEMPT_JOBTYPE=BACKFILL来改变这个规则,允许抢占BACKFILL的作业。
针对更多资源进行抢占(PREEMPTABLE_RESOURCES)
默认情况下,只有在集群内slots不够的情况下,才会触发抢占动作,但是有时候,slots是足够的,但是另一些其他资源不够导致作业不能运行,例如内存或者某些软件的license不够用。
在这种情况下,如何保证高优先级队列中后提交的作业能够通过抢占更早运行呢?
我们可以在PREEMPTABLE_RESOURCES中配置除了slots以外其它可抢占的资源,例如:
这时,如果是高优先级的作业由于内存或者licenseA不够无法运行,同样可以触发抢占,我们看这个例子:
我们看到,在PREEMPTABLE_RESOURCES没有配置licA的时候,如果highQ队列的作业<3171>需要的licA被lowQ队列的作业<3170>占用,但是slots和memory足够的情况下,是不能触发抢占的。
我们将当前作业<3171>kill掉,将licA配置到PREEMPT_RESOURCES中,再提交同样的作业<3272>到highQ试一下:
这一次,highQ的作业<3272>成功触发抢占。
在上面的bhosts -s输出中,被抢占作业中占用的resource数量也会显示到RESERVED中,否则,如果把这些resource放到TOTAL中,就有可能被后面提交的lowQ队列的作业使用,那样的话,被抢占的作业就可能无法及时恢复到运行状态了。
抢占等待(PREEMPTION_WAIT_TIME)
有些外部resource,并非是由LSF直接进行分配和管理,LSF只是通过专门的API或命令行收集有多少可用的resource,再在作业中使用这些resource。
对于这类resource触发的抢占,被抢占的作业暂停后,可能需要一定的时间才能将resource从暂停的作业中归还到外部resource管理系统中,让等待抢占的高优先级队列的作业使用。
但是如果这个释放资源的时间比较长,高优先级作业由于没有得到足够的resource,有可能继续抢占其他低优先级队列的作业,这样就会抢占过多的作业,会导致没有必要的作业抢占。
为了避免这种情况,可以配置PREEMPTION_WAIT_TIME,这个参数的单位是秒,但是至少要配置300秒。如果低优先级队列中的作业被抢占成功,但是高优先级队列的作业暂时没有拿到足够的resource,那么在这个时间段之内,不会有更多的作业被抢占,避免没有意义的过多抢占被触发。
延时抢占策略(PREEMPT_DELAY)
在有些集群中,有大量的短作业,这类作业本身就会很快结束,这种情况下,高优先级队列的作业即使不通过抢占,也可以很快运行起来,没必要额外触发抢占。这种情况下,可以根据自己集群的特点,设置一个延时抢占参数,让高优先级队列的作业在等待了指定时间仍然不能运行时,才触发抢占动作。例如,配置了PREEMPT_DELAY=60,那么高优先级队列的作业就会在等待了60秒之后,如果仍然没有拿到可用的资源运行时,才会触发抢占动作。
我们看到,在配置了PREEMPT_DELAY的情况下,抢占并没有第一时间触发,并且高优先级队列作业的pending reason中还显示了相应的信息,要等一段时间才会抢占。
等待60秒之后,抢占成功触发。
我们通过bhist -l可以看到高优先级作业提交后等待了60秒,才进行了资源抢占。
总结
灵活运用这些进阶的PREEMPTION相关选项,可以让抢占功能更好地适用于各种不同的应用场景,进一步提高集群的调度效率。
欢迎关注下方微信公众号【HPC常青园】,共同交流HPC集群管理经验和最佳实践。如果您有关于HPC集群的具体需求,欢迎邮件沟通交流:hpc@ivyent.cn。