使用 MPI 数据类型无法接收数组的子集
Trouble receiving a subset of an array using MPI Datatypes
我在发送和接收二维数组的列时遇到问题。
我有 2 个进程。第一个进程有一个二维数组,我想将它的一部分发送到第二个进程。所以说每个等级都有一个 9x9 数组,我想将等级 0 发送到等级 1 只是某些列:
示例:
-1--2--3-
-2--3--4-
-5--6--7-
...
我要发送“1,2,5,...”和“3,4,7,...”。
我编写了仅发送第一列的代码,并且通读了 this answer,我相信我已经为该列正确定义了 MPI_Type_vector:
MPI_Type_vector(dime,1,dime-1,MPI_INT,&LEFT_SIDE);
其中dime
这里,9,是数组的大小;我发送 9 个块,每块 1 MPI_INT,每个块以 8 的步幅分隔 - 但即使只发送这一列也会给我无效结果。
我的代码如下:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#define dime 9
int main (int argc, char *argv[])
{
int size,rank;
const int ltag=2;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &size); // Get the number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // Get the rank of the process
int table[dime][dime];
for (int i=0; i<dime; i++)
for (int j=0; j<dime; j++)
table[i][j] = rank;
int message[dime];
MPI_Datatype LEFT_SIDE;
MPI_Type_vector(dime,1,dime-1,MPI_INT,&LEFT_SIDE);
MPI_Type_commit(&LEFT_SIDE);
if(rank==0) {
MPI_Send(table, 1, LEFT_SIDE, 1, ltag, MPI_COMM_WORLD);
} else if(rank==1){
MPI_Status status;
MPI_Recv(message, 1, LEFT_SIDE, 0, ltag, MPI_COMM_WORLD, &status);
}
if(rank == 1 ){
printf("Rank 1's received data: ");
for(int i=0;i<dime;i++)
printf("%6d ",*(message+i));
printf("\n");
}
MPI_Finalize();
return 0;
}
但是当我 运行 它并查看我收到的数据时,我得到的不是全零就是乱码:
$ mpicc -o datatype datatype.c -Wall -g -O3 -std=c99
$ mpirun -np 2 datatype
Rank 1's received data: 0 32710 64550200 0 1828366128 32765 11780096 0 0
数字每次都在变化。我做错了什么?
我不太确定你的问题到底是什么(请在你的问题中明确说明,你会得到更好的答案!另请参阅 How do I ask good questions),但你的代码有几个问题。
您需要使用MPI_Type_vector(dime,1,dime,MPI_INT,&LEFT_SIDE);
,因为您要发送矩阵的每个 dime-th 元素。在 C 中,二维数组只是作为标准数组存储,元素 [i][j] 存储在索引 [i*dime+j] 处。您想要发送索引为 0、dime、2*dime、3*dime、...
的元素
如果您使用 LEFT_SIDE
数据类型来接收数据,MPI 将以 dime 元素的间隙存储您的数据项 - 类似于发送方。但是,您的接收缓冲区 message
是一个简单的数组。您需要接收这样的数据:MPI_Recv(message, dime, MPI_INT, 0, LTAG, newcomm,&status);
。此操作将接收一角整数并将它们放入您的 message
数组中。
编辑: 我更新了我的答案以匹配显着变化的问题。
@Mort 的回答是正确的,是第一个;我只是想用一些 ASCII 艺术图对其进行扩展,以尝试传达他的信息。
MPI 数据类型描述了数据在内存中的布局方式。让我们看一下您的二维数组以获得较小的 dime
(比如 4)和相应的 MPI_Type_vector:
MPI_Type_vector(count=dime, blocksize=1, stride=dime-1, type=MPI_INT ...
= 4 =1 = 3
data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
Vector: X - - X - - X - - X - -
请注意,MPI 类型中的步幅是类型 starts 之间的距离,而不是它们之间的间隙大小;所以你实际上想要 stride=dime 在这里,而不是 dime-1。这很容易解决,但这不是真正的问题:
MPI_Type_vector(count=dime, blocksize=1, stride=dime, type=MPI_INT ...
= 4 =1 = 4
data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
Vector: X - - - X - - - X - - - X - - -
好的,到目前为止一切顺利,我们正在选择正确的元素。但是我们没有正确地接收它们;代码试图将数据接收到一角硬币大小的数组中,使用相同的布局:
int message[dime];
MPI_Recv(message, 1, LEFT_SIDE, 0, ...
message = { 0, 1, 2, 3 };
Vector: X - - - X - - - X - - - X - - -
向量超出了消息的范围,(a) 在消息中留下未初始化的数据,这是乱码的来源,(b) 可能导致超出数组边界的分段错误。
至关重要的是,其中一个 MPI_Type_vector 描述了二维矩阵中所需数据的布局,但 不 描述了与它相同的数据的布局接收到一个紧凑的一维数组。
这里有两个选择。将数据简单地接收到 message
数组中 dime x MPI_INT
:
// ....
} else if(rank==1){
MPI_Status status;
MPI_Recv(message, dime, MPI_INT, 0, ltag, MPI_COMM_WORLD, &status);
}
//...
$ mpirun -np 2 datatype
Rank 1's received data: 0 0 0 0 0 0 0 0 0
或者直接将数据接收到 Rank 1 的 2d 矩阵中,覆盖相应的列:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#define dime 9
int main (int argc, char *argv[])
{
int size,rank;
const int ltag=2;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &size); // Get the number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // Get the rank of the process
int table[dime][dime];
for (int i=0; i<dime; i++)
for (int j=0; j<dime; j++)
table[i][j] = rank;
MPI_Datatype LEFT_SIDE;
MPI_Type_vector(dime,1,dime,MPI_INT,&LEFT_SIDE);
MPI_Type_commit(&LEFT_SIDE);
if(rank==0) {
MPI_Send(table, 1, LEFT_SIDE, 1, ltag, MPI_COMM_WORLD);
} else if(rank==1){
MPI_Status status;
MPI_Recv(table, 1, LEFT_SIDE, 0, ltag, MPI_COMM_WORLD, &status);
}
if(rank == 1 ){
printf("Rank 1's new array:\n");
for(int i=0;i<dime;i++) {
for(int j=0;j<dime;j++)
printf("%6d ",table[i][j]);
printf("\n");
}
printf("\n");
}
MPI_Type_free(&LEFT_SIDE);
MPI_Finalize();
return 0;
}
运行 给出
$ mpicc -o datatype datatype.c -Wall -g -O3 -std=c99
$ mpirun -np 2 datatype
Rank 1's new array:
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
(更正 MPI_Type_vector 后)
关于如何将其扩展到多列的其余部分可能最好留给另一个问题。
我在发送和接收二维数组的列时遇到问题。
我有 2 个进程。第一个进程有一个二维数组,我想将它的一部分发送到第二个进程。所以说每个等级都有一个 9x9 数组,我想将等级 0 发送到等级 1 只是某些列:
示例:
-1--2--3-
-2--3--4-
-5--6--7-
...
我要发送“1,2,5,...”和“3,4,7,...”。
我编写了仅发送第一列的代码,并且通读了 this answer,我相信我已经为该列正确定义了 MPI_Type_vector:
MPI_Type_vector(dime,1,dime-1,MPI_INT,&LEFT_SIDE);
其中dime
这里,9,是数组的大小;我发送 9 个块,每块 1 MPI_INT,每个块以 8 的步幅分隔 - 但即使只发送这一列也会给我无效结果。
我的代码如下:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#define dime 9
int main (int argc, char *argv[])
{
int size,rank;
const int ltag=2;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &size); // Get the number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // Get the rank of the process
int table[dime][dime];
for (int i=0; i<dime; i++)
for (int j=0; j<dime; j++)
table[i][j] = rank;
int message[dime];
MPI_Datatype LEFT_SIDE;
MPI_Type_vector(dime,1,dime-1,MPI_INT,&LEFT_SIDE);
MPI_Type_commit(&LEFT_SIDE);
if(rank==0) {
MPI_Send(table, 1, LEFT_SIDE, 1, ltag, MPI_COMM_WORLD);
} else if(rank==1){
MPI_Status status;
MPI_Recv(message, 1, LEFT_SIDE, 0, ltag, MPI_COMM_WORLD, &status);
}
if(rank == 1 ){
printf("Rank 1's received data: ");
for(int i=0;i<dime;i++)
printf("%6d ",*(message+i));
printf("\n");
}
MPI_Finalize();
return 0;
}
但是当我 运行 它并查看我收到的数据时,我得到的不是全零就是乱码:
$ mpicc -o datatype datatype.c -Wall -g -O3 -std=c99
$ mpirun -np 2 datatype
Rank 1's received data: 0 32710 64550200 0 1828366128 32765 11780096 0 0
数字每次都在变化。我做错了什么?
我不太确定你的问题到底是什么(请在你的问题中明确说明,你会得到更好的答案!另请参阅 How do I ask good questions),但你的代码有几个问题。
您需要使用
MPI_Type_vector(dime,1,dime,MPI_INT,&LEFT_SIDE);
,因为您要发送矩阵的每个 dime-th 元素。在 C 中,二维数组只是作为标准数组存储,元素 [i][j] 存储在索引 [i*dime+j] 处。您想要发送索引为 0、dime、2*dime、3*dime、... 的元素
如果您使用
LEFT_SIDE
数据类型来接收数据,MPI 将以 dime 元素的间隙存储您的数据项 - 类似于发送方。但是,您的接收缓冲区message
是一个简单的数组。您需要接收这样的数据:MPI_Recv(message, dime, MPI_INT, 0, LTAG, newcomm,&status);
。此操作将接收一角整数并将它们放入您的message
数组中。
编辑: 我更新了我的答案以匹配显着变化的问题。
@Mort 的回答是正确的,是第一个;我只是想用一些 ASCII 艺术图对其进行扩展,以尝试传达他的信息。
MPI 数据类型描述了数据在内存中的布局方式。让我们看一下您的二维数组以获得较小的 dime
(比如 4)和相应的 MPI_Type_vector:
MPI_Type_vector(count=dime, blocksize=1, stride=dime-1, type=MPI_INT ...
= 4 =1 = 3
data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
Vector: X - - X - - X - - X - -
请注意,MPI 类型中的步幅是类型 starts 之间的距离,而不是它们之间的间隙大小;所以你实际上想要 stride=dime 在这里,而不是 dime-1。这很容易解决,但这不是真正的问题:
MPI_Type_vector(count=dime, blocksize=1, stride=dime, type=MPI_INT ...
= 4 =1 = 4
data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
Vector: X - - - X - - - X - - - X - - -
好的,到目前为止一切顺利,我们正在选择正确的元素。但是我们没有正确地接收它们;代码试图将数据接收到一角硬币大小的数组中,使用相同的布局:
int message[dime];
MPI_Recv(message, 1, LEFT_SIDE, 0, ...
message = { 0, 1, 2, 3 };
Vector: X - - - X - - - X - - - X - - -
向量超出了消息的范围,(a) 在消息中留下未初始化的数据,这是乱码的来源,(b) 可能导致超出数组边界的分段错误。
至关重要的是,其中一个 MPI_Type_vector 描述了二维矩阵中所需数据的布局,但 不 描述了与它相同的数据的布局接收到一个紧凑的一维数组。
这里有两个选择。将数据简单地接收到 message
数组中 dime x MPI_INT
:
// ....
} else if(rank==1){
MPI_Status status;
MPI_Recv(message, dime, MPI_INT, 0, ltag, MPI_COMM_WORLD, &status);
}
//...
$ mpirun -np 2 datatype
Rank 1's received data: 0 0 0 0 0 0 0 0 0
或者直接将数据接收到 Rank 1 的 2d 矩阵中,覆盖相应的列:
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#define dime 9
int main (int argc, char *argv[])
{
int size,rank;
const int ltag=2;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &size); // Get the number of processes
MPI_Comm_rank(MPI_COMM_WORLD, &rank); // Get the rank of the process
int table[dime][dime];
for (int i=0; i<dime; i++)
for (int j=0; j<dime; j++)
table[i][j] = rank;
MPI_Datatype LEFT_SIDE;
MPI_Type_vector(dime,1,dime,MPI_INT,&LEFT_SIDE);
MPI_Type_commit(&LEFT_SIDE);
if(rank==0) {
MPI_Send(table, 1, LEFT_SIDE, 1, ltag, MPI_COMM_WORLD);
} else if(rank==1){
MPI_Status status;
MPI_Recv(table, 1, LEFT_SIDE, 0, ltag, MPI_COMM_WORLD, &status);
}
if(rank == 1 ){
printf("Rank 1's new array:\n");
for(int i=0;i<dime;i++) {
for(int j=0;j<dime;j++)
printf("%6d ",table[i][j]);
printf("\n");
}
printf("\n");
}
MPI_Type_free(&LEFT_SIDE);
MPI_Finalize();
return 0;
}
运行 给出
$ mpicc -o datatype datatype.c -Wall -g -O3 -std=c99
$ mpirun -np 2 datatype
Rank 1's new array:
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
0 1 1 1 1 1 1 1 1
(更正 MPI_Type_vector 后)
关于如何将其扩展到多列的其余部分可能最好留给另一个问题。