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

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

A = { ad be cf } 

和过程1(等级1)有

 A = { g h i } 

我希望两个处理器将这些列发送到0级,以便排名0将具有下面的另一个2Darrays。

 B = { adg beh cfi } 

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

我的具体问题是:

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

谢谢。

这是我的代码:

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

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

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

这肯定会分配一个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 

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

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

 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离开,而只需要一个字符。 幸运的是,我们可以通过将数据结构的上限设置为距离下限一个字符来做到这一点,以便下一个元素确实从正确的位置开始。 标准允许这样做,事实上,4.1.4节中的一个例子就是它的依据。

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

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

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

 #include  #include  #include  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