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

时间:2011-03-20 21:46:22

标签: c mpi

说2个进程正在参与。过程0(等级0)具有

A = { a d
      b e
      c f
    }

和进程1(排名1)

A = { g
      h
      i
    }

我希望两个处理器都将这些列发送到0级,这样排名0就会有另外两个2D阵列。

B = { a d g
      b e h
      c f i
    }

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

我的具体问题是:

  1. 我应该如何处理这个
  2. send_type和recv_type应该是什么。
  3. 如何指定排名(如果它们是新数据类型或MPI_CHAR)
  4. 感谢。

    这是我的代码:

    #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;
    }
    

1 个答案:

答案 0 :(得分:6)

首先关闭 - 这一直都是MPI和C数组 - 你无法真正做到标准的C二维数组。我们来看看:

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

这肯定会分配3x2字符数组,但您不知道结果数据是如何在内存中布局的。特别是, all 并不保证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);

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

您的数据类型正确 -

MPI_Datatype b_col_type;
MPI_Type_vector(3, 1, 1, MPI_CHAR, &b_col_type);
MPI_Type_commit(&b_col_type);

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);

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

,同样适用于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;
}