在MPI中,我们使用多个进程,它们不共享任何内容,但与recv / send操作进行通信。接收/发送操作是针对通信器完成的,该通信器可以是整个处理器集或它们的子集。基本命令是:
call MPI_Comm_size ( MPI_COMM_WORLD, nproc, ierr )
call MPI_Comm_rank ( MPI_COMM_WORLD, myrank, ierr )
使用 MPI_COMM_WORLD 与所有处理器集关联的通信器。 MPI的一项有趣功能是,我们可以与以下命令一起运行多个可执行文件:
mpirun -n 3 prog1 : -n 2 prog2
,其中3个节点分配给第一个可执行文件,2个节点分配给第二个可执行文件。但是,对于实际工作,可能希望有一个与prog1或prog2相关的通信器。有没有一种方法可以直接使用而不使用命令 MPI_COMM_SPLIT ?
答案 0 :(得分:2)
该标准没有指定此类预定义的通信器。
伟大的哲学家贾格尔曾说过“您不能总是得到想要的东西”,而您最好的选择确实是使用MPI_Comm_split()
和MPI_COMM_WORLD
的{{1}}的值属性作为MPI_APPNUM
参数。
根据MPI 3.1标准第10.5.3章
10.5.3 MPI_APPNUM
有一个MPI_COMM_WORLD的预定义属性MPI_APPNUM。在Fortran中,该属性是整数值。在C中 该属性是一个指向整数值的指针。如果一个过程是 使用MPI_COMM_SPAWN_MULTIPLE生成,MPI_APPNUM是命令号 产生了当前过程。编号从零开始。如果一个 使用MPI_COMM_SPAWN生成进程,它将具有MPI_APPNUM等于 归零。此外,如果该过程不是通过衍生调用启动的, 但是通过特定于实现的启动机制可以处理 多个过程规范,应将MPI_APPNUM设置为数字 相应的工艺规范。特别是如果 始于
mpiexec spec0 [:spec1:spec2:...]
MPI_APPNUM应该设置为相应规范的编号。
如果未使用MPI_COMM_SPAWN生成应用程序,或者 MPI_COMM_SPAWN_MULTIPLE,而MPI_APPNUM在 特定于实现的启动机制的上下文,MPI_APPNUM为 没有设置。
MPI实现可以选择提供一种机制来 通过info参数覆盖MPI_APPNUM的值。 MPI 为所有SPAWN呼叫保留以下密钥。
appnum值包含 覆盖MPI_APPNUM中默认值的整数 孩子。
理性。
启动单个应用程序后,便可以 通过查看大小来找出有多少个进程 MPI_COMM_WORLD。由多个SPMD组成的应用程序 子应用程序无法找出那里有多少个子应用程序 进程属于哪个子应用程序。虽然有 在特殊情况下解决问题的方法,没有通用的机制。 MPI_APPNUM提供了这样的一般机制。 (基本原理结束。)
答案 1 :(得分:1)
对于有兴趣拆分MPI_COMM_WORLD的任何人,我编写了一个小型实用程序库(一个标头)。它将传播者分为本地传播者,并在它们之间建立内部传播者。它照顾了许多技术问题:
您可以在以下位置找到标题:https://github.com/cfd-go/MPI_MPMD
它还提供了相同的语法,用于一次运行多个程序并生成程序。如果您有任何疑问或想法可以扩展此范围,请在github上打开一个问题。
例如,在您的第一个程序中,您要做的是:
MPMDHelper MPMD;
MPI_Init(&argc, &argv);
MPMD.Init(MPI_COMM_WORLD, "programA");
MPMD.local //<-- this is your local Comm.
在另一个程序中,您执行以下操作:
MPMDHelper MPMD;
MPI_Init(&argc, &argv);
MPMD.Init(MPI_COMM_WORLD, "programB");
MPMD.local //<-- this is your local Comm.
MPMD["programA"].local //<-- intercommunicator for communication with programA
希望有帮助。
答案 2 :(得分:0)
另一种选择是使用MPI标准描述的客户机/服务器机制(可以在here上找到该章)。这里的想法是您编译两个独立的MPI应用程序。其中一个最终成为服务器,并打开用于连接的端口。然后另一个是必须连接到该端口的客户端。该代码将如下所示:
服务器:
program server
use mpi_f08
implicit none
integer :: error
type(MPI_Comm) :: intercomm
real, dimension(5) :: data = [1,2,3,4,5]
character(len=MPI_MAX_PORT_NAME) :: port_name
! Normal MPI initialization
call MPI_Init(error)
! Here we open a port for incoming connections
call MPI_Open_port(MPI_INFO_NULL, port_name, error)
! Copy it in order to pass the address to a client
print*, "PORT NAME:", port_name
! Accept the incoming connection creating the intercommunicator
call MPI_Comm_accept(port_name, MPI_INFO_NULL, 0, MPI_COMM_WORLD, intercomm, error)
! Send test data
call MPI_Send(data, 5, MPI_FLOAT, 0, 0, intercomm)
print*, "DATA SENT"
! Close connection
call MPI_Comm_disconnect(intercomm, error)
call MPI_Finalize(error)
end program server
客户:
program client
use mpi_f08
implicit none
integer :: error
type(MPI_Comm) :: intercomm
type(MPI_Status) :: status
real, dimension(5) :: data = [0,0,0,0,0]
character(len=MPI_MAX_PORT_NAME) :: port_name
call MPI_Init(error)
! Here we copy the port name obtained from the server
print*, "Type in port name"
read(*,*) port_name
! Establish a connection creating the intercommunicator
call MPI_Comm_connect(port_name, MPI_INFO_NULL, 0, MPI_COMM_WORLD, intercomm, error)
! Receive test data
call MPI_Recv(data, 5, MPI_FLOAT, 0, 0, intercomm, status, error)
print*, "DATA RECEIVED", data
! Close connection
call MPI_Comm_disconnect(intercomm, error)
call MPI_Finalize(error)
end program client
在实际程序中,您可能会找到其他一些方法来将有关端口地址的信息传输到客户端(例如,名称发布或文件系统传输)。然后,您可以像这样运行代码:
mpirun -n <N1> server &
mpirun -n <N2> client
关于此方法的几点注意事项。如果您仅创建一个互连器-则双方只有一个MPI任务与伙伴代码进行通信。如果您需要发送大量数据-您可能需要考虑创建多个互连器。此外,MPI标准这一部分的实现可能有些挑剔(例如,在Open MPI 2.x中存在一个错误,无法完全使用它。)