MPI收集2D子阵列

3
我知道这个问题之前已经有很多次回答了,还有一个综合的答案在这里,我已经阅读并尝试使用它,但出于某些原因,我的代码无法正常工作。
我已经简化了我的代码以使其更易于理解,但基本上我想做的是让每个进程初始化一个子数组并对其进行操作,然后将整个大数组重新组合在第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个回答

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兼容。

谢谢您的回复,我已经更改了数组的分配,但是不幸的是,我仍然遇到了相同的段错误。 - stroopy
没事儿,我在代码其他地方也犯了个愚蠢的错误。现在一切都正常了,谢谢! - stroopy
@stroopy:为了标记这个问题已经得到解答,请考虑接受这个答案。这将提高搜索的可见性,可能会让下一个人更容易找到。 - talonmies

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接