我有一个用 Matlab 编写的复杂模型。该模型不是由我们编写的,最好将其视为“黑匣子”,即为了从内部解决相关问题,需要重新编写整个模型,这需要数年时间。
如果我有一个“令人尴尬的并行”问题,我可以使用一个数组来提交带有选项的相同模拟的 X 变体#SBATCH --array=1-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>
Run Code Online (Sandbox Code Playgroud)
谁能帮我在 SLURM 集群上做同样的事情?
par函数不会在 Matlab 的并行循环中运行我的模型。我不是阵列作业的大专家,但我可以帮助您处理内部循环。
我将始终使用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}
Run Code Online (Sandbox Code Playgroud)
使用以下方法提交此作业:
$ sbatch job.slurm
Run Code Online (Sandbox Code Playgroud)
向队列提交 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
Run Code Online (Sandbox Code Playgroud)
每个作业获得 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
Run Code Online (Sandbox Code Playgroud)
您现在可以清楚地看到 3 次 5 进程同时运行(因为它们的输出是混合的)。
在这种情况下不需要使用srun. SLURM 将创造 3 个工作岗位。在每个作业中,一切都发生在单独的计算节点上(即就像您在自己的系统上运行一样)。
要将 GNU 并行“安装”到您的主文件夹中,例如在~/opt.
~/opt如果目录尚不存在,请创建该目录
mkdir $HOME/opt
Run Code Online (Sandbox Code Playgroud)“安装”GNU Parallel:
tar jxvf parallel-latest.tar.bz2
cd parallel-XXXXXXXX
./configure --prefix=$HOME/opt
make
make install
Run Code Online (Sandbox Code Playgroud)添加~/opt到您的路径:
export PATH=$HOME/opt/bin:$PATH
Run Code Online (Sandbox Code Playgroud)
(要使其永久化,请将该行添加到您的~/.bashrc.)
使用conda.
(可选)创建新环境
conda create --name myenv
Run Code Online (Sandbox Code Playgroud)加载现有环境:
conda activate myenv
Run Code Online (Sandbox Code Playgroud)安装 GNU 并行:
conda install -c conda-forge parallel
Run Code Online (Sandbox Code Playgroud)请注意,该命令仅在加载环境时可用。
虽然 Tom 关于使用 GNU Parallel 的建议是一个很好的建议,但我将尝试回答所提出的问题。
如果您想使用matlab相同的参数运行该命令的 5 个实例(例如,如果它们通过 MPI 进行通信),那么您需要询问--ncpus-per-task=1,--ntasks=5并且您应该在该行matlab前面加上srun并摆脱循环。
就您而言,由于您的 5 个呼叫中的每一个matlab都是独立的,因此您想要请求--ncpus-per-task=5, --ntasks=1。这将确保您为每个作业分配 5 个 CPU 核心来按照您的意愿进行操作。如果您愿意,您可以在行前matlab加上以下内容srun,但这对您只运行一项任务没有什么影响。
当然,只有当 5 次matlab运行中的每一次都花费相同的时间时,这才是有效的,因为如果其中一个运行时间更长,那么其他 4 个 CPU 核心将处于空闲状态,等待第五次运行完成。