在进程之间传递子矩阵

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 在他的回答中所做的那样压缩数组。