MPI_Alltoall
在使用MPI_Alltoall时,每一个进程都会向任意一个进程发送消息,每一个进程也都会接收到任意一个进程的消息。每个进程的接收缓冲区和发送缓冲区都是一个分为若干个数据块的数组。MPI_Alltoall的具体操作是:将进程i的发送缓冲区中的第j块数据发送给进程j,进程j将接收到的来自进程i的数据块放在自身接收缓冲区的第i块位置。
MPI_Alltoall与MPI_AllGahter相比较,区别在于:allgather操作中,不同进程向某一进程收集到的数据是完全相同的,而在alltoall中,不同的进程向某一进程收集到的数据是不同的。在每个进程的发送缓冲区中,为每个进程都单独准备了一块数据。
MPI_Alltoall(sendbuf,sendcount,sendtype,recvnbuf,recvcount,recvtype,comm);
在下面的例子中,我设置的数据块大小为10。每个进程发送缓冲区的内容定义如下:第i个进程发送缓冲区内第j块数据中的10个数据都为i*10+j。 若alltoall操作成功,则任意进程接收缓冲区内数据的十位应为0-proc_nums,个位应全为该进程的进程号。
#include<stdio.h>
#include"mpi.h"
#include"stdlib.h"
int main(int argc,char **argv)
{
int proc_nums,rank;
int block_size=10;//block_size<=10
int *send_buff;
int *recv_buff;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&proc_nums);
send_buff=(int *)malloc(block_size*proc_nums*sizeof(int));
recv_buff=(int *)malloc(block_size*proc_nums*sizeof(int));
for(int i=0;i<proc_nums;i++){
for(int j=0;j<block_size;j++){send_buff[i*block_size+j]=rank*10+i;}
}
MPI_Alltoall(send_buff,block_size,MPI_INT,recv_buff,block_size,MPI_INT,MPI_COMM_WORLD);
if(rank==0)
{
printf("\n");
for(int m=0;m<block_size*proc_nums;m++){printf("%d,",recv_buff[m]);}
printf("\n");
}
MPI_Finalize();
return 0;
}
MPI_Alltoallv
MPI_Alltoallv 在 MPI_Alltoall的基础上进一步增加了灵活性,它可以由sdispls指定待发送数据的位置,在接收方则由rdispls指定接收的数据存放在缓冲区的偏移量。
MPI_Alltoallv(sendbuf,sendcouts,sdispls,sendtype,recvbuf,recvcounts,rdispls,recvtype,comm);
从gather,scatter,allgather,alltoall再到对应的gatherv,scatterv,allgatherv,alltoallv,其实不难看出,前者其实都是后者的特殊情况,后者是更具一般性的调用操作。 以alltoall与alltoallv为例,若定义sendconts=[10,10,10,…,10],recvcounts=[10,10,10,…,10],rdispls=[0,10,20,…,10*proc_nums],sdispls=[0,10,20,…,10Xproc_nums]。MPI_Alltoall(sendbuf,10,MPI_INT,recvbuf,10,MPI_INT,MPI_COMMWORLD); 这一语句实质上与MPI_Alltoall(sendbuf,sendcounts,sdispls,MPI_INT,recvbuf,recvcounts,rdispls,MPI_INT,MPI_COMM_WORLD); 是等价的。
实例
在下面的例子中,每个进程的发送缓冲区都有proc_nums个数据块,第j个数据块有j个数据且这些数据都定义如下:数据的十位为本身进程号,个位为数据块号。 以进程1的发送缓冲区为例,数据为:10,11,11,12,12,12,13,13,13,13…,同理,进程2的发送缓冲区为:20,21,21,22,22,22…。定义对应的发送数目数组,发送缓冲区位置偏移数组,接收数目数组以及使得接收数据按进程号在接收缓冲区中依次存放的接收偏移数组。
#include<stdio.h>
#include"mpi.h"
#include"stdlib.h"
#include<math.h>
#include <unistd.h>
int main(int argc,char **argv)
{
int proc_nums,rank;//此处使用数据的十位来表示数据来自哪个进程,使用个位表示来自哪一块,所以最大进程数目为10。
int *send_buff;//各个进程发送缓冲区
int *recv_buff;//各个进程的接收缓冲区
int *sendcounts;//向各个进程发送数据数目数组
int *recvcounts;//从各个进程接收数据数目数组
int *sdispls;//发送偏移数组
int *rdispls;//接收偏移数组
int malloc_size;//各进程发送缓冲区,在本例中,各进程发送缓冲区大小相同。
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&proc_nums);
recv_buff=(int *)malloc((rank+1)*proc_nums*sizeof(int));//各进程的接收缓冲区大小不同。
sendcounts=(int *)malloc(proc_nums*sizeof(int));
recvcounts=(int *)malloc(proc_nums*sizeof(int));
rdispls=(int *)malloc(proc_nums*sizeof(int));
sdispls=(int *)malloc(proc_nums*sizeof(int));
int location=0;
for(int i=0;i<proc_nums;i++){//定义数目数组与偏移数组并得到发送缓冲区大小。
sendcounts[i]=i+1;
recvcounts[i]=rank+1;
rdispls[i]=i*(rank+1);
location+=i;
sdispls[i]=location;
}
malloc_size=location+proc_nums;
send_buff=(int *)malloc(malloc_size*sizeof(int));
//发送缓冲区内容
int start=0;
int hu;
for(int j=0;j<proc_nums;j++){
for(int n=0;n<j+1;n++){
send_buff[start+n]=rank*10+j;
hu=start+n;
}
start=hu+1;
}
MPI_Alltoallv(send_buff,sendcounts,sdispls,MPI_INT,recv_buff,recvcounts,rdispls,MPI_INT,MPI_COMM_WORLD);
//测试
for(int k=0;k<proc_nums;k++){
sleep(1);
if(rank==k){
printf("------------------------------------------------------------\n");
printf("processor %d 's recv_buff is:\n",k);
for(int m=0;m<(rank+1)*proc_nums;m++){printf("%d,",recv_buff[m]);}
printf("\n");
}
}
MPI_Finalize();
return 0;
}