有时候,我们的每个节点都需要向其他所有或者一部分节点发送数据,而不仅仅是根节点往其他节点发送数据,这个时候,我们就要用到全局通信。这边主要讲两个API,MPI_Alltoall和MPI_Alltoallv.
MPI_Alltoall
int MPI_Alltoall(const void *sendbuf,
int sendcount,
MPI_Datatype sendtype,
void *recvbuf,
int recvcount,
MPI_Datatype recvtype,
MPI_Comm comm)
sendbuf代表发送缓冲区的起始地址
sendcount代表要发送的数量
sendtype代表发送的数据类型
recvbuf代表接收缓冲区的起始位置
recvcount代表要接收的数量
recvtype代表要接收的类型
comm代表通信子
这个是指当前进程往其他每个进程(包括自己)要发送的数据都是一样的,都是发送sendbuf中的数据。
MPI_Alltoallv
有时候,我们当前进程往其他进程发送的数据不一样,个数也不一样,这个时候就需要用MPI_Alltoallv来解决。
int MPI_Alltoallv(const void *sendbuf,
const int *sendcounts,
const int *sdispls,
MPI_Datatype sendtype,
void *recvbuf,
const int *recvcounts,
const int *rdispls,
MPI_Datatype recvtype,
MPI_Comm comm)
相比MPI_Alltoall,这个方法有几个参数不一样:
int* sendcounts 和int *recvcounts。这两个参数你可以把它当做两个数组,数组中的元素代表往其他节点各发送(接收)多少数据。比如说,sendcounts[0]=3,sendcounts[1]=4,代表该节点要往0号节点发送3个sendtype的数据,往1号节点发送4个sendtype的数据。
多了两个参数,int *sdispls和int *rdispls,这两个可以看做是数组,数组中的每个元素代表了要发送(接收)的那块数据相对于缓冲区起始位置的位移量。
例子
下面的例子是将一个数组的某些部分分别发送到各个进程上,由于每个节点不知道要接收多少数据,所以,先发送数量过去告诉每个节点需要接收多少数据,这样就确定了接收缓冲区的大小。(注意,例子中用的datatype是MPI_BYTE,所以发送和接收的数量都是按照byte来计算的)
#include <mpi.h>
#include <iostream>
#include <vector>
using namespace std;
int main(int argc,char *argv[]){
int myid,numprocs;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
int sendCounts[numprocs];
int recvCounts[numprocs];
int sdisp[numprocs];
int rdisp[numprocs];
std::vector<int> nums;//要发送的数据是一个数组,里面装了10个int
for (int i = 0; i < 10; ++i)
{
nums.push_back(i);
}
//要发送给其他节点的数据的数量,转化成byte来发送
//发送给0号节点3个int,
//发送给1号节点0个int,
//发送给2号节点3个int
sendCounts[0] = sizeof(int)*3;
sendCounts[1]=sizeof(int)*0;
sendCounts[2]=sizeof(int)*3;
sendCounts[3]=sizeof(int)*4;
sendCounts[4]=sizeof(int)*0;
//提前通知一下节点,各个节点要准备接收多少数据
MPI_Alltoall(sendCounts,1,MPI_INT,
recvCounts,1,MPI_INT,MPI_COMM_WORLD);
cout<<"myid is "<<myid<<" receive count is "<<recvCounts[myid]<<" bytes"<<endl;
//发送数据相对于发送缓冲区起始位置的偏移量
sdisp[0]=0;
for (int i = 1; i < numprocs; ++i)
{
sdisp[i]=sendCounts[i-1]+sdisp[i-1];
}
//接收的数据放在相对缓冲区中的起始位置的偏移量
rdisp[0]=0;
for (int i = 1; i < numprocs; ++i)
{
rdisp[i] = recvCounts[i-1]+rdisp[i-1];
}
int ssize =0;
int rsize = 0;
//初始化发送的数量和接收的数量
for (int i = 0; i < numprocs; ++i)
{
ssize = ssize + sendCounts[i];
rsize = rsize + recvCounts[i];
}
std::vector<int>recvvector;
recvvector.resize(rsize/sizeof(int));//接收缓冲区的大小,因为每个节点要接收所有其他节点发过来的,所以要用发过来的总数量除以int的byte数
MPI_Alltoallv(nums.data(),sendCounts,sdisp,MPI_BYTE,
recvvector.data(),recvCounts,rdisp,MPI_BYTE,
MPI_COMM_WORLD);
cout<<"myid is "<<myid<<" receive element size is "<<recvvector.size()<<endl;
for (int i = 0; i < recvvector.size(); ++i)
{
cout<<"myid is "<<myid<<" recvvector index i is "<<i<<" element is "<<recvvector.at(i)<<endl;
}
MPI_Finalize();
}