MPI接收多个部分

MPI receiving in multiple parts

本文关键字:个部 MPI      更新时间:2023-10-16

通常,当我想向下一个处理器发送缓冲区并从上一个处理器接收另一个缓冲区时,我会使用以下方法:

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_size 1元素)放在array1中,其中:

rcv_size1=rcv_size-rcv_size0;

通常,我所做的是首先在这里创建一个类似rcv_buff的伪数组,然后开始将值复制到array0和array1。我的问题是,MPI中有没有任何方法可以接收两个或多个序列中发送的字节?例如,直接接收array0中的第一个size0元素和array1中的其余元素?

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

#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,始终接收到一个大型暂存缓冲区中,并手动解压缩到不同的缓冲区中。

我所知道的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);