【问题标题】:Writing distributed arrays using MPI-IO and Cartesian topology使用 MPI-IO 和笛卡尔拓扑编写分布式数组
【发布时间】:2016-02-05 20:50:08
【问题描述】:

我有一个 MPI 代码,它实现 2D 域分解以计算 PDE 的数值解。目前,我为每个进程编写了某些 2D 分布式数组(例如 array_x--> proc000x.bin)。我想将其减少为单个二进制文件。

array_0,array_1,

数组_2,数组_3,

假设上面说明了一个具有 4 个进程 (2x2) 的笛卡尔拓扑。每个二维数组都有维度 (nx + 2, nz + 2)。 +2 表示为通信目的添加到所有方面的“幽灵”层。

我想提取主数组(省略幽灵层)并将它们写入单个二进制文件,其顺序类似于,

array_0、array_1、array_2、array_3 --> output.bin

如果可能的话,最好把它写成好像我可以访问全局网格并逐行写入,即,

array_0 的第 0 行,array_1 的第 0 行,array_0 的第 1 行 array_1 的第 1 行 ....

下面的尝试尝试文件 array_test.c 中的两种输出格式中的前者

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

/* 2D array allocation */
float **alloc2D(int rows, int cols);

float **alloc2D(int rows, int cols) {
  int i, j; 
  float *data = malloc(rows * cols * sizeof(float));
  float **arr2D = malloc(rows * sizeof(float *));

  for (i = 0; i < rows; i++) {
    arr2D[i] = &(data[i * cols]);
  }                 
  /* Initialize to zero */
  for (i= 0; i < rows; i++) {
     for (j=0; j < cols; j++) {
        arr2D[i][j] = 0.0;      
     }                               
  }                    
  return arr2D;
}   

int main(void) {

   /* Creates 5x5 array of floats with padding layers and 
   * attempts to write distributed arrays */

   /* Run toy example with 4 processes */
   int i, j, row, col;
   int nx = 5, ny = 5, npad = 1;
   int my_rank, nproc=4;
   int dim[2] = {2, 2}; /* 2x2 cartesian grid */
   int period[2] = {0, 0};
   int coord[2];
   int reorder = 1;
   float **A = NULL;
   MPI_Comm grid_Comm;

   /* Initialize MPI */
   MPI_Init(NULL, NULL);
   MPI_Comm_size(MPI_COMM_WORLD, &nproc);
   MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

   /* Establish cartesian topology */
   MPI_Cart_create(MPI_COMM_WORLD, 2, dim, period, reorder, &grid_Comm);

   /* Get cartesian grid indicies of processes */
   MPI_Cart_coords(grid_Comm, my_rank, 2, coord);
   row = coord[1];
   col = coord[0];

   /* Add ghost layers */
   nx += 2 * npad;
   ny += 2 * npad;
   A = alloc2D(nx, ny);

   /* Create derived datatype for interior grid (output grid) */
   MPI_Datatype grid;
   int start[2] = {npad, npad};
   int arrsize[2] = {nx, ny};
   int gridsize[2] = {nx - 2 * npad, ny - 2 * npad};

   MPI_Type_create_subarray(2, arrsize, gridsize,
                            start, MPI_ORDER_C, MPI_FLOAT, &grid);
   MPI_Type_commit(&grid);

   /* Fill interior grid */
   for (i = npad; i < nx-npad; i++) {
      for (j = npad; j < ny-npad; j++) {
         A[i][j] = my_rank + i;
      }
   }

   /* MPI IO */
   MPI_File fh;
   MPI_Status status;
   char file_name[100];
   int N, offset;

   sprintf(file_name, "output.bin");
   MPI_File_open(grid_Comm, file_name, MPI_MODE_CREATE | MPI_MODE_WRONLY,
              MPI_INFO_NULL, &fh);

   N = (nx - 2 * npad) * (ny - 2 *npad);
   offset = (row * 2 + col) * N * sizeof(float);
   MPI_File_set_view(fh, offset, MPI_FLOAT, grid, "native",
                     MPI_INFO_NULL);
   MPI_File_write_all(fh, &A[0][0], N, MPI_FLOAT, MPI_STATUS_IGNORE);
   MPI_File_close(&fh);

   /* Cleanup */
   free(A[0]);
   free(A);
   MPI_Type_free(&grid);
   MPI_Finalize();

   return 0;

}

编译
mpicc -o array_test array_test.c  

一起运行
mpiexec -n 4 array_test

代码编译运行时,输出不正确。我假设在这种情况下我误解了派生数据类型和文件写入的使用。我会很感激一些帮助找出我的错误。

【问题讨论】:

    标签: c multidimensional-array parallel-processing mpi hpc


    【解决方案1】:

    你在这里犯的错误是你有错误的文件视图。您无需创建表示当前处理器负责的文件共享的类型,而是使用与您要写入的本地数据相对应的掩码。

    您实际上有两个截然不同的面具需要考虑:

    1. 本地数据的掩码,不包括晕层;和
    2. 全局数据的掩码,因为它应该被整理到文件中。

    前者对应这个布局:

    在这里,您要为给定进程在文件上输出的数据以深蓝色显示,不应在文件上写入的光晕层以浅蓝色显示。

    后面对应这个布局:

    在这里,每种颜色对应于来自不同进程的本地数据,分布在二维笛卡尔网格上。

    要了解你需要创建什么来达到这个最终结果,你必须反省:

    1. 您对 IO 例程的最终调用应该是 MPI_File_write_all(fh, &amp;A[0][0], 1, interior, MPI_STATUS_IGNORE);。因此,您必须定义 interior 类型,以排除光环边界。幸运的是,您创建的类型 grid 已经做到了这一点。所以我们会使用它。
    2. 但是现在,您必须拥有该文件的视图才能允许此MPI_Fie_write_all() 调用。所以视图必须如第二张图片中描述的那样。因此,我们将创建一个新的 MPI 类型来表示它。为此,MPI_Type_create_subarray() 是我们所需要的。

    这里是这个函数的概要:

    int MPI_Type_create_subarray(int ndims,
                                 const int array_of_sizes[],
                                 const int array_of_subsizes[],
                                 const int array_of_starts[],
                                 int order,
                                 MPI_Datatype oldtype,
                                 MPI_Datatype *newtype)
    
       Create a datatype for a subarray of a regular, multidimensional array
    
    INPUT PARAMETERS
      ndims   - number of array dimensions (positive integer)
      array_of_sizes
              - number of elements of type oldtype in each
                dimension of the full array (array of positive integers)
      array_of_subsizes
              - number of elements of type oldtype in each dimension of
                the subarray (array of positive integers)
      array_of_starts
              - starting coordinates of the subarray in each dimension
                (array of nonnegative integers)
      order   - array storage order flag (state)
      oldtype - array element datatype (handle)
    
    OUTPUT PARAMETERS
      newtype - new datatype (handle)
    

    对于我们的二维笛卡尔文件视图,以下是我们需要的这些输入参数:

    • ndims: 2 因为网格是二维的
    • array_of_sizes:这些是要输出的全局数组的维度,即{ nnx*dim[0], nny*dim[1] }
    • array_of_subsizes:这些是要输出的数据的本地份额的维度,即{ nnx, nny }
    • array_of_start:这些是本地共享进入全局网格的 x,y 起始坐标,即{ nnx*coord[0], nny*coord[1] }
    • order:排序是 C,所以这必须是 MPI_ORDER_C
    • oldtype:数据是floats 所以这必须是MPI_FLOAT

    现在我们有了文件视图的类型,我们只需使用MPI_File_set_view(fh, 0, MPI_FLOAT, view, "native", MPI_INFO_NULL); 应用它,魔法就完成了。

    您的完整代码变为:

    #include <stdio.h>
    #include <mpi.h>
    #include <stdlib.h>
    
    /* 2D array allocation */
    float **alloc2D(int rows, int cols);
    
    float **alloc2D(int rows, int cols) {
      int i, j; 
      float *data = malloc(rows * cols * sizeof(float));
      float **arr2D = malloc(rows * sizeof(float *));
    
      for (i = 0; i < rows; i++) {
        arr2D[i] = &(data[i * cols]);
      }                 
      /* Initialize to zero */
      for (i= 0; i < rows; i++) {
         for (j=0; j < cols; j++) {
            arr2D[i][j] = 0.0;      
         }                               
      }                    
      return arr2D;
    }   
    
    int main(void) {
    
       /* Creates 5x5 array of floats with padding layers and 
       * attempts to write distributed arrays */
    
       /* Run toy example with 4 processes */
       int i, j, row, col;
       int nx = 5, ny = 5, npad = 1;
       int my_rank, nproc=4;
       int dim[2] = {2, 2}; /* 2x2 cartesian grid */
       int period[2] = {0, 0};
       int coord[2];
       int reorder = 1;
       float **A = NULL;
       MPI_Comm grid_Comm;
    
       /* Initialize MPI */
       MPI_Init(NULL, NULL);
       MPI_Comm_size(MPI_COMM_WORLD, &nproc);
       MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
    
       /* Establish cartesian topology */
       MPI_Cart_create(MPI_COMM_WORLD, 2, dim, period, reorder, &grid_Comm);
    
       /* Get cartesian grid indicies of processes */
       MPI_Cart_coords(grid_Comm, my_rank, 2, coord);
       row = coord[1];
       col = coord[0];
    
       /* Add ghost layers */
       nx += 2 * npad;
       ny += 2 * npad;
       A = alloc2D(nx, ny);
    
       /* Create derived datatype for interior grid (output grid) */
       MPI_Datatype grid;
       int start[2] = {npad, npad};
       int arrsize[2] = {nx, ny};
       int gridsize[2] = {nx - 2 * npad, ny - 2 * npad};
    
       MPI_Type_create_subarray(2, arrsize, gridsize,
                                start, MPI_ORDER_C, MPI_FLOAT, &grid);
       MPI_Type_commit(&grid);
    
       /* Fill interior grid */
       for (i = npad; i < nx-npad; i++) {
          for (j = npad; j < ny-npad; j++) {
             A[i][j] = my_rank + i;
          }
       }
    
       /* Create derived type for file view */
       MPI_Datatype view;
       int nnx = nx-2*npad, nny = ny-2*npad; 
       int startV[2] = { coord[0]*nnx, coord[1]*nny };
       int arrsizeV[2] = { dim[0]*nnx, dim[1]*nny };
       int gridsizeV[2] = { nnx, nny };
    
       MPI_Type_create_subarray(2, arrsizeV, gridsizeV,
                                startV, MPI_ORDER_C, MPI_FLOAT, &view);
       MPI_Type_commit(&view);
    
       /* MPI IO */
       MPI_File fh;
    
       MPI_File_open(grid_Comm, "output.bin", MPI_MODE_CREATE | MPI_MODE_WRONLY,
                     MPI_INFO_NULL, &fh);
    
       MPI_File_set_view(fh, 0, MPI_FLOAT, view, "native", MPI_INFO_NULL);
       MPI_File_write_all(fh, &A[0][0], 1, grid, MPI_STATUS_IGNORE);
       MPI_File_close(&fh);
    
       /* Cleanup */
       free(A[0]);
       free(A);
       MPI_Type_free(&view);
       MPI_Type_free(&grid);
       MPI_Finalize();
    
       return 0;
    
    }
    

    【讨论】:

    • 我不得不稍微调整一下,但这几乎完全符合我的目标,尽管我不完全理解如何。这似乎以我在原始帖子中概述的第二种方式存储数组,而我本来希望是第一种。您能否解释一下如何访问内存以产生结果?
    • 感谢您的更新,尽管我从您之前提交的内容中理解了那部分。我对内存和写入的内部工作原理更加好奇,即如何将 2D 数组写入文件,但我会对此进行研究。
    猜你喜欢
    • 2014-01-22
    • 2012-03-17
    • 2017-12-17
    • 2017-12-18
    • 2018-03-10
    • 2016-12-01
    • 2017-08-22
    • 2015-11-23
    • 1970-01-01
    相关资源
    最近更新 更多