在MPI中,使用多个可执行文件时如何获取通讯器?

时间:2019-07-17 14:52:35

标签: fortran mpi distributed

在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

3 个答案:

答案 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中存在一个错误,无法完全使用它。)