使用 mpi4py 并行启动子进程时速度变慢

Slow down when subprocesses launched in parallel when using mpi4py

使用 mpi4py,我正在 运行ning 一个 python 程序,该程序并行启动多个 fortran 进程,从使用(例如)的 SLURM 脚本开始:

mpirun -n 4 python myprog.py

但注意到 myprog.py 需要更长的时间才能 运行 请求的任务数量越多,例如。 运行ning myprog.py(以下代码仅显示程序的mpi部分):

comm = MPI.COMM_WORLD
size = comm.Get_size()
rank = comm.Get_rank()

data = None

if rank == 0:
    data = params

recvbuf = np.empty(4, dtype=np.float64) 
comm.Scatter(data, recvbuf, root=0)

py_task(int(recvbuf[0]), recvbuf[1], recvbuf[2], int(recvbuf[3]))

with mpi运行 -n 1 ... 在单个 recvbuf 数组上需要 3 分钟,而 运行ning 在四个 recvbuf 数组上(预计并行),在四个处理器上使用 mpi运行 -n 4 ... 大约需要 5 分钟。但是,我希望单处理器和四处理器情况下的 运行 时间大致相等。

py_task 实际上是一个 python 包装器,用于启动 Fortran 程序,使用:

subprocess.check_call(cmd) 

subprocess.check_call(cmd) 和 mpi4py 包之间似乎存在一些相互作用,阻止了代码正确并行运行。

我查过这个问题,但似乎找不到任何帮助它的东西。是否有针对此问题的任何修复/解释此处发生的事情的详细描述/关于如何隔离此代码中的瓶颈原因的建议?

补充说明:

此管道已从 "joblib import Parallel" 改编为 mpi4py,其中 subprocess.check_call() 运行ning 并行没有以前的问题,这就是我怀疑这个问题的原因与subprocess和mpi4py的交互有关。

减速最初是通过添加以下内容来解决的:

export SLURM_CPU_BIND=none

到启动作业的 slurm 脚本。

虽然上面确实提供了一个临时修复,但问题实际上要深得多,我将在这里提供一个非常基本的描述。

1) 我卸载了我用 conda 安装的 mpi4py,然后重新安装了它并加载了 Intel MPI(我们的计算集群推荐的 MPI 版本)。在 SLURM 脚本中,我将 python 程序的启动更改为:

srun python my_prog.py .

并删除了上面的 export... 行,并删除了减速。

2) 一次启动 > 40 个任务时发现速度变慢。这是由于:

每次启动基于 fortran 的子进程时,文件系统都会产生请求初始资源的成本(例如,将文件作为参数提供给程序)。在我的例子中,同时启动了大量任务,每个文件可能约为 500mb,这可能超出了集群文件系统的 IO 能力。这导致启动每个子进程的速度变慢,从而给程序带来了很大的开销。

以前的并行化 joblib 实现一次最多只使用 24 个内核,因此在对文件系统的请求中没有明显的瓶颈 - 因此以前没有发现性能问题。

对于 2),我发现最好的解决方案是 显着重构我的代码 以最小化启动的子进程的数量。一个非常简单的修复,但在发现文件系统资源请求的瓶颈之前我并没有意识到这一点。

(最后补充一点,网上一般不推荐使用mpi4py中的subprocess模块​​,单节点使用首选multiprocessing模块。)