相同脚本但具有不同输入参数的SLURM sbatch作业数组并行运行



我遇到了一个问题,需要启动相同的脚本,但输入参数不同。

假设我有一个脚本myscript.py -p <par_Val> -i <num_trial>,其中我需要为par_values的每个值考虑N不同的par_values(在x0x1之间)和M个试验。

M的每一次试用都几乎达到了我工作的集群的时间限制(我没有权限更改这一点)。所以在实践中,我需要运行独立于NxM的作业。

因为每个批处理作业都有相同的节点/cpu配置,并调用相同的python脚本,除了更改输入参数外,原则上,在伪语言中,我应该有一个sbatch脚本,它应该执行以下操作:

#!/bin/bash
#SBATCH --job-name=cv_01
#SBATCH --output=cv_analysis_eis-%j.out
#SBATCH --error=cv_analysis_eis-%j.err
#SBATCH --partition=gpu2
#SBATCH --nodes=1
#SBATCH --cpus-per-task=4
for p1 in 0.05 0.075 0.1 0.25 0.5
do
for i in {0..150..5}
do
python myscript.py -p p1 -v i
done
done

其中脚本的每个调用本身就是一个批处理作业。从sbatch文档来看,-a --array选项似乎很有希望。但在我的情况下,我需要更改NxM的每个脚本的输入参数。我该怎么做?我不想写NxM批处理脚本,然后按照本文的建议将它们列在txt文件中。这里提出的解决方案似乎也不理想,因为这是作业数组的imho情况。此外,我想确保所有的NxM脚本都同时启动,并且调用上述脚本之后立即终止,这样它就不会与时间限制冲突,我的整个作业将被系统终止并保持不完整(然而,由于每个NxM作业都在这样的限制内,如果它们并行但独立地一起运行,则不会发生这种情况)。

最好的方法是使用作业数组。

一种选择是在提交作业脚本时传递参数p1,因此您只有一个脚本,但必须多次提交,每个p1值一次。

代码如下(未经测试):

#!/bin/bash
#SBATCH --job-name=cv_01
#SBATCH --output=cv_analysis_eis-%j-%a.out
#SBATCH --error=cv_analysis_eis-%j-%a.err
#SBATCH --partition=gpu2
#SBATCH --nodes=1
#SBATCH --cpus-per-task=4
#SBATCH -a 0-150:5
python myscript.py -p $1 -v $SLURM_ARRAY_TASK_ID

您将提交:

sbatch my_jobscript.sh 0.05
sbatch my_jobscript.sh 0.075
...

另一种方法是定义bash数组中的所有p1参数,并提交NxM作业(未测试)

#!/bin/bash
#SBATCH --job-name=cv_01
#SBATCH --output=cv_analysis_eis-%j-%a.out
#SBATCH --error=cv_analysis_eis-%j-%a.err
#SBATCH --partition=gpu2
#SBATCH --nodes=1
#SBATCH --cpus-per-task=4
#Make the array NxM
#SBATCH -a 0-150
PARRAY=(0.05 0.075 0.1 0.25 0.5)    
#p1 is the element of the array found with ARRAY_ID mod P_ARRAY_LENGTH
p1=${PARRAY[`expr $SLURM_ARRAY_TASK_ID % ${#PARRAY[@]}`]}
#v is the integer division of the ARRAY_ID by the lenght of 
v=`expr $SLURM_ARRAY_TASK_ID / ${#PARRAY[@]}`
python myscript.py -p $p1 -v $v

如果您使用SLURM作业数组,您可以将两个for循环的索引线性化,然后对循环索引和数组任务id进行比较:

#!/bin/bash
#SBATCH --job-name=cv_01
#SBATCH --output=cv_analysis_eis-%j.out
#SBATCH --error=cv_analysis_eis-%j.err
#SBATCH --partition=gpu2
#SBATCH --nodes=1
#SBATCH --cpus-per-task=4
#SBATCH -a 0-154
# NxM = 5 * 31 = 154
p1_arr=(0.05 0.075 0.1 0.25 0.5)
# SLURM_ARRAY_TASK_ID=154 # comment in for testing
for ip1 in {0..4} # 5 steps
do
for i in {0..150..5} # 31 steps
do
let task_id=$i/5+31*$ip1
# printf $task_id"n" # comment in for testing
if [ "$task_id" -eq "$SLURM_ARRAY_TASK_ID" ]
then
p1=${p1_arr[ip1]}
# printf "python myscript.py -p $p1 -v $in" # comment in for testing
python myscript.py -p $p1 -v $in
fi
done
done

这个答案与Carles非常相似。因此,我宁愿把它作为评论来写,但没有足够的声誉。

根据此页面,作业数组会产生显著的开销:

如果程序的运行时间很短,比如说十分钟或更短,那么创建作业数组将产生大量开销,您应该考虑打包作业。

该页面提供了一些使用数组和"打包作业"来运行您的作业的示例

如果你不想/需要为你的工作指定资源,这里有另一种方法:我不确定这是否是Slurm想要的用例,但它似乎有效,提交脚本看起来更好一点,因为我们不必线性化索引来将其纳入作业数组范型。此外,它可以很好地处理任意深度的嵌套循环。

直接作为shell脚本运行:

#!/bin/bash
FLAGS="--ntasks=1 --cpus-per-task=1"
for i in 1 2 3 4 5; do
for j in 1 2 3 4 5; do
for k in 1 2 3 4 5; do
sbatch $FLAGS testscript.py $i $j $k
done
done
done

您需要使用#!确保testscript.py指向第一行中正确的解释器,例如

#!/usr/bin/env python 
import time
import sys
time.sleep(5)
print "This is my script"
print sys.argv[1], sys.argv[2], sys.argv[3] 

或者(未经测试),您可以像这个一样使用--wrap标志

sbatch $FLAGS --wrap="python testscript.py $i $j $k"

并且在testscript.py中不需要#!/usr/bin/env python线路

最新更新