我有一个数组,在我的计算过程之间按列拆分。之后我想在一个进程(0)中收集这个数组。
每个进程的列都保存在数组A中,进程0有一个数组F用于收集数据。 F数组的大小为n * n,每个进程都有part_size列,因此本地数组A是n * part_size。列被发送到交替进程 - c0再次进入p0,c1进入p1,c2进入p0,依此类推。
我创建了用于发送和接收列的新数据类型。
在所有流程中:
MPI_Type_vector(n, 1, part_size, MPI::FLOAT, &col_send);
MPI_Type_commit(&col_send);
在流程0:
MPI_Type_vector(n, 1, n, MPI::FLOAT, &col_recv);
MPI_Type_commit(&col_recv);
现在我想按如下方式收集数组:
MPI_Gather(&A, part_size, col_send, &F, part_size, col_recv, 0, MPI::COMM_WORLD);
然而,结果并不像预期的那样。我的例子有n = 4和两个进程。因此,p0的值应该在F的第0列和第2列中,p1应该存储在1和3中。而是p0的两列都存储在0和1中,而p1的值根本不存在。 / p>
0: F[0][0]: 8.31786
0: F[0][1]: 3.90439
0: F[0][2]: -60386.2
0: F[0][3]: 4.573e-41
0: F[1][0]: 0
0: F[1][1]: 6.04768
0: F[1][2]: -60386.2
0: F[1][3]: 4.573e-41
0: F[2][0]: 0
0: F[2][1]: 8.88266
0: F[2][2]: -60386.2
0: F[2][3]: 4.573e-41
0: F[3][0]: 0
0: F[3][1]: 0
0: F[3][2]: -60386.2
0: F[3][3]: 4.573e-41
我会承认我对这个想法不合时宜。我显然误解了Gather或Type_vector如何工作并保存它们的值。有人能指出我正确的方向吗?任何帮助将不胜感激。
答案 0 :(得分:1)
我看到的问题是,使用MPI_Type_vector()
创建的数据类型的范围从第一个项到最后一个项。例如:
col_recv
数据类型的范围介于>
和<
之间(我希望掩码的这种表示足够清晰):
>x . . .
x . . .
x . . .
x<. . .
这是13个MPI_FLOAT项目(必须按行读取,即C顺序)。 收到其中两个将导致:
>x . . .
x . . .
x . . .
x y . .
. y . .
. y . .
. y . .
显然这不是你想要的。
要让MPI_Gather()
正确跳过接收器上的数据,您需要将col_recv
的范围设置为与 ONE ELEMENT 完全相同的范围。您可以使用MPI_Type_create_resized()
:
>x<. . .
x . . .
x . . .
x . . .
以便接收连续的块正确交错:
x y . .
x y . .
x y . .
x y . .
然而,接收两列而不是一列将导致:
x x y y
x x y y
x x y y
x x y y
这又不是你想要的,即使是更接近。
由于您需要交错列,因此您需要创建一个更复杂的数据类型,能够描述所有列,并具有以前的1项目范围:
每列被分隔(步幅)为一个ELEMENT(即范围 - 不是之前定义的列的大小,即4个元素):
>x<. x .
x . x .
x . x .
x . x .
每个处理器接收其中一个,你会得到你想要的东西:
x y x y
x y x y
x y x y
x y x y
您也可以使用MPI_Type_create_darray()
来完成,因为它允许创建适合与scalapack的块循环分布一起使用的数据类型,作为它的一个子版本。
我也试过了。这是两个处理器上的工作代码:
#include <mpi.h>
#define N 4
#define NPROCS 2
#define NPART (N/NPROCS)
int main(int argc, char **argv) {
float a_send[N][NPART];
float a_recv[N][N] = {0};
MPI_Datatype column_send_type;
MPI_Datatype column_recv_type;
MPI_Datatype column_send_type1;
MPI_Datatype column_recv_type1;
MPI_Datatype matrix_columns_type;
MPI_Datatype matrix_columns_type1;
MPI_Init(&argc, &argv);
int my_rank;
MPI_Comm_rank(MPI_COMM_WORLD, &my_rank);
for(int i=0; i<N; ++i) {
for(int j=0; j<NPART; ++j) {
a_send[i][j] = my_rank*100+10*(i+1)+(j+1);
}
}
MPI_Type_vector(N, 1, NPART, MPI_FLOAT, &column_send_type);
MPI_Type_commit(&column_send_type);
MPI_Type_create_resized(column_send_type, 0, sizeof(float), &column_send_type1);
MPI_Type_commit(&column_send_type1);
MPI_Type_vector(N, 1, N, MPI_FLOAT, &column_recv_type);
MPI_Type_commit(&column_recv_type);
MPI_Type_create_resized(column_recv_type, 0, sizeof(float), &column_recv_type1);
MPI_Type_commit(&column_recv_type1);
MPI_Type_vector(NPART, 1, NPROCS, column_recv_type1, &matrix_columns_type);
MPI_Type_commit(&matrix_columns_type);
MPI_Type_create_resized(matrix_columns_type, 0, sizeof(float), &matrix_columns_type1);
MPI_Type_commit(&matrix_columns_type1);
MPI_Gather(a_send, NPART, column_send_type1, a_recv, 1, matrix_columns_type1, 0, MPI_COMM_WORLD);
if (my_rank==0) {
for(int i=0; i<N; ++i) {
for(int j=0; j<N; ++j) {
printf("%4.0f ",a_recv[i][j]);
}
printf("\n");
}
}
MPI_Finalize();
}