在进程之间传递子矩阵
Passing submatrix between processes
首先,我正在学习消息传递接口 (MPI)
https://computing.llnl.gov/tutorials/mpi/
在创建您自己的 MPI 数据类型时,我遇到了麻烦
用它。
我的程序正在尝试获取每个象限。
说下面的 4 x 4 矩阵,
A = {
1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0,
9.0, 10.0, 11.0, 12.0,
13.0, 14.0, 15.0, 16.0
}
所以我想把它分成 4 个子矩阵,这样当 master 发出 3 个子矩阵(子矩阵 1、2、3)时,每个 worker 都能收到它各自的子矩阵。
Submatrix 0 | Submatrix 1
Submatrix 2 | Submatrix 3
现在,我的程序只获取每个子矩阵的第一行,并将第二行打印为零。
以下是当前的打印输出。 (可以忽略子矩阵0)
3 4
0 0
9 10
0 0
11 12
0 0
附上我的程序。任何指针将不胜感激。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<mpi.h>
//matrix size
#define SIZE 4
double A[SIZE][SIZE] ={
1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0,
9.0, 10.0, 11.0, 12.0,
13.0, 14.0, 15.0, 16.0
};
static double B[SIZE/2][SIZE/2];
MPI_Datatype QUAD;
#define QUADRANT(Q,y,x) (Q[y * SIZE/2]+(x * SIZE/2))
void printout(double Y[SIZE/2][SIZE/2]){
int i,j;
for(i=0;i< SIZE/2;i++){
for(j=0; j< SIZE/2; j++){
printf("%.0f ",Y[i][j]);
}
printf("\n");
}
}
int main(int argc, char** argv){
int rank, size, i, j;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
MPI_Status stat;
//Define a MPI datatype, Quadrant
MPI_Type_vector(SIZE/2, SIZE/2, SIZE, MPI_DOUBLE, &QUAD);
MPI_Type_commit(&QUAD);
//master process
if(rank==0){
MPI_Send(QUADRANT(A,0,1),1,QUAD,1,0, MPI_COMM_WORLD);
MPI_Send(QUADRANT(A,1,0),1,QUAD,2,0,MPI_COMM_WORLD);
MPI_Send(QUADRANT(A,1,1),1,QUAD,3,0,MPI_COMM_WORLD);
}else{
MPI_Recv(B,1,QUAD,0,0,MPI_COMM_WORLD,&stat);
printout(B);
printf("\n");
}
MPI_Finalize();
}
有类似的节目在
https://computing.llnl.gov/tutorials/mpi/samples/C/mpi_vector.c
但试图获取列矩阵中的所有数字。
你的大部分问题是你想要接收的不是 QUAD
,而是一个直接的 2x2 子矩阵。因此,您的代码的发送部分没问题。但是,接收错误。
所以你需要做的是修复你的代码,要么在发送前将你的象限复制到直 2x2 矩阵中,要么在接收方分配一个 2x4 接收缓冲区来存储发送的消息,然后将相关部分复制到您的 2x2 矩阵中。
下面是使用第二个选项的代码,我选择它是为了说明目的,因为您似乎想使用派生类型。 (注意:我保留了代码风格,尽管这不是我自己使用的风格)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<mpi.h>
//matrix size
#define SIZE 4
double A[SIZE][SIZE] ={
1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0,
9.0, 10.0, 11.0, 12.0,
13.0, 14.0, 15.0, 16.0
};
static double B[SIZE/2][SIZE/2];
static double tmpB[SIZE/2][SIZE];
MPI_Datatype QUAD;
#define QUADRANT(Q,y,x) (Q[y * SIZE/2]+(x * SIZE/2))
void printout(double Y[SIZE/2][SIZE/2]){
int i,j;
for(i=0;i< SIZE/2;i++){
for(j=0; j< SIZE/2; j++){
printf("%.0f ",Y[i][j]);
}
printf("\n");
}
}
void compress(double Y[SIZE/2][SIZE/2], double tmpY[SIZE/2][SIZE]){
int i,j;
for(i=0;i< SIZE/2;i++){
for(j=0; j< SIZE/2; j++){
Y[i][j]=tmpY[i][j];
}
}
}
int main(int argc, char** argv){
int rank, size, i, j;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
MPI_Status stat;
//Define a MPI datatype, Quadrant
MPI_Type_vector(SIZE/2, SIZE/2, SIZE, MPI_DOUBLE, &QUAD);
MPI_Type_commit(&QUAD);
//master process
if(rank==0){
MPI_Send(QUADRANT(A,0,1),1,QUAD,1,0,MPI_COMM_WORLD);
MPI_Send(QUADRANT(A,1,0),1,QUAD,2,0,MPI_COMM_WORLD);
MPI_Send(QUADRANT(A,1,1),1,QUAD,3,0,MPI_COMM_WORLD);
}else{
MPI_Recv(tmpB,1,QUAD,0,0,MPI_COMM_WORLD,&stat);
compress(B,tmpB);
printout(B);
printf("\n");
}
MPI_Finalize();
}
最后一句话:在现实生活中,如果你要进行这种传输,我建议你在发送之前寻求将数据压缩到象限中的解决方案,以避免额外的潜在无用副本在 MPI 库本身内部(尽管它们是否发生超出了 MPI 标准的范围)
问题是,MPI_Recv
与不符合接收缓冲区布局的相同跨步矢量数据类型一起使用。
例如调用
MPI_Send(QUADRANT(A,0,1),1,QUAD,1,0, MPI_COMM_WORLD);
与 QUAD
的定义一起正确选择 A
右上象限的数据值,并通过网络发送值 3.0、4.0、7.0 和 8.0。
但是,相同的数据类型不能用于接收缓冲区,因为 B
中行的大小,因此步长小于 A
中的行。因此,值 7.0 和 8.0 存储在 B
:
的范围之外
Matrix as seen by MPI_Recv Memory Layout of
with data-type QUAD Matrix B
M[0][0] <-- 3.0 --> B[0][0]
M[0][1] <-- 4.0 --> B[0][1]
M[0][2] B[1][0] <-- unchanged, e.g. 0.0
M[0][3] B[1][1] <-- unchanged, e.g. 0.0
M[1][0] <-- 7.0 --> !beyond array!
M[1][1] <-- 8.0 --> !beyond array!
M[1][2]
M[1][3]
...
EDIT: 符合标准,接收时必须使用相同的类型。因此,接收缓冲区必须这样声明:
double B[SIZE/2][SIZE]; // SIZE elements per row.
之后,可以按照 Gilles 在他的回答中所做的那样压缩数组。
首先,我正在学习消息传递接口 (MPI) https://computing.llnl.gov/tutorials/mpi/
在创建您自己的 MPI 数据类型时,我遇到了麻烦 用它。
我的程序正在尝试获取每个象限。 说下面的 4 x 4 矩阵,
A = {
1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0,
9.0, 10.0, 11.0, 12.0,
13.0, 14.0, 15.0, 16.0
}
所以我想把它分成 4 个子矩阵,这样当 master 发出 3 个子矩阵(子矩阵 1、2、3)时,每个 worker 都能收到它各自的子矩阵。
Submatrix 0 | Submatrix 1
Submatrix 2 | Submatrix 3
现在,我的程序只获取每个子矩阵的第一行,并将第二行打印为零。
以下是当前的打印输出。 (可以忽略子矩阵0)
3 4
0 0
9 10
0 0
11 12
0 0
附上我的程序。任何指针将不胜感激。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<mpi.h>
//matrix size
#define SIZE 4
double A[SIZE][SIZE] ={
1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0,
9.0, 10.0, 11.0, 12.0,
13.0, 14.0, 15.0, 16.0
};
static double B[SIZE/2][SIZE/2];
MPI_Datatype QUAD;
#define QUADRANT(Q,y,x) (Q[y * SIZE/2]+(x * SIZE/2))
void printout(double Y[SIZE/2][SIZE/2]){
int i,j;
for(i=0;i< SIZE/2;i++){
for(j=0; j< SIZE/2; j++){
printf("%.0f ",Y[i][j]);
}
printf("\n");
}
}
int main(int argc, char** argv){
int rank, size, i, j;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
MPI_Status stat;
//Define a MPI datatype, Quadrant
MPI_Type_vector(SIZE/2, SIZE/2, SIZE, MPI_DOUBLE, &QUAD);
MPI_Type_commit(&QUAD);
//master process
if(rank==0){
MPI_Send(QUADRANT(A,0,1),1,QUAD,1,0, MPI_COMM_WORLD);
MPI_Send(QUADRANT(A,1,0),1,QUAD,2,0,MPI_COMM_WORLD);
MPI_Send(QUADRANT(A,1,1),1,QUAD,3,0,MPI_COMM_WORLD);
}else{
MPI_Recv(B,1,QUAD,0,0,MPI_COMM_WORLD,&stat);
printout(B);
printf("\n");
}
MPI_Finalize();
}
有类似的节目在 https://computing.llnl.gov/tutorials/mpi/samples/C/mpi_vector.c
但试图获取列矩阵中的所有数字。
你的大部分问题是你想要接收的不是 QUAD
,而是一个直接的 2x2 子矩阵。因此,您的代码的发送部分没问题。但是,接收错误。
所以你需要做的是修复你的代码,要么在发送前将你的象限复制到直 2x2 矩阵中,要么在接收方分配一个 2x4 接收缓冲区来存储发送的消息,然后将相关部分复制到您的 2x2 矩阵中。
下面是使用第二个选项的代码,我选择它是为了说明目的,因为您似乎想使用派生类型。 (注意:我保留了代码风格,尽管这不是我自己使用的风格)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<mpi.h>
//matrix size
#define SIZE 4
double A[SIZE][SIZE] ={
1.0, 2.0, 3.0, 4.0,
5.0, 6.0, 7.0, 8.0,
9.0, 10.0, 11.0, 12.0,
13.0, 14.0, 15.0, 16.0
};
static double B[SIZE/2][SIZE/2];
static double tmpB[SIZE/2][SIZE];
MPI_Datatype QUAD;
#define QUADRANT(Q,y,x) (Q[y * SIZE/2]+(x * SIZE/2))
void printout(double Y[SIZE/2][SIZE/2]){
int i,j;
for(i=0;i< SIZE/2;i++){
for(j=0; j< SIZE/2; j++){
printf("%.0f ",Y[i][j]);
}
printf("\n");
}
}
void compress(double Y[SIZE/2][SIZE/2], double tmpY[SIZE/2][SIZE]){
int i,j;
for(i=0;i< SIZE/2;i++){
for(j=0; j< SIZE/2; j++){
Y[i][j]=tmpY[i][j];
}
}
}
int main(int argc, char** argv){
int rank, size, i, j;
MPI_Init(&argc,&argv);
MPI_Comm_rank(MPI_COMM_WORLD,&rank);
MPI_Comm_size(MPI_COMM_WORLD,&size);
MPI_Status stat;
//Define a MPI datatype, Quadrant
MPI_Type_vector(SIZE/2, SIZE/2, SIZE, MPI_DOUBLE, &QUAD);
MPI_Type_commit(&QUAD);
//master process
if(rank==0){
MPI_Send(QUADRANT(A,0,1),1,QUAD,1,0,MPI_COMM_WORLD);
MPI_Send(QUADRANT(A,1,0),1,QUAD,2,0,MPI_COMM_WORLD);
MPI_Send(QUADRANT(A,1,1),1,QUAD,3,0,MPI_COMM_WORLD);
}else{
MPI_Recv(tmpB,1,QUAD,0,0,MPI_COMM_WORLD,&stat);
compress(B,tmpB);
printout(B);
printf("\n");
}
MPI_Finalize();
}
最后一句话:在现实生活中,如果你要进行这种传输,我建议你在发送之前寻求将数据压缩到象限中的解决方案,以避免额外的潜在无用副本在 MPI 库本身内部(尽管它们是否发生超出了 MPI 标准的范围)
问题是,MPI_Recv
与不符合接收缓冲区布局的相同跨步矢量数据类型一起使用。
例如调用
MPI_Send(QUADRANT(A,0,1),1,QUAD,1,0, MPI_COMM_WORLD);
与 QUAD
的定义一起正确选择 A
右上象限的数据值,并通过网络发送值 3.0、4.0、7.0 和 8.0。
但是,相同的数据类型不能用于接收缓冲区,因为 B
中行的大小,因此步长小于 A
中的行。因此,值 7.0 和 8.0 存储在 B
:
Matrix as seen by MPI_Recv Memory Layout of
with data-type QUAD Matrix B
M[0][0] <-- 3.0 --> B[0][0]
M[0][1] <-- 4.0 --> B[0][1]
M[0][2] B[1][0] <-- unchanged, e.g. 0.0
M[0][3] B[1][1] <-- unchanged, e.g. 0.0
M[1][0] <-- 7.0 --> !beyond array!
M[1][1] <-- 8.0 --> !beyond array!
M[1][2]
M[1][3]
...
EDIT: 符合标准,接收时必须使用相同的类型。因此,接收缓冲区必须这样声明:
double B[SIZE/2][SIZE]; // SIZE elements per row.
之后,可以按照 Gilles 在他的回答中所做的那样压缩数组。