我有一个用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的并行循环中运行我的模型。答案 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
。
如果目录~/opt
尚不存在,请将其设为
mkdir $HOME/opt
&#39;安装&#39; GNU Parallel:
tar jxvf parallel-latest.tar.bz2
cd parallel-XXXXXXXX
./configure --prefix=$HOME/opt
make
make install
将~/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)
您还会在标准输出中看到一个进度条,这很好……您还可以轻松添加检查以查看输出文件是否存在并忽略运行。