【问题标题】:MPI receiving in multiple partsMPI 多部分接收
【发布时间】:2013-08-06 02:04:04
【问题描述】:

通常,当我想将缓冲区发送到下一个处理器并从前一个处理器接收另一个缓冲区时,我使用以下内容:

MPI_Irecv(rcv_buff,rcv_size,
        MPI_DOUBLE,rcv_p,0,world,
        &request);
MPI_Send(snd_buff,snd_size,
        MPI_DOUBLE,snd_p,0,world);
MPI_Wait(&request,&status);

假设我想将 rcv_buff 的第一个 rcv_size0 元素放在 array0 中,其余的(rcv_size1 元素)放在 array1 中,其中:

rcv_size1=rcv_size-rcv_size0;

通常我会先在这里创建一个像rcv_buff 这样的虚拟数组,然后开始将值复制到array0 和array1。我的问题是 MPI 有什么方法可以接收两个或多个序列中发送的字节?例如直接接收 array0 中的第一个 size0 元素和 array1 中的其余元素?

【问题讨论】:

    标签: c++ mpi


    【解决方案1】:

    您可以通过创建特定于该对缓冲区的类型来做到这一点 - 接收到两个缓冲区中:

    #include <stdio.h>
    #include <mpi.h>
    #include <stdlib.h>
    
    int recv_split(const int total, const int src, const int tag,
                   double *buffA, const int sizeA, double *buffB) {
    
        if (total <= 0)    return -1;
        if (sizeA > total) return -1;
        if (buffA == NULL) return -2;
        if (buffB == NULL) return -2;
    
        const int sizeB = total - sizeA;
        int blocksizes[2] = {sizeA, sizeB};
        MPI_Datatype types[2] = {MPI_DOUBLE, MPI_DOUBLE};
        MPI_Aint displacements[2], addrA, addrB;
        MPI_Datatype splitbuffer;
        MPI_Status status;
    
        displacements[0] = 0;
        MPI_Get_address(buffA, &addrA);
        MPI_Get_address(buffB, &addrB);
        displacements[1] = addrB - addrA;
    
        MPI_Type_create_struct(2, blocksizes, displacements, types, &splitbuffer);
        MPI_Type_commit(&splitbuffer);
    
        MPI_Recv(buffA, 1, splitbuffer, src, tag, MPI_COMM_WORLD, &status);
    
        MPI_Type_free(&splitbuffer);
    
        return 0;
    }
    
    
    int main(int argc, char **argv) {
        int rank, size;
    
        MPI_Init(&argc, &argv);
    
        MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        MPI_Comm_size(MPI_COMM_WORLD, &size);
    
        const int sendSize = 15;
        const int tag = 1;
    
        if (rank == 0 && size >= 2) {
            double sendbuff[sendSize];
            for (int i=0; i<sendSize; i++)
                sendbuff[i] = 1.*i;
    
            MPI_Send(sendbuff, sendSize, MPI_DOUBLE, 1, tag, MPI_COMM_WORLD);
        }
        if (rank == 1) {
            const int buffLen = 12;
            const int recvIntoA = 10;
            double buffA[buffLen];
            double buffB[buffLen];
    
            for (int i=0; i<buffLen; i++) {
                buffA[i] = buffB[i] = -1.;
            }
    
            recv_split(sendSize, 0, tag, buffA, recvIntoA, buffB);
    
            printf("---Buffer A--\n");
            for (int i=0; i<buffLen; i++)
                printf("%5.1lf ", buffA[i]);
    
            printf("\n---Buffer B--\n");
            for (int i=0; i<buffLen; i++)
                printf("%5.1lf ", buffB[i]);
            printf("\n");
        }
    
        MPI_Finalize();
        return 0;
    
    }
    

    编译运行给出

    $ mpicc -o recvsplit recvsplit.c  -std=c99
    $ mpirun -np 2 ./recvsplit
    ---Buffer A--
      0.0   1.0   2.0   3.0   4.0   5.0   6.0   7.0   8.0   9.0  -1.0  -1.0
    ---Buffer B--
     10.0  11.0  12.0  13.0  14.0  -1.0  -1.0  -1.0  -1.0  -1.0  -1.0  -1.0
    

    请注意,这种类型只适用于这对缓冲区;不同的对通常会有不同的相对位移。当然,您也可以始终使用自己的代码或MPI_Unpack 接收到一个大型暂存缓冲区并手动解压缩到不同的缓冲区。

    【讨论】:

    • 请注意,您可以使用其他类型,例如Vector,以避免重新定义底层数据类型;我个人觉得这里的 struct 更清晰一些,但是 YMMV。
    【解决方案2】:

    据我所知,在 MPI 中没有任何东西可以让您这样做,尽管您可能可以使用一些讨厌的指针魔法来使其工作。一般来说,如果你想这样做的话,两次发送会更干净。

    另一件事不是直接回答您的问题,但您可能不知道上面的三行命令可以使用 MPI_SENDRECV 组合成一个。试试这条线:

    MPI_Sendrecv(snd_buff, snd_size, MPI_DOUBLE, snd_p, 0, 
                 rcv_buff, rcv_size, MPI_DOUBLE, rcv_p, 0, 
                 world, &status);
    

    【讨论】:

    • +1 用于发送接收;它很可能会变成与“引擎盖下”的原始代码非常相似的东西,但它的代码行数更少,而且对于第一次接触代码的人来说更清晰。
    猜你喜欢
    • 2013-12-20
    • 2015-03-08
    • 2018-03-12
    • 2015-08-16
    • 1970-01-01
    • 2011-05-17
    • 1970-01-01
    • 1970-01-01
    • 2014-08-11
    相关资源
    最近更新 更多