如何在不使用 MPI_Send 和 MPI_Recv 例程的情况下仅将元素广播到某些等级

How to Broadcast elements to only certain ranks without using the MPI_Send and MPI_Recv routines

只是一个一般性问题:

我想问一下是否有在不使用 MPI_SendMPI_Recv 例程的情况下仅将元素广播到 MPI 中的某些等级。

I wanted to ask if there is anyway to broadcast elements to only certain ranks in MPI without using the MPI_Send MPI_Recv.

让我们先看看 MPI_Bcast 例程的描述。

Broadcasts a message from the process with rank "root" to all other processes of the communicator

MPI_Bcast广播例程是集体交流。 Hence:

Collective communication is a method of communication which involves participation of all processes in a communicator.

请注意粗体文本 ,即 “通信器中的所有进程”。因此,一种方法(实现您想要的)是创建一个由将参与广播例程的进程组成的子集。这个子集可以通过创建一个新的 MPI communicator. To create that communicator one can use the MPI function MPI_Comm_split. About that routine from source 来实现:

As the name implies, MPI_Comm_split creates new communicators by “splitting” a communicator into a group of sub-communicators based on the input values color and key. It’s important to note here that the original communicator doesn’t go away, but a new communicator is created on each process.
The first argument, comm, is the communicator that will be used as the basis for the new communicators. This could be MPI_COMM_WORLD, but it could be any other communicator as well.
The second argument, color, determines to which new communicator each processes will belong. All processes which pass in the same value for color are assigned to the same communicator. If the color is MPI_UNDEFINED, that process won’t be included in any of the new communicators. The third argument, key, determines the ordering (rank) within each new communicator. The process which passes in the smallest value for key will be rank 0, the next smallest will be rank 1, and so on. If there is a tie, the process that had the lower rank in the original communicator will be first. The final argument, newcomm is how MPI returns the new communicator back to the user.

假设我们只想让排名偶数的进程参与 MPI_Bcast;我们首先创建通信器:

MPI_Comm new_comm;
int color = (world_rank % 2  == 0) ? 1 : MPI_UNDEFINED;
MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &new_comm);

并最终为新的通信器调用 MPI_Bcast

    if(world_rank % 2  == 0){
        ....
        MPI_Bcast(&bcast_value, 1, MPI_INT, 0, new_comm);
        ...
   }

最后我们将释放通信器使用的内存:

    MPI_Comm_free(&new_comm);

一个运行代码示例:

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

int main(int argc,char *argv[]){
    MPI_Init(NULL,NULL); // Initialize the MPI environment
    int world_rank; 
    int world_size;
    MPI_Comm_rank(MPI_COMM_WORLD,&world_rank);
    MPI_Comm_size(MPI_COMM_WORLD,&world_size);
    int bcast_value = world_rank;  
    MPI_Bcast(&bcast_value, 1, MPI_INT, 0, MPI_COMM_WORLD);
    printf("MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = %d, bcast_value = %d \n", world_rank, bcast_value);

    MPI_Comm new_comm;
    int color = (world_rank % 2  == 0) ? 1 : MPI_UNDEFINED;
    MPI_Comm_split(MPI_COMM_WORLD, color, world_rank, &new_comm); 
   
    if(world_rank % 2  == 0){
    int new_comm_rank, new_comm_size;
    MPI_Comm_rank(new_comm, &new_comm_rank);
        MPI_Comm_size(new_comm, &new_comm_size);
        bcast_value = 1000;
        MPI_Bcast(&bcast_value, 1, MPI_INT, 0, new_comm);
    
    printf("MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = %d, new_comm = %d, bcast_value = %d \n", world_rank, new_comm_rank,  bcast_value);
        MPI_Comm_free(&new_comm);
   }
   MPI_Finalize(); 
   return 0;
 }

此代码示例展示了两个 MPI_Bcast 调用,一个包含 MPI_COMM_WORLD 的所有进程( MPI_Bcast 1)和另一个只有这些进程的一个子集( MPI_Bcast 2)。

输出(8 个进程):

MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 0, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 4, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 5, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 6, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 7, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 1, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 2, bcast_value = 0 
MPI_Bcast 1 : MPI_COMM_WORLD ProcessID = 3, bcast_value = 0 
MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = 0, new_comm = 0, bcast_value = 1000 
MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = 4, new_comm = 2, bcast_value = 1000 
MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = 2, new_comm = 1, bcast_value = 1000 
MPI_Bcast 2 : MPI_COMM_WORLD ProcessID = 6, new_comm = 3, bcast_value = 1000