SLURM:令人尴尬的并行程序在一个令人尴尬的并行程序中

时间:2018-03-15 10:57:36

标签: bash matlab slurm

我有一个用Matlab编写的复杂模型。该模型不是由我们编写的,最好被认为是一个“黑匣子”,即为了从内部修复相关问题需要重写整个模型,这需要数年时间。

如果我遇到“令人尴尬的并行”问题,我可以使用数组通过选项#SBATCH --array=1-X提交相同模拟的X变体。但是,群集通常对最大阵列大小具有(令人沮丧的小)限制。

在使用PBS / TORQUE集群的同时,我通过强制Matlab在单个线程上运行,请求多个CPU然后在后台运行多个Matlab实例来解决这个问题。示例提交脚本是:

#!/bin/bash
<OTHER PBS COMMANDS>
#PBS -l nodes=1:ppn=5,walltime=30:00:00
#PBS -t 1-600

<GATHER DYNAMIC ARGUMENTS FOR MATLAB FUNCTION CALLS BASED ON ARRAY NUMBER>

# define Matlab options
options="-nodesktop -noFigureWindows -nosplash -singleCompThread"

for sub_job in {1..5}
do
    <GATHER DYNAMIC ARGUMENTS FOR MATLAB FUNCTION CALLS BASED ON LOOP NUMBER (i.e. sub_job)>
    matlab ${options} -r "run_model(${arg1}, ${arg2}, ..., ${argN}); exit" &
done
wait
<TIDY UP AND FINISH COMMANDS>

任何人都可以帮我在SLURM群集上做同等的工作吗?

  • par函数不会在Matlab的并行循环中运行我的模型。
  • PBS / TORQUE语言非常直观,但SLURM让我很困惑。假设一个类似结构的提交脚本和我的PBS示例一样,这就是我认为某些命令会产生的结果。
    • - ncpus-per-task = 5对我来说似乎是最明显的一个。我是否会在循环中将srun放在matlab命令前面,或者将它保留在PBS脚本循环中?
    • - ntasks = 5我会想象会请求5个CPU但是会以串行方式运行,除非程序专门请求它们(即MPI或Python-Multithreaded等)。在这种情况下,我是否需要将srun放在Matlab命令前面?

3 个答案:

答案 0 :(得分:4)

我不是阵列作业的大专家,但我可以帮助你完成内循环。

我总是使用GNU parallel在一个有多个CPU可用的作业中并行运行多个串行进程。这是一个简单的perl脚本,因此安装起来并不困难,而且它的语法非常简单。它基本上做的是并行运行一些(嵌套)循环。此循环的每次迭代都包含一个(长)进程,就像您的Matlab命令一样。与您的解决方案相比,它不会立即提交所有这些进程,但它同时只运行N个进程(其中N是您可用的CPU数)。一旦完成,下一个提交,依此类推,直到整个循环结束。完全没有问题,并不是所有进程都花费相同的时间,只要释放一个CPU,就会启动另一个进程。

然后,您要做的是启动600个作业(我将其替换为3,以显示完整的行为),每个作业有5个CPU。要做到这一点,你可以做以下事情(我没有包括matlab的实际运行,但可以包含这一点:

#!/bin/bash
#SBATCH --job-name example
#SBATCH --out job.slurm.out
#SBATCH --nodes 1
#SBATCH --ntasks 1
#SBATCH --cpus-per-task 5
#SBATCH --mem 512
#SBATCH --time 30:00:00
#SBATCH --array 1-3

cmd="echo matlab array=${SLURM_ARRAY_TASK_ID}"

parallel --max-procs=${SLURM_CPUS_PER_TASK} "$cmd,subjob={1}; sleep 30" ::: {1..5}

使用以下方式提交此作业:

$ sbatch job.slurm

向队列提交3个作业。例如:

$ squeue | grep tdegeus
         3395882_1     debug  example  tdegeus  R       0:01      1 c07
         3395882_2     debug  example  tdegeus  R       0:01      1 c07
         3395882_3     debug  example  tdegeus  R       0:01      1 c07

每个作业获得5个CPU。这些被parallel命令利用,以并行运行内部循环。再一次,这个内部循环的范围可能(大)大于5,parallel负责此作业中5个可用CPU之间的平衡。

让我们检查一下输出:

$ cat job.slurm.out

matlab array=2,subjob=1
matlab array=2,subjob=2
matlab array=2,subjob=3
matlab array=2,subjob=4
matlab array=2,subjob=5
matlab array=1,subjob=1
matlab array=3,subjob=1
matlab array=1,subjob=2
matlab array=1,subjob=3
matlab array=1,subjob=4
matlab array=3,subjob=2
matlab array=3,subjob=3
matlab array=1,subjob=5
matlab array=3,subjob=4
matlab array=3,subjob=5

您可以清楚地看到现在同时运行3次5个进程(因为它们的输出是混合的)。

在这种情况下无需使用srun。 SLURM将创造3个工作岗位。在每个作业中,一切都发生在各个计算节点上(即,就像您在自己的系统上运行一样)。

安装&#39; GNU并行到您的主文件夹,例如~/opt

  1. Download the latest GNU Parallel

  2. 如果目录~/opt尚不存在,请将其设为

    mkdir $HOME/opt
    
  3. &#39;安装&#39; GNU Parallel:

    tar jxvf parallel-latest.tar.bz2
    cd parallel-XXXXXXXX
    ./configure --prefix=$HOME/opt
    make
    make install
    
  4. ~/opt添加到您的路径中:

    export PATH=$HOME/opt/bin:$PATH
    

    (要使其永久化,请将该行添加到~/.bashrc。)

答案 1 :(得分:3)

虽然Tom建议使用GNU Parallel是一个很好的建议,但我会尝试回答所提出的问题。

如果要运行具有相同参数的matlab命令的5个实例(例如,如果他们通过MPI进行通信),那么您可以要求--ncpus-per-task=1--ntasks=5你应该在matlab行前加srun并摆脱循环。

在您的情况下,由于您对matlab的5次调用都是独立的,因此您需要--ncpus-per-task=5--ntasks=1。这将确保您按照自己的意愿为每个作业分配5个CPU核心。如果您愿意,可以在matlab行前加srun,但如果您只运行一项任务,则会有所不同。

当然,只有当你的5 matlab次运行中的每次运行花费相同的时间时,这才有效,因为如果一次运行需要更长的时间,那么其他4个CPU内核将处于空闲状态,等待第五次运行完成

答案 2 :(得分:0)

你可以用 python 和 subprocess 来做,在我下面描述的你只设置节点和任务的数量,就是这样,不需要数组,不需要将数组的大小与数量匹配模拟等......它只会执行python代码直到完成,更多的节点更快地执行。 此外,由于一切都在 python 中准备,因此更容易决定变量(这比 bash 更容易)。

它确实假设 Matlab 脚本将输出保存到文件 - 此函数不返回任何内容(可以更改..)

在 sbatch 脚本中,您需要添加如下内容:

#!/bin/bash
#SBATCH --output=out_cluster.log
#SBATCH --error=err_cluster.log
#SBATCH --time=8:00:00
#SBATCH --nodes=36
#SBATCH --exclusive
#SBATCH --cpus-per-task=2

export IPYTHONDIR="`pwd`/.ipython"
export IPYTHON_PROFILE=ipyparallel.${SLURM_JOBID}

whereis ipcontroller

sleep 3
echo "===== Beginning ipcontroller execution ======"
ipcontroller --init --ip='*' --nodb --profile=${IPYTHON_PROFILE} --ping=30000 & # --sqlitedb
echo "===== Finish ipcontroller execution ======"
sleep 15
srun ipengine --profile=${IPYTHON_PROFILE} --timeout=300 &
sleep 75
echo "===== Beginning python execution ======"

python run_simulations.py

根据您的系统,在此处阅读更多信息:https://ipyparallel.readthedocs.io/en/latest/process.html

和 run_simulations.py 应该包含如下内容:

import os
from ipyparallel import Client
import sys
from tqdm import tqdm
import subprocess
from subprocess import PIPE
def run_sim(x):
    import os
    import subprocess
    from subprocess import PIPE
    
    # send job!
    params = [str(i) for i in x]
    p1 = subprocess.Popen(['matlab','script.mat'] + params, env=dict(**os.environ))
    p1.wait()

    return

##load ipython parrallel
rc = Client(profile=os.getenv('IPYTHON_PROFILE'))
print('Using ipyparallel with %d engines', len(rc))
lview = rc.load_balanced_view()
view = rc[:]
print('Using ipyparallel with %d engines', len(rc))
sys.stdout.flush()
map_function = lview.map_sync

to_send = []
#prepare variables  <-- here you should prepare the arguments for matlab
####################
for param_1 in [1,2,3,4]:
    for param_2 in [10,20,40]:
        to_send.append([param_1, param_2])



ind_raw_features = lview.map_async(run_sim,to_send)
all_results = []

print('Sending jobs');sys.stdout.flush()
for i in tqdm(ind_raw_features,file=sys.stdout):
    all_results.append(i)

您还会在标准输出中看到一个进度条,这很好……您还可以轻松添加检查以查看输出文件是否存在并忽略运行。