MPI Scatter 从最终分区丢失值

MPI Scatter losing values from the final partition

我有一个数字数组,需要将其分散到 MPI 程序中的每个节点。设置是我有一个从 1 到 100 的数字数组,除了数字 2 之外的所有偶数都被删除了。由于我删除偶数的方式,数字 2 是数组中的最后一个元素。

所以我的数组包含 51 个奇数,3、5、7,... 99、2。我的问题是散点后的最终分区不包含数组中的最后三个数字 - 97、99和 2.

int *oddsOnly  = //array as setup above, 3,5,7,...99,2
int chunkSize = (oddsFound / worldSize);
int *localPartition = new int[chunkSize];

// Send everyone the chunk size
MPI_Bcast(&chunkSize, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Scatter(oddsOnly, chunkSize, MPI_INT, localPartition, chunkSize, MPI_INT, 0, MPI_COMM_WORLD);

我知道问题是行数和数组大小不均匀,我试过

chunkSize = ceil(oddsFound / worldSize);

chunkSize = (oddsFound / worldSize) + 1;

但这会在拆分中给出重复项。

现在我得到

0 scatter is 1  3   5   7   9   11  13  15  17  19  21  23

1 scatter is 25 27  29  31  33  35  37  39  41  43  45  47  

2 scatter is 49 51  53  55  57  59  61  63  65  67  69  71

3 scatter is 73 75  77  79  81  83  85  87  89  91  93  95  

是否可以整齐地做我正在尝试的事情?我看过 scatterV,但我不确定它是我需要的。我应该补充一点,我 没有 来分散,所以也许有更好的 MPI 方法来做到这一点。

一种方法是使用 MPI_Scatter 并向数组添加填充,以便它的大小在进程之间均匀分割。但是,每当进程必须操作该数组时,它需要忽略填充部分。

另一种方法是使用 MPI_ScatterV,并“手动”在进程之间划分块。

这种方法的一个例子:

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

int main(int argc,char *argv[]){
    MPI_Init(NULL,NULL); // Initialize the MPI environment
    int world_rank; 
    int world_size;
    MPI_Comm_rank(MPI_COMM_WORLD,&world_rank);
    MPI_Comm_size(MPI_COMM_WORLD,&world_size);

    int size = 50;
    int *numbers= NULL;
    if(world_rank == 0){
      numbers = malloc(sizeof(int) * size);
      for(int i = 0; i < size; i++)
         numbers[i] = (2*i + 1); 
    }
    // The Important Part
    int sendcounts [world_size];
    int displs [world_size]; 
    int res = size % world_size;
    int size_per_process = size / world_size;
    int increment = 0;
    for(int processID = 0; processID < world_size; processID++){
       displs[processID] = increment;
       sendcounts[processID] = (processID + 1 <= res) ? size_per_process + 1 : size_per_process;
       increment += sendcounts[processID];
    }
    int process_size = sendcounts[world_rank];
    int local_numbers[process_size];
    MPI_Scatterv(numbers, sendcounts, displs, MPI_INT, local_numbers, process_size, MPI_INT, 0, MPI_COMM_WORLD);  
    if(world_rank == world_size - 1){
       for(int i = 0; i < size_per_process; i++)
      printf("%d ", local_numbers[i]); 
       printf("\n"); 
    }

    MPI_Finalize();
    return 0;
 }

这有点令人费解,但好消息是对于这种类型的分布它总是相同的。

代码解释:

首先我们要了解MPI_Scatterv的参数:

MPI_Scatterv

Scatters a buffer in parts to all processes in a communicator

int MPI_Scatterv(const void *sendbuf, const int *sendcounts, const int *displs, MPI_Datatype sendtype, void *recvbuf, int recvcount,
          MPI_Datatype recvtype, int root, MPI_Comm comm)

输入参数

sendbuf address of send buffer (choice, significant only at root)
sendcounts integer array (of length group size) specifying the number of elements to send to each processor
displs integer array (of length group size). Entry i specifies the displacement (relative to sendbuf from which to take the outgoing data to process i
sendtype data type of send buffer elements (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)

首先,我们创建一个数组,其中包含每个进程将发送的元素数量:

int sendcounts [world_size];

然后我们创建位移数组:

int displs [world_size]; 

然后我们计算额外的迭代次数:

int res = size % world_size;

然后我们计算每个进程保证有的迭代次数:

int size_per_process = size / world_size;

最后我们计算每个进程的大小和位移:

int increment = 0;
for(int processID = 0; processID < world_size; processID++){
   displs[processID] = increment;
   sendcounts[processID] = (processID + 1 <= res) ? size_per_process + 1 : size_per_process;
   increment += sendcounts[processID];
}

行:

   sendcounts[processID] = (processID + 1 <= res) ? size_per_process + 1 : size_per_process;

意味着我们将从左到右分配额外的迭代(即, 从进程等级 0 到进程总数 - 1)。