学习笔记:MPI组通信的散发与组收集

散发

在MPI组通信中,散发操作有两个函数,分别是MPI_Scatter与MPI_Scatterv。分别与之前所说的MPI_Gather,MPI_Gatherv为互逆操作。

MPI_Scatter

使用MPI_Scatter函数可以将root进程的发送缓冲区中的数据顺序地分发给各个进程(自己的接收缓冲区也会接收到)。调用方式为:MPI_Scatter(sendbuf,sendcount,sendtype,recvbuf,recvcount,recvtype,root,comm);
需要注意的是在MPI_Scatter中向每个进程发送的数据数目是相同的,sendcount是固定值且必须和recvcount相同。

MPI_Scatterv

与MPI_Gatherv类似,MPI_Scatterv也是更加复杂更加灵活同时更加有逼格的组通信散发操作。它同样也将root进程的发送缓冲区的数据散发给各个进程,区别在于发送给每个进程的数据数目以及在发送缓冲区的起始位置都可以指定
调用方式为:MPI_Scatterv(sendbuf,sendcounts,displs,sendtype,recvbuf,recvcount,recvtype,root,comm);
注意sendcounts代表该参数是一个数组,指明向每一个进程发送数据的数量,而displs是另一个数组,指明向每一个进程发送数据的起始位置。此外,若向不同进程发送数据数目不同,recvcount并不是一个固定值,但也需要与对应进程发送的数据数目相同。

实例

在下面的例子中,root进程的第一个发送缓冲区的大小为向每一个进程发送数据的数目scatter_size进程数目proc_nums,第i位数据为整数i。将发送缓冲区分为proc_nums块,依次散发给各个进程。实现了组通信中的普通散发操作。
第二个发送缓冲区的大小为向每一个进程发送数据的数目的累加和,第i位数据为整数i。向第j个进程散发从发送缓冲区的第j个数据开始的共j+1个数,即sendbuf[j-1]到sendbuf[2*j]

#include<stdio.h>
#include"mpi.h"
#include"stdlib.h"
#include <unistd.h>
#define scatter_size 10
#define dummy 1
int main(int argc,char **argv)
{
  int proc_size,rank;
  int root,*send_buffer;
  int recv_buffer[scatter_size];

  int *displs;
  int *scatterv_count;
  int *v_send_buff;
  int *v_recv_buff;

  root=0;

  MPI_Init(&argc,&argv);
  MPI_Comm_rank(MPI_COMM_WORLD,&rank);
  MPI_Comm_size(MPI_COMM_WORLD,&proc_size);

  if(rank==root){
    int scatter_send_buffer_size;
    scatter_send_buffer_size=proc_size*scatter_size;
    send_buffer=(int *)malloc(scatter_send_buffer_size*sizeof(int));
    for(int i=0;i<scatter_send_buffer_size;i++){send_buffer[i]=i;}
    //scatterv
    int send_size=proc_size;
    for(int m=0;m<proc_size;m++){send_size+=m;}

    displs=(int *)malloc(proc_size*sizeof(int));
    scatterv_count=(int *)malloc(proc_size*sizeof(int));
    v_send_buff=(int *)malloc(send_size*sizeof(int));
    int location=0;
    for(int j=0;j<proc_size;j++){
      location+=j;
      displs[j]=location;
      scatterv_count[j]=j+1;
    }
    for(int k=0;k<send_size;k++){v_send_buff[k]=k;}
  }
  
  v_recv_buff=(int *)malloc((rank+1)*sizeof(int));
  MPI_Barrier(MPI_COMM_WORLD);
  MPI_Scatter(send_buffer,scatter_size,MPI_INT,recv_buffer,scatter_size,MPI_INT,root,MPI_COMM_WORLD);
  MPI_Scatterv(v_send_buff,scatterv_count,displs,MPI_INT,v_recv_buff,rank+1,MPI_INT,root,MPI_COMM_WORLD);
  MPI_Barrier(MPI_COMM_WORLD);
  for(int hwf=0;hwf<proc_size;hwf++){
    sleep(dummy);
    if(rank==l){
      printf("-------------------------------------------\n");
      printf("processor %d has been scatterd:\n",rank);
      for(int k=0;k<scatter_size;k++){printf("%d, ",recv_buffer[k]);}
      printf("\n");
      printf("processor %d has been scattervd:\n",rank);
      for(int p=0;p<rank+1;p++){printf("%d,",v_recv_buff[p]);}
      printf("\n");
    }
  }
  //MPI_Abort(MPI_COMM_WORLD,99);
  MPI_Finalize();
  return 0;
}

组收集

组收集是对MPI组通信中收集操作的扩展,相当于通信域中的每一个进程都作为root进程对全体进程进行了收集操作。在之前的MPI_Gather与MPI_Gatherv操作中,只有root进程的接收缓冲区有意义。而在组收集中,所有进程的接收缓冲区都有意义,且接收结果都相同。

MPI_Allgather

每一个进程都收集全体进程发送缓冲区中的数据,并依次存放在自己的接收缓冲区中。从每个进程收集到的数据数目相同。
MPI_Allgather(sendbuf, sendcount, sendtype, recvbuf, recvcount,recvtype,comm);
注意sendcount是固定值且必须与recvcount相同。

MPI_Allgatherv

每一个进程都收集全体进程发送缓冲区中的数据,并按照偏移数组指定的位置存放在接收缓冲区中,从每个进程收集到的数据数目可以使用数目数组指定。
MPI_Allgatherv(sendbuf, sendcount, sendtype, recvbuf, recvcounts,displs,recvtype,comm);
recvcounts为指定从各进程收集数据数目的数组,displs为指定接收缓冲区中存放位置的偏移数组。此时sendcount不一定是固定值,但需要与对应接收缓冲区的接收数据数目相同。

实例

在下面的例子中,有两接收缓冲区,分别属于allgather与allgatherv。每一个进程都有这两种接收缓冲区。
在allgather操作中,每个进程都从各个进程收集gather_size个整数,依此存放在接收缓冲区中。
在allgatherv操作中,每个进程都从进程i的发送缓冲区中收集前i+1个数据,并定义了对应的存放偏移数组使得它们在接收缓冲区中依次存放。实质上是对上一篇博客中的gatherv例子的扩展。
代码如下,注意此处我直接定义了各进程的发送缓冲区大小为进程号+1。

#include<stdio.h>
#include"mpi.h"
#include"stdlib.h"
#include <unistd.h>
#define gather_size 10
#define dummy 1

int main(int argc,char **argv)
{
  int proc_nums,rank;
  int send_buff[gather_size];//allgather的发送缓冲区
  int *recv_buff;//allgather的接收缓冲区

  int v_send_buff[gather_size];//allgatherv的发送缓冲区
  int *v_recv_buff;//allgatherv的接收缓冲区
  int *sendcounts;//收集数目数组
  int *displs;//存放位置偏移数组
  int malloc_size;//allgatherv的接收缓冲区大小

  MPI_Init(&argc,&argv);
  MPI_Comm_size(MPI_COMM_WORLD,&proc_nums);
  MPI_Comm_rank(MPI_COMM_WORLD,&rank);
  //定义收集数目数组,存放位置偏移数组
  sendcounts=(int *)malloc(proc_nums*sizeof(int));
  displs=(int *)malloc(proc_nums*sizeof(int));
  int location=0;
  for(int j=0;j<proc_nums;j++){
    sendcounts[j]=j+1;//从第i个进程收集i+1个数字。
    location+=j;
    displs[j]=location;//每个进程收集到的数字在结果数组中的首地址偏移量。
  }
    malloc_size=location+proc_nums;

  //printf("processor %d malloc_size is %d,first count is %d,first loc %d,second loc %d ",rank,malloc_size,sendcounts[0],displs[0],displs[1]);
  //每个进程都申请接收数组空间
  recv_buff=(int *)malloc(proc_nums*gather_size*sizeof(int));
  v_recv_buff=(int *)malloc(malloc_size*sizeof(int));
  //定义allgather与allgahterv过程中,每个进程发送缓冲区的内容
  for(int p=0;p<gather_size;p++){send_buff[p]=rank;}
  for(int q=0;q<rank+1;q++){v_send_buff[q]=rank;}
  //全收集
  MPI_Allgather(send_buff,gather_size,MPI_INT,recv_buff,gather_size,MPI_INT,MPI_COMM_WORLD);
  MPI_Allgatherv(v_send_buff,sendcounts[rank],MPI_INT,v_recv_buff,sendcounts,displs,MPI_INT,MPI_COMM_WORLD);//每个进程都向其余进程收集对方进程号+1个数据
  //测试
  for(int h=0;h<proc_nums;h++){
    sleep(dummy);//打印顺序太乱,不知道怎么办,停一秒再继续。嘻嘻。
    if(rank==h){
      printf("------------------------------------------\n");
    printf("processor %d has gathered:",rank);
    for(int w=0;w<gather_size*proc_nums;w++){
      printf("%d,",recv_buff[w]);
    }
    printf("\n");
    for(int f=0;f<malloc_size;f++){
      printf("%d,",v_recv_buff[f]);
    }
    printf("\n");
  }
  }
  MPI_Finalize();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值