简体   繁体   English

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

[英]How to MPI_Gatherv columns from processor, where each process may send different number of columns

Say 2 processes are participating. 假设2个进程正在参与。 Process 0 (rank 0) has 过程0(等级0)具有

A = { a d
      b e
      c f
    }

and process 1 (rank 1) has 和进程1(排名1)有

A = { g
      h
      i
    }

I want both processors to send these columns to rank 0 so that rank 0 will have the following in say another 2D-array. 我希望两个处理器都将这些列发送到0级,以便排名0在另一个2D阵列中具有以下内容。

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

I create a new column data type for the MPI_Gatherv and am trying the following code, which is getting me no where. 我为MPI_Gatherv创建了一个新的列数据类型,我正在尝试以下代码,这让我无处可去。

My specific questions are: 我的具体问题是:

  1. How should I approach this 我应该如何处理这个问题
  2. What should be the send_type and recv_type. 什么应该是send_type和recv_type。
  3. How should displacements be specified ( should they be in term of the new data type or MPI_CHAR) 如何指定位移(如果它们是新数据类型或MPI_CHAR)

Thanks. 谢谢。

This is my code: 这是我的代码:

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

So first off - and this comes up with MPI and C arrays all the time - you can't really do the standard C two dimensional array thing. 所以首先 - 这一直是MPI和C数组 - 你不能真正做标准的C二维数组。 Let's look at this: 我们来看看这个:

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

This will definately allocate a 3x2 array of characters, but you have no idea how the resulting data is laid out in memory. 这肯定会分配一个3x2字符数组,但你不知道结果数据是如何在内存中布局的。 In particular, there's no guarantee at all that A[1][0] immediately follows A[0][1] . 特别是,谁也不能保证在所有 A[1][0]紧随A[0][1] That makes it very difficult to create MPI datatypes which span the data structure! 这使得创建跨越数据结构的MPI数据类型非常困难! You need to allocate 3x2 contiguous bytes, and then make the array point into it: 您需要分配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);

Now we know something about the layout of the array, and can depend on that to build datatypes. 现在我们对数组的布局有所了解,并且可以依赖于它来构建数据类型。

You're on the right track with the data types -- 你在数据类型的正确轨道上 -

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

the signature of MPI_Type_vector is (count, blocklen, stride, old_type, *newtype). MPI_Type_vector的签名是(count,blocklen,stride,old_type,* newtype)。
We want nrows characters, that come in blocks of 1; 我们想要nrows字符,以1为块; but they're spaced ncols apart; 但它们间隔开了ncols; so that's the stride. 这就是步伐。

Note that this is really the column type of the A array, rather than B ; 注意,这实际上是A数组的列类型,而不是B ; the type will depend on the number of columns in the array. 类型取决于数组中的列数。 So each process is using a different sendtype, which is fine. 所以每个进程都使用不同的sendtype,这很好。

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

The final step is the MPI_Gatherv , and here you have to be a little cute. 最后一步是MPI_Gatherv ,在这里你必须有点可爱。 The trick is, we want to send (and receive) multiple of these things at a time - that is, several consecutive ones. 诀窍是,我们希望一次发送(并接收)多个这些东西 - 也就是几个连续的东西。 But we need the next column not to be nrows*ncols chars away, but just one char away. 但是我们需要下一个专栏,不要让ncols离开,而只需要一个字符。 Luckily, we can do that by setting the upper bound of the data structure to be just one character away from the lower bound, so that the next element does start in the right place. 幸运的是,我们可以通过将数据结构的上限设置为距离下限一个字符来做到这一点,以便下一个元素确实从正确的位置开始。 This is allowed by the standard , and in fact one of their examples in section 4.1.4 there hinges on it. 标准允许这样做,事实上,4.1.4节中的一个例子就是它的依据。

To do that, we create a resized type that ends just one byte after it starts: 为此,我们创建一个调整大小的类型,它在启动后只结束一个字节:

MPI_Type_create_resized(a_col_type, 0, 1*sizeof(char), &new_a_col_type);
MPI_Type_commit(&new_a_col_type); 

and similarly for B ; B类似; and now we can send and recieve multiples of these as one would expect. 现在我们可以按照人们的预期发送和接收这些的倍数。 So the following works for me: 所以以下内容对我有用:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM