【问题标题】:MPI Scatter losing values from the final partitionMPI Scatter 从最终分区中丢失值
【发布时间】:2021-07-09 18:36:18
【问题描述】:

我有一个数字数组,需要分散到 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 方法。

【问题讨论】:

    标签: c++ c parallel-processing mpi hpc


    【解决方案1】:

    一种方法是使用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

    将缓冲区分散到通信器中的所有进程中

    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 发送缓冲区的地址(选择,仅在根处有效)
    sendcounts 整数数组(长度为组大小),指定要发送到的元素数量每个处理器
    displs 整数数组(长度为组大小)。条目 i 指定位移(相对于从其获取传出数据以处理 i
    sendtype 发送缓冲区元素(句柄)的数据类型
    recvcount接收缓冲区中的元素数(整数)
    recvtype 接收缓冲区元素的数据类型(句柄)
    root 发送进程的等级(整数)
    comm 通讯器(句柄)

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

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

    【讨论】:

      猜你喜欢
      • 2016-07-22
      • 1970-01-01
      • 2018-01-19
      • 2021-04-16
      • 2017-04-10
      • 2017-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多