MPI_Gather of columns

时间:2014-02-17 14:17:43

标签: mpi

我有一个数组,在我的计算过程之间按列拆分。之后我想在一个进程(0)中收集这个数组。

每个进程的列都保存在数组A中,进程0有一个数组F用于收集数据。 F数组的大小为n * n,每个进程都有part_size列,因此本地数组A是n * part_size。列被发送到交替进程 - c0再次进入p0,c1进入p1,c2进入p0,依此类推。

我创建了用于发送和接收列的新数据类型。

在所有流程中:

MPI_Type_vector(n, 1, part_size, MPI::FLOAT, &col_send);
MPI_Type_commit(&col_send);

在流程0:

MPI_Type_vector(n, 1, n, MPI::FLOAT, &col_recv);
MPI_Type_commit(&col_recv);

现在我想按如下方式收集数组:

MPI_Gather(&A, part_size, col_send, &F, part_size, col_recv, 0, MPI::COMM_WORLD);

然而,结果并不像预期的那样。我的例子有n = 4和两个进程。因此,p0的值应该在F的第0列和第2列中,p1应该存储在1和3中。而是p0的两列都存储在0和1中,而p1的值根本不存在。 / p>

0: F[0][0]: 8.31786
0: F[0][1]: 3.90439
0: F[0][2]: -60386.2
0: F[0][3]: 4.573e-41
0: F[1][0]: 0
0: F[1][1]: 6.04768
0: F[1][2]: -60386.2
0: F[1][3]: 4.573e-41
0: F[2][0]: 0
0: F[2][1]: 8.88266
0: F[2][2]: -60386.2
0: F[2][3]: 4.573e-41
0: F[3][0]: 0
0: F[3][1]: 0
0: F[3][2]: -60386.2
0: F[3][3]: 4.573e-41

我会承认我对这个想法不合时宜。我显然误解了Gather或Type_vector如何工作并保存它们的值。有人能指出我正确的方向吗?任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

我看到的问题是,使用MPI_Type_vector()创建的数据类型的范围从第一个项到最后一个项。例如:

col_recv数据类型的范围介于><之间(我希望掩码的这种表示足够清晰):

>x . . .
 x . . .
 x . . .
 x<. . .

这是13个MPI_FLOAT项目(必须按行读取,即C顺序)。 收到其中两个将导致:

>x . . .
 x . . .
 x . . .
 x y . .
 . y . .
 . y . .
 . y . .

显然这不是你想要的。

要让MPI_Gather()正确跳过接收器上的数据,您需要将col_recv的范围设置为与 ONE ELEMENT 完全相同的范围。您可以使用MPI_Type_create_resized()

执行此操作
>x<. . .
 x . . .
 x . . .
 x . . .

以便接收连续的块正确交错:

   x y . . 
   x y . . 
   x y . . 
   x y . . 

然而,接收两列而不是一列将导致:

   x x y y
   x x y y
   x x y y
   x x y y

这又不是你想要的,即使是更接近。

由于您需要交错列,因此您需要创建一个更复杂的数据类型,能够描述所有列,并具有以前的1项目范围:

每列被分隔(步幅)为一个ELEMENT(即范围 - 不是之前定义的列的大小,即4个元素):

  >x<. x .
   x . x .
   x . x .
   x . x .

每个处理器接收其中一个,你会得到你想要的东西:

   x y x y
   x y x y
   x y x y
   x y x y

您也可以使用MPI_Type_create_darray()来完成,因为它允许创建适合与scalapack的块循环分布一起使用的数据类型,作为它的一个子版本。

我也试过了。这是两个处理器上的工作代码:

#include <mpi.h>

#define N      4
#define NPROCS 2
#define NPART  (N/NPROCS)

int main(int argc, char **argv) {
  float a_send[N][NPART];
  float a_recv[N][N] = {0};
  MPI_Datatype column_send_type;
  MPI_Datatype column_recv_type;
  MPI_Datatype column_send_type1;
  MPI_Datatype column_recv_type1;
  MPI_Datatype matrix_columns_type;
  MPI_Datatype matrix_columns_type1;

  MPI_Init(&argc, &argv);
  int my_rank;
  MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

  for(int i=0; i<N; ++i) {
    for(int j=0; j<NPART; ++j) {
      a_send[i][j] = my_rank*100+10*(i+1)+(j+1);
    }
  }

  MPI_Type_vector(N, 1, NPART, MPI_FLOAT, &column_send_type);
  MPI_Type_commit(&column_send_type);

  MPI_Type_create_resized(column_send_type, 0, sizeof(float), &column_send_type1);
  MPI_Type_commit(&column_send_type1);

  MPI_Type_vector(N, 1, N, MPI_FLOAT, &column_recv_type);
  MPI_Type_commit(&column_recv_type);

  MPI_Type_create_resized(column_recv_type, 0, sizeof(float), &column_recv_type1);
  MPI_Type_commit(&column_recv_type1);

  MPI_Type_vector(NPART, 1, NPROCS, column_recv_type1, &matrix_columns_type);
  MPI_Type_commit(&matrix_columns_type);

  MPI_Type_create_resized(matrix_columns_type, 0, sizeof(float), &matrix_columns_type1);
  MPI_Type_commit(&matrix_columns_type1);

  MPI_Gather(a_send, NPART, column_send_type1, a_recv, 1, matrix_columns_type1, 0, MPI_COMM_WORLD);

  if (my_rank==0) {
    for(int i=0; i<N; ++i) {
      for(int j=0; j<N; ++j) {
        printf("%4.0f  ",a_recv[i][j]);
      }
      printf("\n");
    }
  }

  MPI_Finalize();
}