使用MPI_Type_create_subarray发送时可以转置数组吗?

我试图在C中使用MPI转置矩阵。每个进程都有一个方形子矩阵,我想将它发送到正确的进程(网格上的“对面”),将其转换为通信的一部分。

我正在使用MPI_Type_create_subarray ,它有一个订单参数,分别是行主要和列主要的MPI_ORDER_CMPI_ORDER_FORTRAN 。 我认为,如果我作为其中一个发送,并作为另一个接收,那么我的矩阵将被转换为通信的一部分。 然而,这似乎并没有发生 – 它只是保持非转置。

代码的重要部分如下所示,整个代码文件都可以在这个要点中找到 。 有没有人有任何想法为什么这不起作用? 这种方法是否应该进行转置工作? 我已经想过,在阅读了MPI_ORDER_CMPI_ORDER_FORTRAN的描述MPI_ORDER_FORTRAN ,但也许没有。

 /* ----------- DO TRANSPOSE ----------- */ /* Find the opposite co-ordinates (as we know it's a square) */ coords2[0] = coords[1]; coords2[1] = coords[0]; /* Get the rank for this process */ MPI_Cart_rank(cart_comm, coords2, &rank2); /* Send to these new coordinates */ tag = (coords[0] + 1) * (coords[1] + 1); /* Create new derived type to receive as */ /* MPI_Type_vector(rows_in_core, cols_in_core, cols_in_core, MPI_DOUBLE, &vector_type); */ sizes[0] = rows_in_core; sizes[1] = cols_in_core; subsizes[0] = rows_in_core; subsizes[1] = cols_in_core; starts[0] = 0; starts[1] = 0; MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_FORTRAN, MPI_DOUBLE, &send_type); MPI_Type_commit(&send_type); MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_DOUBLE, &recv_type); MPI_Type_commit(&recv_type); /* We're sending in row-major form, so it's just rows_in_core * cols_in_core lots of MPI_DOUBLE */ MPI_Send(&array[0][0], 1, send_type, rank2, tag ,cart_comm); /* Receive from these new coordinates */ MPI_Recv(&new_array[0][0], 1, recv_type, rank2, tag, cart_comm, &status); 

我本以为这也会起作用,但显然不行。

如果你通过MPI标准的相关位实际定义了生成的MPI_Type_create_subarray映射,原因就变得清晰了 – MPI_Type_create_subarray映射出子数组在完整数组中占用的区域,但是以线性顺序遍历内存,所以数据布局不会改变。 换句话说,当大小等于子量时,子arrays只是一个连续的内存块; 对于一个严格小于整个数组的子数组,您只需更改正在发送/接收的子区域,而不是数据排序。 选择一个子区域时,您可以看到效果:

 int sizes[]={cols,rows}; int subsizes[]={2,4}; int starts[]={1,1}; MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_FORTRAN, MPI_INT, &ftype); MPI_Type_commit(&ftype); MPI_Type_create_subarray(2, sizes, subsizes, starts, MPI_ORDER_C, MPI_INT, &ctype); MPI_Type_commit(&ctype); MPI_Isend(&(send[0][0]), 1, ctype, 0, 1, MPI_COMM_WORLD,&reqc); MPI_Recv(&(recvc[0][0]), 1, ctype, 0, 1, MPI_COMM_WORLD, &statusc); MPI_Isend(&(send[0][0]), 1, ctype, 0, 1, MPI_COMM_WORLD,&reqf); MPI_Recv(&(recvf[0][0]), 1, ftype, 0, 1, MPI_COMM_WORLD, &statusf); /*...*/ printf("Original:\n"); printarr(send,rows,cols); printf("\nReceived -- C order:\n"); printarr(recvc,rows,cols); printf("\nReceived: -- Fortran order:\n"); printarr(recvf,rows,cols); 

给你这个:

  0 1 2 3 4 5 6 10 11 12 13 14 15 16 20 21 22 23 24 25 26 30 31 32 33 34 35 36 40 41 42 43 44 45 46 50 51 52 53 54 55 56 60 61 62 63 64 65 66 Received -- C order: 0 0 0 0 0 0 0 0 11 12 13 14 0 0 0 21 22 23 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Received: -- Fortran order: 0 0 0 0 0 0 0 0 11 12 0 0 0 0 0 13 14 0 0 0 0 0 21 22 0 0 0 0 0 23 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 

因此,相同的数据将被发送和接收; 所有真正发生的事情是数组大小,子范围和开始都被颠倒了。

可以使用MPI数据类型进行转置 – 标准甚至提供了几个示例 ,其中一个我已在此处音译为C – 但您必须自己创建类型。 好消息是,它实际上不再是子arrays的东西:

 MPI_Type_vector(rows, 1, cols, MPI_INT, &col); MPI_Type_hvector(cols, 1, sizeof(int), col, &transpose); MPI_Type_commit(&transpose); MPI_Isend(&(send[0][0]), rows*cols, MPI_INT, 0, 1, MPI_COMM_WORLD,&req); MPI_Recv(&(recv[0][0]), 1, transpose, 0, 1, MPI_COMM_WORLD, &status); MPI_Type_free(&col); MPI_Type_free(&transpose); printf("Original:\n"); printarr(send,rows,cols); printf("Received\n"); printarr(recv,rows,cols); $ mpirun -np 1 ./transpose2 Original: 0 1 2 3 4 5 6 10 11 12 13 14 15 16 20 21 22 23 24 25 26 30 31 32 33 34 35 36 40 41 42 43 44 45 46 50 51 52 53 54 55 56 60 61 62 63 64 65 66 Received 0 10 20 30 40 50 60 1 11 21 31 41 51 61 2 12 22 32 42 52 62 3 13 23 33 43 53 63 4 14 24 34 44 54 64 5 15 25 35 45 55 65 6 16 26 36 46 56 66