作为MPI应用程序的子进程,在串行中调用MPI二进制文件

7

我有一个大规模并行(使用MPI)的模拟应用程序,它产生大量数据。为了评估这些数据,我使用Python脚本。

现在我需要做的是运行这个应用程序很多次(>1000),并从生成的数据中计算统计特性。

我的方法到目前为止是,让一个运行在并行中的Python脚本(使用mpi4py,例如使用48个节点)通过subprocess.check_call调用模拟代码。 我需要这个调用以串行方式运行mpi模拟应用程序。 在这种情况下,我不需要模拟也以并行方式运行。Python脚本可以并行地分析数据,并在完成后启动新的模拟运行,直到积累了大量运行。

目标是

  • 不保存来自2000次运行的整个数据集
  • 将中间数据保存在内存中

示例代码:

文件 multi_call_master.py:

from mpi4py import MPI
import subprocess

print "Master hello"

call_string = 'python multi_call_slave.py'

comm = MPI.COMM_WORLD

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

print "rank %d of size %d in master calling: %s" % (rank, size, call_string)

std_outfile = "./sm_test.out"
nr_samples = 1
for samples in range(0, nr_samples):
    with open(std_outfile, 'w') as out:
        subprocess.check_call(call_string, shell=True, stdout=out)
#       analyze_data()
#       communicate_results()

文件 multi_call_slave.py (这将是 C 模拟代码):

from mpi4py import MPI

print "Slave hello"

comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
print "rank %d of size %d in slave" % (rank, size)

这样做是行不通的。标准输出结果为stdout

Master hello
rank 1 of size 2 in master calling: python multi_call_slave_so.py
Master hello
rank 0 of size 2 in master calling: python multi_call_slave_so.py
[cli_0]: write_line error; fd=7 buf=:cmd=finalize
:
system msg for write_line failure : Broken pipe
Fatal error in MPI_Finalize: Other MPI error, error stack:
MPI_Finalize(311).....: MPI_Finalize failed
MPI_Finalize(229).....: 
MPID_Finalize(150)....: 
MPIDI_PG_Finalize(126): PMI_Finalize failed, error -1
[cli_1]: write_line error; fd=8 buf=:cmd=finalize
:
system msg for write_line failure : Broken pipe
Fatal error in MPI_Finalize: Other MPI error, error stack:
MPI_Finalize(311).....: MPI_Finalize failed
MPI_Finalize(229).....: 
MPID_Finalize(150)....: 
MPIDI_PG_Finalize(126): PMI_Finalize failed, error -1

sm_test.out 中的输出结果如下:

Slave hello
rank 0 of size 2 in slave

原因是,子进程假定作为并行应用程序运行,而我打算将其作为串行应用程序运行。 作为一种非常“hacky”的解决方法,我做了以下几点:
  • 使用特定的mpi发行版(例如intel mpi)编译所有需要mpi感知库
  • 使用不同的mpi库(例如openmpi)编译模拟代码
如果现在使用intel mpi启动我的并行python脚本,则底层模拟不会意识到周围的并行环境,因为它使用了不同的库。
这个方法一段时间内效果很好,但遗憾的是,在不同的集群上维护起来非常困难,也不太便携。
我可以:
  • 将调用子进程的循环放入使用srun的shell脚本中
    • 需要在硬盘上缓存结果
  • 在Python中使用某种MPI_Comm_spawn技术
    • 不适合此类用法
    • 很难找出子进程是否已经完成
    • 可能需要更改C代码
  • 以某种方式欺骗子进程不转发MPI信息
    • 尝试操作环境变量,但无济于事
    • 这种方法也不适合此类用法
    • 使用mpirun -n 1srun调用子进程无济于事
有没有什么优雅、官方的方法来解决这个问题?我真的没有什么想法,非常感谢任何意见!
1个回答

7
不,没有优雅或官方的方法来做到这一点。在MPI应用程序中执行其他程序的唯一官方支持方式是使用MPI_Comm_spawn。通过像subprocess提供的简单OS机制生成子MPI进程是危险的,并且在某些情况下甚至可能具有灾难性后果。
虽然MPI_Comm_spawn不提供查找子进程何时退出的机制,但您可以通过intercomm屏障模拟它。您仍将面临问题,因为MPI_Comm_spawn调用不允许将标准I/O任意重定向,而是将其重定向到mpiexec/mpirun。
您可以编写一个包装脚本,删除MPI库可能用于传递会话信息的所有可能途径。对于Open MPI,这将是以OMPI_开头的任何环境变量。对于Intel MPI,这将是以I_开头的变量。等等。一些库可能使用文件或共享内存块或其他操作系统机制,您也必须处理这些。一旦消除了通信MPI会话信息的任何可能机制,您只需启动可执行文件,它就应该形成一个单例MPI作业(即,表现得好像使用mpiexec -n 1运行)。

非常感谢您的见解!昨天我采用了MPI_Comm_spawn方法,但遇到了I/O问题,我将使用一个中间文件来进行管道bash脚本解决。对于完成检测,我将被迫修改C代码,因为中间bash脚本不起作用,因为它将涉及多个MPI_init或多个MPI_Comm_spawn...这一切都非常混乱,我不明白为什么这在MPI接口标准中没有得到很好的定义。 - Nils_M
2
MPI 的主要目标是高度可移植而非通用。这就是为什么该标准故意避免纳入诸如扩展进程控制之类的特定于操作系统的内容。一个简单的例子:fork() 在 Blue Gene 上不可用。因此,system() 也不受支持。 - Hristo Iliev
有道理,谢谢。不过这并不妨碍他们提供一个带有某种管道支持的阻塞MPI_Comm_spawn版本。 - Nils_M
2
这种支持最初是为MPI-2提出的,后来在1996年的最后阶段投票反对:“修正案从MPI-2的动态章节中删除所有独立函数、信号和监视器。这将删除4.3.4(启动独立进程)、4.3.5(启动多个独立进程)、4.3.6(非阻塞请求)第2部分、4.5.2(信号进程)和4.5.3(通知进程状态变化)。-14赞成/7反对/4弃权” - Hristo Iliev

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