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

时间:2017-01-27 18:23:23

标签: arrays slurm sbatch

我有一个问题,我需要启动相同的脚本,但使用不同的输入参数。

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

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 doc-a --array选项看起来很有希望。但在我的情况下,我需要更改我所拥有的NxM的每个脚本的输入参数。我怎样才能做到这一点?我不想编写NxM批处理脚本,然后按照this post的建议将它们列在txt文件中。提议的解决方案here似乎也不是理想的,因为这是工作阵列的情况。此外,我想确保所有NxM脚本同时启动,并且调用上面的脚本后立即终止,以便它不会与时间限制和我的整个工作发生冲突将由系统终止并保持不完整(而由于每个NxM工作都在这样的限制内,如果它们并行运行但是独立运行,则不会发生这种情况。

3 个答案:

答案 0 :(得分:6)

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

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

代码将是这样的(未经测试):

void MyCirclecle::turnBall(float rotate_amount)
{
    const float rotate_amount_in_rads = rotate_amount * b2_pi / 180;
    const float c = cos(rotate_amount_in_rads);
    const float s = sin(rotate_amount_in_rads);

    const b2Vec2 pivot_point = body_->GetWorldPoint(circle_shape_.m_p);
    const b2Transform xfm = body_->GetTransform();
    const b2Vec2 p = xfm.p - pivot_point;
    const float x = p.x * c - p.y * s;
    const float y = p.x * s + p.y * c;

    const b2Vec2 pos = b2Vec2(x, y) + pivot_point;
    const float angle = xfm.q.GetAngle() + rotate_amount_in_rads;
    body_->SetTransform(pos, angle);
}

您将通过以下方式提交:

#!/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

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

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

答案 1 :(得分:0)

如果您使用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 $i\n" # comment in for testing
          python myscript.py -p $p1 -v $i\n
        fi
    done
done

这个答案与卡尔斯很相似。因此,我更愿意将其作为评论撰写,但没有足够的声誉。

答案 2 :(得分:0)

根据this page,作业数组会产生很大的开销:

  

如果程序的运行时间很短,比如十分钟或更短,创建一个作业阵列会产生很多开销,你应该考虑打包你的工作。

该页面提供了一些示例来运行您的工作,使用数组和&#34;打包作业。&#34;

如果您不想/需要为您的工作指定资源,这是另一种方法:我不确定它是否是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"

并且您不再需要#!/usr/bin/env python

中的testscript.py