使用mpi4py,我正在运行一个python程序,该程序并行启动多个fortran进程,从SLURM脚本开始使用(例如):
mpirun -n 4 python myprog.py
但是已经注意到myprog.py需要更长的时间来运行所请求的任务数量越多,例如。运行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]))
在单个recvbuf阵列上使用mpirun -n 1 ...需要3分钟 - 同时在四个recvbuf阵列上运行(预期并行),在使用mpirun -n 4的四个处理器上运行大约需要5分钟。但是,我预计单处理器和四处理器情况的运行时间大致相等。
py_task实际上是一个python包装器,用于启动fortran程序:
subprocess.check_call(cmd)
subprocess.check_call(cmd)和mpi4py包之间似乎存在一些交互,它阻止代码并行正常运行。
我查了一下这个问题,但似乎找不到任何有帮助的东西。是否有任何针对此问题的修复/详细说明解释此处发生了什么/关于如何在此代码中找出瓶颈原因的建议?
附加说明:
此管道已经适应了“joblib import Parallel”的mpi4py,其中没有先前并行运行的subprocess.check_call()问题,这就是为什么我怀疑这个问题与子进程和mpi4py之间的交互有关。
减速最初是通过添加:
export SLURM_CPU_BIND = none
发布工作的slurm脚本。
虽然以上确实提供了临时解决方案,但问题实际上要深刻得多,我将在此提供一个非常基本的描述。
1)我卸载了我用conda安装的mpi4py,然后重新加载了英特尔MPI(我们的计算集群推荐的MPI版本)。在SLURM脚本中,我然后将python程序的启动更改为:
srun python my_prog.py。
并删除了上面的导出...行,并删除了减速。
2)发现另一次减速,一次发射> 40个任务。这是由于:
每次启动基于fortran的子流程时,文件系统都会要求初始资源(例如,将文件作为参数提供给程序)。在我的情况下,同时启动了大量任务,每个文件可能大约500mb,这可能超过了集群文件系统的IO功能。这导致从启动每个子进程的速度减慢引入程序的大量开销。
以前的并行化joblib实现一次只使用最多24个核心,然后对文件系统的请求没有明显的瓶颈 - 因此之前没有发现性能问题。
对于2),我发现最好的解决方案是显着重构我的代码,以最小化启动的子进程数。一个非常简单的修复,但在找到文件系统上的资源请求瓶颈之前我还没有意识到。
(最后,我还要补充说,通常不建议在线使用mpi4py中的子进程模块,多处理模块首选用于单节点。)