MPI收集2D子阵列

时间:2015-08-12 09:40:10

标签: c parallel-processing mpi

我知道之前已经多次回答这个问题并且有一个全面的答案here我已经阅读并试图使用但是我无法让我的代码因某些原因而起作用。

我已经删除了一些代码以使其更容易理解,但基本上我要做的是让每个进程初始化一个子数组并对其进行处理,然后将整个大数组放回到一起等级0. MPI_Gatherv给我一个段错误,我无法弄清楚原因。

非常感谢任何帮助。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <mpi.h>
#define N 32

void init_lattice(double **site, int row, int col){
  int i,j;
  for(i=0; i<row; i++){
    for(j=0; j<col; j++){
      site[i][j]=(drand48()/4294967295.0 + 0.5)*2*M_PI;
    }
  }
}

int main(int argc, char *argv[]){

  int nprocs, rank;
  MPI_Init(&argc, &argv);
  MPI_Comm_size (MPI_COMM_WORLD, &nprocs);
  MPI_Comm_rank (MPI_COMM_WORLD, &rank);   

  int dim = 2;
  int grid[dim];
  grid[0]=0;
  grid[1]=0;

  // Assign the grid dimensions
  MPI_Dims_create(nprocs, dim, grid);
  printf("Dim grid: length: %d, width: %d\n", grid[0], grid[1]);
  // The new communicator
  MPI_Comm comm_grid;
  // Allow cyclic behavior
  int periodic[dim];
  periodic[0] = 1;
  periodic[1] = 1;

  // Create the communicator
  MPI_Cart_create(MPI_COMM_WORLD, dim, grid, periodic, 0, &comm_grid);

  int block_len, block_width;
  block_len = N/grid[1];
  block_width = N/grid[0];

  int i, j;
  //Create lattice subset
  double  *data   = (double  *) malloc (block_len * block_width * sizeof(double));
  double **site = (double **) malloc (block_len * sizeof(double *));
  for (i = 0; i < block_len; i++)
    site[i] = & (data[i * block_width]);

  //Initialise lattice
  init_lattice(site, block_len, block_width);

  MPI_Datatype newtype, subtype;

  int sizes[dim];
  sizes[0]=N;
  sizes[1]=N;

  int subsizes[dim];  
  subsizes[0] = block_len;
  subsizes[1] = block_width;

  int starts[dim];   
  starts[0] = 0;
  starts[1] = 0;  

  MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_DOUBLE, &newtype);
  MPI_Type_create_resized(newtype, 0, N/grid[1]*sizeof(double), &subtype);
  MPI_Type_commit(&subtype);

  int sendcounts[grid[0]*grid[1]];
  int displs[grid[0]*grid[1]];

  if (rank == 0) {
    for (i=0; i<grid[0]*grid[1]; i++) sendcounts[i] = 1;
    int disp = 0;
    for (i=0; i<grid[0]; i++) {
      for (j=0; j<grid[1]; j++) {
        displs[i*grid[0]+j] = disp;
        disp += 1;
      }
      disp += ((N/grid[1])-1)*grid[0];
    }
  }

  //Create global lattice
  double  *global_data   = (double  *) malloc (N * N * sizeof(double));
  double **global_site = (double **) malloc (N * sizeof(double *));
  for (i = 0; i < N; i++)
    global_site[i] = & (global_data[i * N]);

  MPI_Gatherv(&(site[0][0]), N*N/(grid[0]*grid[1]),  MPI_DOUBLE, &(global_site[0][0]), sendcounts, displs, subtype, 0, MPI_COMM_WORLD);

  if(rank==0){
    printf("Rank: %d\n", rank);
    for(i=0; i<N; i++){
      for(j=0; j<N; j++){
        printf("%.2lf ", global_site[i][j]);  
      }
      printf("\n");
    }
  }

  return 0;
}

编辑: 好的,所以我已经将我的数组分配更改为连续的内存,一切都正常。谢谢talonmies!

1 个答案:

答案 0 :(得分:2)

这里的根本问题是MPI期望所有分配都是连续的内存块。您的siteglobal_site数组不是,它们是指针数组。 MPI例程只是读取每个单独行分配的结尾并导致您的段错误。

如果你想分配一个n x n数组用于MPI,那么你需要替换它:

  double **global_site;
  if(rank==0){
    global_site = malloc(sizeof(double *)*(N));
    for(i=0; i<N; i++)
      global_site[i] = malloc(sizeof(double)*(N));
  }

有这样的事情:

  double *global_site = malloc(sizeof(double)*(N * N));

您显然需要相应地调整其余代码。

看起来你实际使用指针数组的唯一原因是为了方便[i][j]样式的二维索引。如果你使用线性或斜线性内存,你可以轻松地制作一个小的预处理器宏或辅助函数,它可以为你提供那种仍然与MPI兼容的行或列主要有序存储器的索引样式。