MPI Note[4]: 集合通信

前面讲完了点到点通信,下面来看另一个话题,集体(Collective)通信. 点到点的时候已经介绍了一些关于MPI_Barrier的内容:

MPI Note[4]: 集合通信

接下来介绍一些BCast、Scatter、Gather的操作

MPI_Bcast

半开玩笑的说,应该叫组播更合适,因为MPI_Bcast有明确的MPI_Comm组,和组播源root的概念

MPI_Bcast(void* data, int count, MPI_Datatype datatype, int root, MPI_Comm communicator)

这种模式用于将相同的数据分发到其它节点,如下图所示:

MPI Note[4]: 集合通信

当然一开始,我们就可以用单播的方式,一个个的发就好

void my_bcast(void* data, int count, MPI_Datatype datatype, int root, MPI_Comm communicator) {
  int world_rank;
  int world_size;

  MPI_Comm_rank(communicator, &world_rank);
  MPI_Comm_size(communicator, &world_size);

  if (world_rank == root) {
    //作为根节点逐个单播发送
    int i;
    for (i = 0; i < world_size; i++) {
      if (i != world_rank) {
        MPI_Send(data, count, datatype, i, 0, communicator);
      }
    }
  } else {
    //其它节点阻塞接收
    MPI_Recv(data, count, datatype, root, 0, communicator, MPI_STATUS_IGNORE);
  }
}

但是系统自带的更加高效的一种树状分发结构:

但是这样的分发效率并不是很高,初期泛洪速度有些慢,而思科ASR1000组播也是类似的,只是一开始就复制N份出去,第二轮N*N…N是几不能说

// Author: Wes Kendall
// Copyright 2011 www.mpitutorial.com
// This code is provided freely with the tutorials on mpitutorial.com. Feel
// free to modify it for your own use. Any distribution of the code must
// either provide a link to www.mpitutorial.com or keep this header intact.
//
// Comparison of MPI_Bcast with the my_bcast function
//

MPI_Scatter

MPI_Scatter 和MPI_Bcast的不同点在于,Scatter针对每个非root节点分发的数据都不同。

MPI_Scatter(
    void* send_data,
    int send_count,
    MPI_Datatype send_datatype,
    void* recv_data,
    int recv_count,
    MPI_Datatype recv_datatype,
    int root,
    MPI_Comm communicator)

具体实例如下,首先我们在Root节点构造了一个array,然后每个node构造了用于接受的sub_array,并且在MPI_Scatter中定义好了发送和接收的数量。

 

执行

zartbot@mpi1:~/mpi/study/collective$ mpicc scatter.c -o scatter
zartbot@mpi1:~/mpi/study/collective$ mpiexec -np 8 ./scatter 2
proc 0 sub_array[0]: 0.000000
proc 0 sub_array[1]: 1.000000
proc 1 sub_array[0]: 2.000000
proc 1 sub_array[1]: 3.000000
proc 2 sub_array[0]: 4.000000
proc 2 sub_array[1]: 5.000000
proc 3 sub_array[0]: 6.000000
proc 3 sub_array[1]: 7.000000
proc 4 sub_array[0]: 8.000000
proc 4 sub_array[1]: 9.000000
proc 5 sub_array[0]: 10.000000
proc 5 sub_array[1]: 11.000000
proc 6 sub_array[0]: 12.000000
proc 6 sub_array[1]: 13.000000
proc 7 sub_array[0]: 14.000000
proc 7 sub_array[1]: 15.000000
zartbot@mpi1:~/mpi/study/collective$ mpiexec -np 8 ./scatter 3
proc 0 sub_array[0]: 0.000000
proc 0 sub_array[1]: 1.000000
proc 0 sub_array[2]: 2.000000
proc 1 sub_array[0]: 3.000000
proc 1 sub_array[1]: 4.000000
proc 1 sub_array[2]: 5.000000
proc 2 sub_array[0]: 6.000000
proc 2 sub_array[1]: 7.000000
proc 2 sub_array[2]: 8.000000
proc 3 sub_array[0]: 9.000000
proc 3 sub_array[1]: 10.000000
proc 3 sub_array[2]: 11.000000
proc 4 sub_array[0]: 12.000000
proc 4 sub_array[1]: 13.000000
proc 4 sub_array[2]: 14.000000
proc 5 sub_array[0]: 15.000000
proc 5 sub_array[1]: 16.000000
proc 5 sub_array[2]: 17.000000
proc 6 sub_array[0]: 18.000000
proc 6 sub_array[1]: 19.000000
proc 6 sub_array[2]: 20.000000
proc 7 sub_array[0]: 21.000000
proc 7 sub_array[1]: 22.000000
proc 7 sub_array[2]: 23.000000

MPI_Gather

Gather和Scatter刚好相反,如下图所示:

MPI_Gather(
    void* send_data,
    int send_count,
    MPI_Datatype send_datatype,
    void* recv_data,
    int recv_count,
    MPI_Datatype recv_datatype,
    int root,
    MPI_Comm communicator)

例如我们来做一个示例

 

执行:

zartbot@mpi1:~/mpi/study/collective$ mpicc gather.c -o gather
zartbot@mpi1:~/mpi/study/collective$ mpiexec -np 8 ./gather 3
all_array[0]: 0.000000
all_array[1]: 0.000000
all_array[2]: 0.000000
all_array[3]: 1.000000
all_array[4]: 1.000000
all_array[5]: 1.000000
all_array[6]: 2.000000
all_array[7]: 2.000000
all_array[8]: 2.000000
all_array[9]: 3.000000
all_array[10]: 3.000000
all_array[11]: 3.000000
all_array[12]: 4.000000
all_array[13]: 4.000000
all_array[14]: 4.000000
all_array[15]: 5.000000
all_array[16]: 5.000000
all_array[17]: 5.000000
all_array[18]: 6.000000
all_array[19]: 6.000000
all_array[20]: 6.000000
all_array[21]: 7.000000
all_array[22]: 7.000000
all_array[23]: 7.000000

一个计算平均数的示例

 
zartbot@mpi1:~/mpi/study/collective$ mpicc avg.c -o avg
zartbot@mpi1:~/mpi/study/collective$ mpiexec -np 8 ./avg 3
proc 0 sub_array[0]: 0.000000
proc 0 sub_array[1]: 1.000000
proc 0 sub_array[2]: 2.000000
proc 1 sub_array[0]: 3.000000
proc 1 sub_array[1]: 4.000000
proc 1 sub_array[2]: 5.000000
proc 2 sub_array[0]: 6.000000
proc 2 sub_array[1]: 7.000000
proc 2 sub_array[2]: 8.000000
proc 3 sub_array[0]: 9.000000
proc 3 sub_array[1]: 10.000000
proc 3 sub_array[2]: 11.000000
proc 4 sub_array[0]: 12.000000
proc 4 sub_array[1]: 13.000000
proc 4 sub_array[2]: 14.000000
proc 5 sub_array[0]: 15.000000
proc 5 sub_array[1]: 16.000000
proc 5 sub_array[2]: 17.000000
proc 6 sub_array[0]: 18.000000
proc 6 sub_array[1]: 19.000000
proc 6 sub_array[2]: 20.000000
proc 7 sub_array[0]: 21.000000
proc 7 sub_array[1]: 22.000000
proc 7 sub_array[2]: 23.000000
proc 0 sub_average: 1.000000
proc 1 sub_average: 4.000000
proc 2 sub_average: 7.000000
proc 3 sub_average: 10.000000
proc 4 sub_average: 13.000000
proc 5 sub_average: 16.000000
proc 7 sub_average: 22.000000
proc 6 sub_average: 19.000000
Avg of all elements is 11.500000
zartbot@mpi1:~/mpi/study/collective$

MPI_Allgather

等于MPI_Gather后再加一个Bcast

例如我们稍微修改一下前述代码

    MPI_Barrier(MPI_COMM_WORLD);
    printf("proc %d sub_average: %f\n", world_rank, sub_avg);

    float *sub_avgs = (float *)malloc(sizeof(float) * world_size);
    MPI_Allgather(&sub_avg, 1, MPI_FLOAT, sub_avgs, 1, MPI_FLOAT, MPI_COMM_WORLD);

    float avg = compute_avg(sub_avgs, world_size);
    printf("Proc %d Avg of all elements is %f\n", world_rank,avg);

    if (world_rank == 0)
    {
        free(array);
    }

执行结果

Proc 7 Avg of all elements is 11.500000
Proc 1 Avg of all elements is 11.500000
Proc 4 Avg of all elements is 11.500000
Proc 2 Avg of all elements is 11.500000
Proc 0 Avg of all elements is 11.500000
Proc 5 Avg of all elements is 11.500000
Proc 3 Avg of all elements is 11.500000
Proc 6 Avg of all elements is 11.500000

MPI Note[4]: 集合通信》来自互联网,仅为收藏学习,如侵权请联系删除。本文URL:http://www.bookhoes.com/744.html