1

只是一个普遍的问题:

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

4

1 回答 1

1

我想问一下,是否有办法在不使用 MPI_Send MPI_Recv 的情况下仅将元素广播到 MPI 中的某些等级。

让我们从查看MPI_Bcast例程的描述开始。

将消息从具有“root”等级的进程广播到 通信器的所有其他进程

广播例程是一种MPI_Bcast集体通信。因此

集体通信是一种通信方法,它涉及到一个通信器中的所有进程的参与。

注意粗体字,即 “通讯器中的所有进程”。因此,一种方法(实现您想要的)是创建一个由将参与广播例程的进程组成的子集。这个子集可以通过创建一个新的 MPI通信器来实现。要创建该通信器,可以使用 MPI 函数MPI_Comm_split。关于源代码中的该例程,可以阅读:

顾名思义,MPI_Comm_split通过根据输入值颜色和键将通信器“拆分”为一组子通信器来创建新的通信器。这里需要注意的是,原来的通信器不会消失,而是在每个进程上创建一个新的通信器。
第一个参数 comm 是将用作新通信器基础的通信器。这可能是 MPI_COMM_WORLD,但也可能是任何其他通信器。
第二个参数颜色决定了每个进程将属于哪个新的通信器。传递相同颜色值的所有进程都分配给相同的通信器。如果颜色是 MPI_UNDEFINED,则该进程将不会包含在任何新的通信器中。第三个参数 key 决定了每个新通信器中的排序(等级)。传入键最小值的过程将是 0 级,下一个最小的将是 1 级,依此类推。如果出现平局,则在原始通信器中排名较低的进程将是第一个。最后一个参数 newcomm 是 MPI 如何将新的通信器返回给用户。

假设我们只想让具有偶数等级的进程参与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( ie, MPI_Bcast 1 ) 的所有进程,另一个仅包含这些进程的一个子集 ( ie, 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
于 2021-01-28T21:07:16.297 回答