在进程之间传递子矩阵

时间:2015-12-12 21:38:00

标签: c mpi

首先,我正在学习消息传递接口(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)时,每个工作者都可以接收它的相应子矩阵。

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

但是试图获得列矩阵中的所有数字。

2 个答案:

答案 0 :(得分:2)

你的大部分问题是你想要收到的不是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标准的范围)

答案 1 :(得分:2)

问题是,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]
...

编辑:符合标准,收到后必须使用相同的类型。因此,接收缓冲区必须声明如下:

double B[SIZE/2][SIZE]; // SIZE elements per row.

之后,人们可以按照Gilles的回答压缩阵列。