首页 > 解决方案 > MPI_Get 不会在两个进程的缓冲区之间发送正确的元素

问题描述

我正在尝试创建一个程序,该程序最终将在 MPI 中转置一个矩阵,以便它可以用于进一步的计算。但现在我正在尝试做一件简单的事情:根进程有一个 4x4 矩阵“A”,其中包含以行优先顺序排列的元素 0..15。该数据被分散到 2 个进程,以便每个进程接收矩阵的一半。进程 0 有一个 2x4 子矩阵“a”并接收元素 0..7,进程 1 在其子矩阵“a”中获取元素 8..15。

我的目标是让这些进程使用 MPI_Get 相互交换它们的矩阵。由于遇到问题,我决定测试一个更简单的版本,并简单地让进程 0 获取进程 1 的“a”矩阵,这样,一旦我在 MPI_Get 调用和调用 MPI_fence。

然而输出不稳定,已经尝试了几个小时的故障排除,但未能解决问题。感谢您对此的帮助。

这是下面的代码和运行命令: mpirun -n 2 ./get

编译:mpicc -std=c99 -g -O3 -o get get.c -lm

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

#define NROWS 4
#define NCOLS 4

int allocate_matrix(int ***M, int ROWS, int COLS) {
  int *p;
  if (NULL == (p = malloc(ROWS * COLS * sizeof(int)))) {
    perror("Couldn't allocate memory for input (p in allocate_matrix)");
    return -1;
  }

  if (NULL == (*M = malloc(ROWS * sizeof(int*)))) {
    perror("Couldn't allocate memory for input (M in allocate_matrix)");
    return -1;
  }

  for(int i = 0; i < ROWS; i++) {
    (*M)[i] = &(p[i * COLS]);
  }
  return 0;
}

int main(int argc, char *argv[])
{
  int rank, nprocs, **A, **a, n_cols, n_rows, block_len;
  MPI_Win win;
  int errs = 0;

  if(rank==0)
    {
      allocate_matrix(&A, NROWS, NCOLS);
      for (int i=0; i<NROWS; i++)
        for (int j=0; j<NCOLS; j++)
          A[i][j] = i*NCOLS + j;
    }

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

  n_cols=NCOLS; //cols in a sub_matrix
  n_rows=NROWS/nprocs; //rows in a sub_matrix
  block_len = n_cols*n_rows;

  allocate_matrix(&a, n_rows, n_cols);
  for (int i = 0; i <n_rows; i++) 
    for (int j = 0; j < n_cols; j++) 
      a[i][j] = 0;


  MPI_Datatype block_type;
  MPI_Type_vector(n_rows, n_cols, n_cols, MPI_INTEGER, &block_type);
  MPI_Type_commit(&block_type);

  MPI_Scatter(*A, 1, block_type, &(a[0][0]), block_len, MPI_INTEGER, 0, MPI_COMM_WORLD);

  MPI_Barrier(MPI_COMM_WORLD);

  printf("process %d: \n", rank);
  for (int j=0; j<n_rows; j++){
    for (int i=0; i<n_cols; i++){
      printf("%d ",a[j][i]);
    }
    printf("\n");
  }


  if (rank == 0)
    {
      printf("TESTING, before Get a[0][0] %d\n", a[0][0]);
      MPI_Win_create(NULL, 0, 1, MPI_INFO_NULL, MPI_COMM_WORLD, &win);
      MPI_Win_fence((MPI_MODE_NOPUT | MPI_MODE_NOPRECEDE), win);
      MPI_Get(*a, 8, MPI_INTEGER, 1, 0, 8, MPI_INTEGER, win);
      MPI_Win_fence(MPI_MODE_NOSUCCEED, win);

      printf("TESTING, after Get a[0][0] %d\n", a[0][0]);

      printf("process %d:\n", rank);
      for (int j=0; j<n_rows; j++){
        for (int i=0; i<n_cols; i++){
          printf("%d ", a[j][i]);
        }
        printf("\n");
      }

    }
  else
    { /* rank = 1 */
      MPI_Win_create(a, n_rows*n_cols*sizeof(int), sizeof(int), MPI_INFO_NULL, MPI_COMM_WORLD, &win);
      MPI_Win_fence((MPI_MODE_NOPUT | MPI_MODE_NOPRECEDE), win);
      MPI_Win_fence(MPI_MODE_NOSUCCEED, win);
    }


  MPI_Type_free(&block_type);
  MPI_Win_free(&win);
  MPI_Finalize();
  return errs;
}

这是我得到的输出:

process 0: 
0 1 2 3 
4 5 6 7 

process 1: 
8 9 10 11 
12 13 14 15 

process 0:
1552976336 22007 1552976352 22007 
1552800144 22007 117 0 

但我想要的是第二次从进程 0 打印矩阵,它应该具有与进程 1 中相同的元素。

标签: cmatrixparallel-processingmpi

解决方案


首先,我怀疑这真的是您正在测试的代码。您正在释放一些未定义且未rank初始化的MPI 类型变量

if(rank==0)
  {
    allocate_matrix(&A, NROWS, NCOLS);
    for (int i=0; i<NROWS; i++)
      for (int j=0; j<NCOLS; j++)
        A[i][j] = i*NCOLS + j;
  }

并且代码段错误,因为A不会在根中分配。

移动这篇文章MPI_Comm_rank(),释放正确的 MPI 类型变量,并修复对MPI_Win_createin rank的调用1

  MPI_Win_create(&a[0][0], n_rows*n_cols*sizeof(int), sizeof(int), MPI_INFO_NULL, MPI_COMM_WORLD, &win);
  // This -------^^^^^^^^

产生你正在寻找的结果。

我建议在数组的开头使用一个符号,&a[0][0]而不是混合使用*aand &a[0][0]。这将在未来防止(或至少减少发生)类似错误。


推荐阅读