我遇到了一个问题,需要启动相同的脚本,但输入参数不同。
假设我有一个脚本myscript.py -p <par_Val> -i <num_trial>
,其中我需要为par_values
的每个值考虑N
不同的par_values
(在x0
和x1
之间)和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
线路