如何从处理器MPI_Gatherv列,其中每个进程可以发送不同数量的列

Ree*_*eep 6 c mpi

假设2个进程正在参与.过程0(等级0)具有

A = { a d
      b e
      c f
    }
Run Code Online (Sandbox Code Playgroud)

和进程1(排名1)有

A = { g
      h
      i
    }
Run Code Online (Sandbox Code Playgroud)

我希望两个处理器都将这些列发送到0级,以便排名0在另一个2D阵列中具有以下内容.

B = { a d g
      b e h
      c f i
    }
Run Code Online (Sandbox Code Playgroud)

我为MPI_Gatherv创建了一个新的列数据类型,我正在尝试以下代码,这让我无处可去.

我的具体问题是:

  1. 我应该如何处理这个问题
  2. 什么应该是send_type和recv_type.
  3. 如何指定位移(如果它们是新数据类型或MPI_CHAR)

谢谢.

这是我的代码:

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

int main(int argc, char *argv[])
{
  int numprocs, my_rank;
   long int i, j;
   MPI_Status status;
   char **A;
   char **B;
  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
  MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

  if(my_rank == 0)
  {
    A = (char **)calloc((3), sizeof(char *));
    B = (char **)calloc((3), sizeof(char *));
    for(i=0; i<3; ++i)
    {
       A[i] = (char *)calloc(2, sizeof(char));
       B[i] = (char *)calloc(3, sizeof(char));
    }

    A[0][0] = 'a';
    A[1][0] = 'b';
    A[2][0] = 'c';
    A[0][1] = 'd';
    A[1][1] = 'e';
    A[2][1] = 'f';
  }
  else
  {
    A = (char **)calloc((3), sizeof(char *));
    for(i=0; i<3; ++i)
    {
       A[i] = (char *)calloc(1, sizeof(char));
    }
    A[0][0] = 'g';
    A[1][0] = 'h';
    A[2][0] = 'i';

  }
  MPI_Datatype b_col_type;
  MPI_Type_vector(3, 1, 1, MPI_CHAR, &b_col_type);
  MPI_Type_commit(&b_col_type);
  int displs[2] = {0, 2};
  int recvcounts[2] = {2, 1};
  MPI_Gatherv(&A[0][0], recvcounts[my_rank], b_col_type, &B[0][0], recvcounts, displs,    b_col_type, 0, MPI_COMM_WORLD);
  if(my_rank == 0)
  {
    for(i=0; i<3; ++i)
    {
      for(j=0; j<3; ++j)
        printf("%c ", B[i][j]);
      printf("\n");
    }
  }
  MPI_Finalize();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*rsi 6

所以首先 - 这一直是MPI和C数组 - 你不能真正做标准的C二维数组.我们来看看这个:

A = (char **)calloc((3), sizeof(char *));
for(i=0; i<3; ++i)
{
   A[i] = (char *)calloc(2, sizeof(char));
}
Run Code Online (Sandbox Code Playgroud)

这肯定会分配一个3x2字符数组,但你不知道结果数据是如何在内存中布局的.特别是,有一个在不能保证所有A[1][0]紧跟A[0][1].这使得创建跨越数据结构的MPI数据类型非常困难!您需要分配3x2连续字节,然后使数组指向它:

char **charalloc2d(int n, int m) {
    char *data = (char *)calloc(n*m,sizeof(char));
    char **array = (char **)calloc(n, sizeof(char *));
    for (int i=0; i<n; i++)
        array[i] = &(data[i*m]);

    return array;
}

void charfree2d(char **array) {
    free(array[0]);
    free(array);
    return;
}

/* ... */
nrows = 3;
ncols = 2;
A = charalloc2d(nrows,ncols);
Run Code Online (Sandbox Code Playgroud)

现在我们对数组的布局有所了解,并且可以依赖于它来构建数据类型.

你在数据类型的正确轨道上 -

MPI_Datatype b_col_type;
MPI_Type_vector(3, 1, 1, MPI_CHAR, &b_col_type);
MPI_Type_commit(&b_col_type);
Run Code Online (Sandbox Code Playgroud)

MPI_Type_vector的签名是(count,blocklen,stride,old_type,*newtype).
我们想要nrows字符,以1为块; 但它们间隔开了ncols; 这就是步伐.

请注意,这实际上是A数组的列类型,而不是B; 类型取决于数组中的列数.所以每个进程都使用不同的sendtype,这很好.

MPI_Datatype a_col_type;
MPI_Type_vector(nrows, 1, ncols, MPI_CHAR, &a_col_type);
MPI_Type_commit(&a_col_type);
Run Code Online (Sandbox Code Playgroud)

最后一步是MPI_Gatherv,在这里你必须有点可爱.诀窍是,我们希望一次发送(并接收)多个这些东西 - 也就是几个连续的东西.但是我们需要下一个专栏,不要让ncols离开,而只需要一个字符.幸运的是,我们可以通过将数据结构的上限设置为距离下限一个字符来做到这一点,以便下一个元素确实从正确的位置开始.标准允许这样做,事实上,4.1.4节中的一个例子就是它的依据.

为此,我们创建一个调整大小的类型,它在启动后只结束一个字节:

MPI_Type_create_resized(a_col_type, 0, 1*sizeof(char), &new_a_col_type);
MPI_Type_commit(&new_a_col_type); 
Run Code Online (Sandbox Code Playgroud)

同样地B; 现在我们可以按照人们的预期发送和接收这些的倍数.所以以下内容对我有用:

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

char **charalloc2d(int n, int m) {
    char *data = (char *)calloc(n*m,sizeof(char));
    char **array = (char **)calloc(n, sizeof(char *));
    for (int i=0; i<n; i++)
        array[i] = &(data[i*m]);

    return array;
}

void charfree2d(char **array) {
    free(array[0]);
    free(array);
    return;
}


int main(int argc, char *argv[])
{
    int numprocs, my_rank;
    int nrows, ncols, totncols;
    long int i, j;
    char **A;
    char **B;
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);

    if(my_rank == 0)
    {
        nrows=3;
        ncols=2;
        totncols = 3;

        A = charalloc2d(nrows, ncols);
        B = charalloc2d(nrows, totncols);

        A[0][0] = 'a';
        A[1][0] = 'b';
        A[2][0] = 'c';
        A[0][1] = 'd';
        A[1][1] = 'e';
        A[2][1] = 'f';
    }
    else
    {
        nrows = 3;
        ncols = 1;
        A = charalloc2d(nrows, ncols);
        B = charalloc2d(1,1); /* just so gatherv survives */
        A[0][0] = 'g';
        A[1][0] = 'h';
        A[2][0] = 'i';

    }
    MPI_Datatype a_col_type, new_a_col_type;
    MPI_Type_vector(nrows, 1, ncols, MPI_CHAR, &a_col_type);
    MPI_Type_commit(&a_col_type);

    /* make the type have extent 1 character -- now the next
     * column starts in the next character of the array 
     */
    MPI_Type_create_resized(a_col_type, 0, 1*sizeof(char), &new_a_col_type);
    MPI_Type_commit(&new_a_col_type);

    MPI_Datatype b_col_type, new_b_col_type;
    if (my_rank == 0) {
        MPI_Type_vector(nrows, 1, totncols, MPI_CHAR, &b_col_type);
        MPI_Type_commit(&b_col_type);

        /* similarly "resize" b columns */
        MPI_Type_create_resized(b_col_type, 0, 1*sizeof(char), &new_b_col_type);
        MPI_Type_commit(&new_b_col_type);
    }

    int displs[2] = {0, 2};
    int recvcounts[2] = {2, 1};
    MPI_Gatherv(A[0], recvcounts[my_rank], new_a_col_type,
                B[0], recvcounts, displs, new_b_col_type,
                0, MPI_COMM_WORLD);
    if(my_rank == 0)
    {
        for(i=0; i<3; ++i)
        {
            for(j=0; j<3; ++j)
                printf("%c ", B[i][j]);
            printf("\n");
        }
    }
    MPI_Finalize();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)