MPI全局通信之MPI_Alltoall和MPI_Alltoallv

有时候,我们的每个节点都需要向其他所有或者一部分节点发送数据,而不仅仅是根节点往其他节点发送数据,这个时候,我们就要用到全局通信。这边主要讲两个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();
}

这里写图片描述

示例代码下载

MPI_Alltoallv详细讲解ppt

猜你喜欢

转载自blog.csdn.net/u011337574/article/details/78947986
mpi