我想以某种方式触发执行 MPI程序(用C ++编写)的某些功能,例如通过(串行)python脚本。这个python脚本应该在开头用例如
启动mpi程序subprocess.call(['mpirun','-np', '4', 'mpibinary', 'args' ])
我需要多次调用这个MPI程序的函数,我想避免为不同的输入重新启动程序,因为我必须重新初始化我的所有数据结构,这是昂贵的。因此,我想到了当MPI程序空闲时外部触发一个功能。我认为这可以通过文件IO完成,即MPI程序的根级别在while(1)循环中监视某个文件,并且一旦其内容发生变化,它就会解析新内容,通知其他级别并调用函数。我的问题有更优雅的解决方案吗?
最好的解决方案是拥有一个python类,它包装了C ++ MPI程序的重要功能,以便我可以使用
从python中调用它们mpiprogram.superfunction(a,b)
答案 0 :(得分:5)
也许最优雅的解决方案是将Python代码作为MPI应用程序的一部分。然后,它将能够直接将数据(通过MPI消息)发送到MPI应用程序的其余部分,因为它将成为其中的一部分。这里有两种不同的方法:
1)在您的MPI作业中将Python二进制文件作为等级0插入。为了将其排除在参与mpibinary
的集体行动之外,您必须建立一个排除0级的子通信员,并将其用于mpibinary
中的所有进一步集体通信。第一步是简单的部分。在Open MPI中你会这样做:
mpirun --hostfile hosts -np 1 pythonbinary args : -np 32 mpibinary args
这称为MPMD(多个程序多个数据)启动,它将启动pythonbinary
的一个副本,该副本将成为等级0,并且还将成为等级为1的等级为2的mpibinary
的32个副本, ......排名第32位(总共33个进程)。其他MPI实现也为MPMD发布提供了非常类似的机制。然后你使用MPI_Comm_split()
来创建一个不包含Python程序的新通信器。拆分通信器是一项集体操作。这就是为什么你必须在Python代码和C ++应用程序中调用它。 MPI_Comm_split()
采用“颜色”和密钥,并根据不同的颜色将通信器分成多个子通信器。然后,基于键值对具有相同颜色的处理进行排序。您很可能想要这样称呼它:
:
python_comm = mpi.mpi_comm_split(mpi.MPI_COMM_WORLD, 0, 0)
在C ++中:
int rank;
MPI_Comm c_comm;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_split(MPI_COMM_WORLD, 1, rank, &c_comm);
通过使用rank
作为关键字,可以保证c_comm
中的进程顺序与分割前的顺序相同,即来自MPI_COMM_WORLD
的等级1将成为等级c_comm
中的0,等级2将成为等级1,等等。
从现在开始,C ++应用程序可以像往常一样使用c_comm
来执行集合操作。为了在Python和C ++代码之间进行通信,您仍然必须使用MPI_COMM_WORLD
,并且Python代码仍然在其中排名为0.
2)使用MPI-2流程管理工具。首先,您将运行仅包含Python二进制文件的MPI作业:
mpirun --hostfile hosts -np 1 pythonbinary args
然后Python二进制文件将使用MPI_Comm_spawn()
直接生成其他MPI二进制文件,并使用所需数量的新进程。新生成的流程将拥有自己的MPI_COMM_WORLD
,您无需使用MPI_Comm_split()
。此外,spawn操作将建立一个相互通信器,允许Python代码将消息发送到MPI应用程序的其他部分。
在这两种情况下,hosts
文件都包含可以执行MPI二进制文件的所有执行主机的定义。您还需要使用一个可用的Python MPI绑定。
请注意,您只需要向Python脚本添加一些MPI调用,例如MPI_Init
,MPI_Finalize
,MPI_Comm_split
以及相关的MPI_Send
/ MPI_Recv
。你不需要使它平行。 MPI非常通用,它不仅可以用于并行工作共享,还可以用作通用消息传递框架。但请注意,Python绑定应使用与程序其余部分相同的MPI库。
另一个解决方案是使用一些消息队列库或文件池(这实际上是一个粗略的MQ实现)。