使用 MPI_Scatter 向所有其他进程发送一个数组索引的进程输出错误

Wrong output with processes sending one array index to all other processes using MPI_Scatter

我只是想了解 MPI,但似乎无法理解为什么以下程序输出与我预期的不同。

int rank, size;
  
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);

int *sendbuf, *recvbuf;
sendbuf = (int *) malloc(sizeof(int) * size);
recvbuf = (int *) malloc(sizeof(int) * size);

for(int i = 0; i < size; i++) {
  sendbuf[i] = rank;
}
for(int i = 0; i < size; i++) {
  printf("sendbuf[%d] = %d, rank: %d\n", i, sendbuf[i], rank);
}

MPI_Scatter(sendbuf, 1, MPI_INT, 
  recvbuf, 1, MPI_INT, rank, MPI_COMM_WORLD);

for(int i = 0; i < size; i++) {
  printf("recvbuf[%d] = %d, rank: %d\n", i, recvbuf[i], rank);
}

据我了解,MPI_Scatter 将数组中的 sendcount 值发送到所有进程。 在我的示例中,我为每个进程提供了一个数组,其中填充了自己的排名编号。 然后每个进程将其数组中的索引之一发送给所有其他进程。对于两个进程,第一个进程有一个 sendbuf 数组:

sendbuf[0] = 0
sendbuf[1] = 0

第二个进程(排名 1)有一个大小为 MPI_Comm_size 的数组,其中填充了 1。 预期输出应为:

recvbuf[0] = 0, rank: 0
recvbuf[1] = 1, rank: 0
recvbuf[0] = 0, rank: 1
revcbuf[1] = 1, rank: 1

但是我得到了以下输出(针对两个进程):

sendbuf[0] = 0, rank: 0
sendbuf[1] = 0, rank: 0
sendbuf[0] = 1, rank: 1
sendbuf[1] = 1, rank: 1
recvbuf[0] = 0, rank: 0
recvbuf[1] = 32690, rank: 0
recvbuf[0] = 1, rank: 1
recvbuf[1] = 32530, rank: 1

非常感谢任何指出我错误的帮助。

I am just trying to get my head around MPI and can't seem to understand, why the following programs output is different from what I expect.

问题在于使用MPI_Scatter来完成你的目标:

Sends data from one process to all other processes in a communicator Synopsis

int MPI_Scatter(const void *sendbuf, int sendcount, MPI_Datatype sendtype, void *recvbuf, int recvcount, MPI_Datatype recvtype, int root, MPI_Comm comm) Input Parameters

sendbuf address of send buffer (choice, significant only at root)
sendcount number of elements sent to each process (integer, significant only at root) sendtype data type of send buffer elements (significant only at root) (handle)
recvcount number of elements in receive buffer (integer)
recvtype data type of receive buffer elements (handle)
root rank of sending process (integer)
comm communicator (handle)

每个进程都应该使用相同的根调用MPI_Scatter ,而不是使用不同的根(进程' rank) 正如你所做的那样:

MPI_Scatter(sendbuf, 1, MPI_INT,  recvbuf, 1, MPI_INT, rank, MPI_COMM_WORLD);
                                                       ^^^^

因此,您误用了 MPI_Scatter,该例程的目的是 “将数据从一个进程发送到通信器中的所有其他进程”。下图(取自 source)最能说明问题:

只有一个根进程,它将其数据分散到不同的进程中。例如,当进程有一大块数据(例如, 一个数组)并且代码对该数据执行某些操作时,会使用此例程。您可以通过在进程之间拆分数据来并行化代码,其中每个进程在其分配的数据块上并行执行上述操作。之后,您可以调用 MPI_Gather 将所有进程的数据收集回数据来源的 原始 进程。

Then each process sends one of the indexes in its array to all other processes.

为此,您可以使用 MPI_Allgather“从所有任务收集数据并将组合数据分发给所有任务”。下图(摘自 source)最能说明这一点:

如你所见,每个进程都会收集所有进程(包括它自己)发送的数据。

一个运行例子:

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>


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);

        int *sendbuf = malloc(sizeof(int) * size);
        int *recvbuf = malloc(sizeof(int) * size);

        for(int i = 0; i < size; i++)
            sendbuf[i] = rank;      
        MPI_Allgather(sendbuf, 1, MPI_INT, recvbuf, 1, MPI_INT, MPI_COMM_WORLD);

        for(int i = 0; i < size; i++)
           printf("recvbuf[%d] = %d, rank: %d\n", i, recvbuf[i], rank);
        MPI_Finalize();
        return 0;
}

OUTPUT 两个进程:

recvbuf[0] = 0, rank: 0
recvbuf[1] = 1, rank: 0
recvbuf[0] = 0, rank: 1
recvbuf[1] = 1, rank: 1

对于您的特定情况(具有相同的输入大小),MPI_Alltoall would also work, to understand the differences between MPI_Allgather versus MPI_Alltoall, I recommend you to check this SO thread