Python多进程在MPI中的应用

20

我有一个使用multiprocessing模块编写的python脚本,以实现更快的执行速度。计算具有尴尬并行性,因此效率随处理器数量扩大而提高。现在,我想在管理多台计算机上的MCMC计算的MPI程序中使用它。该代码调用system(),以调用python脚本。但是,我发现当以这种方式调用时,使用python multiprocessing获取的效率优势消失了。

当从MPI调用时,该如何确保我的python脚本保留multiprocessing带来的速度提升?

以下是一个简单的示例,类似于我想要使用的更为复杂的代码,但显示出相同的一般行为。 我编写了一个名为junk.py的可执行python脚本。

#!/usr/bin/python
import multiprocessing
import numpy as np

nproc = 3
nlen = 100000


def f(x):
    print x
    v = np.arange(nlen)
    result = 0.
    for i, y in enumerate(v):
        result += (x+v[i:]).sum()
    return result


def foo():
    pool = multiprocessing.Pool(processes=nproc)
    xlist = range(2,2+nproc)
    print xlist
    result = pool.map(f, xlist)
    print result

if __name__ == '__main__':
    foo()

当我单独从shell中运行此命令时,使用"top"命令可以看到三个Python进程在我的16核机器上每个占用100%的CPU。

node094:mpi[ 206 ] /usr/bin/time junk.py
[2, 3, 4]
2
3
4
[333343333400000.0, 333348333450000.0, 333353333500000.0]
62.68user 0.04system 0:21.11elapsed 297%CPU (0avgtext+0avgdata 16516maxresident)k
0inputs+0outputs (0major+11092minor)pagefaults 0swaps

然而,如果我使用mpirun来调用它,则每个Python进程占用33%的CPU,总体运行时间大约需要三倍。使用“-np 2”或更多参数调用不会加速计算。

node094:mpi[ 208 ] /usr/bin/time mpirun -np 1 junk.py
[2, 3, 4]
2
3
4
[333343333400000.0, 333348333450000.0, 333353333500000.0]
61.63user 0.07system 1:01.91elapsed 99%CPU (0avgtext+0avgdata 16520maxresident)k
0inputs+8outputs (0major+13715minor)pagefaults 0swaps

(附加说明:这是在Linux Debian wheezy版本上使用mpirun 1.8.1和python 2.7.3。我听说MPI程序中不总是允许使用system(),但它在我的计算机上已经工作了五年。例如,我曾在MPI程序中从system()调用基于pthread的并行代码,并且每个线程都使用了100%的CPU,正如所期望的那样。此外,如果您打算建议在串行模式下运行Python脚本并仅在更多节点上调用它,请注意MCMC计算涉及一定数量的需要同步移动的链,因此计算不能以这种方式重新组织。)

1个回答

19

OpenMPI的mpirun,版本为1.7及更高版本,默认将进程绑定到核心上——也就是说,当它启动python junk.py进程时,会将其绑定到将要运行的核心上。这很好,并且对于大多数MPI用例来说是正确的默认行为。但在这里,每个MPI任务都会fork出更多的进程(通过multiprocessing包),而这些被fork出的进程会继承其父进程的绑定状态——因此它们都绑定到同一个核心上,相互争夺资源。(在top命令中的“P”列可以显示它们都在同一个处理器上)

如果你执行mpirun -np 2命令,你会发现有两组三个进程,每组进程都在不同的核心上,相互竞争。

使用OpenMPI,您可以通过关闭绑定来避免这种情况。

mpirun -np 1 --bind-to none junk.py

或者选择其他绑定方式,以使其符合您运行的最终几何形状。MPICH有与hydra相似的选项

请注意,使用mpi进行子进程的fork()并不总是安全或受支持, 特别是在使用Infiniband互连的集群上运行时,但OpenMPI的mpirun / mpiexec会在不安全时发出警告。


那么答案就是在脚本中关闭多进程处理吗? - Cmag
2
抱歉,您是在问哪个部分?使用“--bind-to none”启动将避免 CPU 问题。至于 fork 的问题,OpenMPI 的 mpirun/mpiexec 会让您知道是否在不安全的 fork 情况下运行,并且当它出现时,您可以解决这个问题。 - Jonathan Dursi
我已经确认了这个解决方案可行,无论是在简单的示例还是在我的实际代码中都是如此。非常感谢!我猜测mpirun的这种行为改变是伴随着openmpi版本1.7中的绑定选项发生的,否则我不知道为什么我之前运行的一些代码会正常工作。 - Raymond Nicolet
在使用Infiniband互连的集群中,您可以通过在调用mpirun之前添加“export RDMAV_FORK_SAFE=1”来使fork()安全。如果该集群还使用大页,则可能需要“export RDMAV_HUGEPAGES_SAFE=1”。 - James Matta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接