如何使用MPI_Scatterv将矩阵行发送到所有进程?

时间:2014-08-31 23:42:00

标签: c matrix mpi

我正在使用MPI界面。我想分割矩阵(按行)并在每个过程中分配部分。

例如,我有这个7x7方阵M。

M = [
    0.00    1.00    2.00    3.00    4.00    5.00    6.00    
    7.00    8.00    9.00    10.00   11.00   12.00   13.00
    14.00   15.00   16.00   17.00   18.00   19.00   20.00
    21.00   22.00   23.00   24.00   25.00   26.00   27.00
    28.00   29.00   30.00   31.00   32.00   33.00   34.00
    35.00   36.00   37.00   38.00   39.00   40.00   41.00
    42.00   43.00   44.00   45.00   46.00   47.00   48.00
];

我有3个进程,因此拆分可能是:

  • 进程0获取行0,1
  • 流程1获取第2,3,4行
  • 流程2获取第5,6行

在Scatterv之后,它应该如下所示:

Process 0:
M0 = [
    0.00    1.00    2.00    3.00    4.00    5.00    6.00    
    7.00    8.00    9.00    10.00   11.00   12.00   13.00
];

Process 1:
M1 = [
    14.00   15.00   16.00   17.00   18.00   19.00   20.00
    21.00   22.00   23.00   24.00   25.00   26.00   27.00
    28.00   29.00   30.00   31.00   32.00   33.00   34.00
];

Process 2:
M2 = [
    35.00   36.00   37.00   38.00   39.00   40.00   41.00
    42.00   43.00   44.00   45.00   46.00   47.00   48.00
];

我认为我对自己想要实现的目标非常清楚。随意问我是否解释不好。

现在,我告诉你我的代码:

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

#define BLOCK_LOW(id,p,n) ((id)*(n)/(p))
#define BLOCK_HIGH(id,p,n) ((id+1)*(n)/(p) - 1)
#define BLOCK_SIZE(id,p,n) ((id+1)*(n)/(p) - (id)*(n)/(p))
#define BLOCK_OWNER(index,p,n) (((p)*((index)+1)-1)/(n))

void **matrix_create(size_t m, size_t n, size_t size) {
   size_t i; 
   void **p= (void **) malloc(m*n*size+ m*sizeof(void *));
   char *c=  (char*) (p+m);
   for(i=0; i<m; ++i)
      p[i]= (void *) c+i*n*size;
   return p;
}

void matrix_print(double **M, size_t m, size_t n, char *name) {
    size_t i,j;
    printf("%s=[",name);
    for(i=0; i<m; ++i) {
        printf("\n  ");
        for(j=0; j<n; ++j)
            printf("%f  ",M[i][j]);
    }
    printf("\n];\n");
}

main(int argc, char *argv[]) {

    int npes, myrank, root = 0, n = 7, rows, i, j, *sendcounts, *displs;
    double **m, **mParts;

    MPI_Status status;
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD,&npes);
    MPI_Comm_rank(MPI_COMM_WORLD,&myrank);

    // Matrix M is generated in the root process (process 0)
    if (myrank == root) {
        m = (double**)matrix_create(n, n, sizeof(double));
        for (i = 0; i < n; ++i)
            for (j = 0; j < n; ++j)
                m[i][j] = (double)(n * i + j);
    }

    // Array containing the numbers of rows for each process
    sendcounts = malloc(n * sizeof(int));
    // Array containing the displacement for each data chunk
    displs = malloc(n * sizeof(int));
    // For each process ...
    for (j = 0; j < npes; j++) {
        // Sets each number of rows
        sendcounts[j] = BLOCK_SIZE(j, npes, n);
        // Sets each displacement
        displs[j] = BLOCK_LOW(j, npes, n);
    }
    // Each process gets the number of rows that he is going to get
    rows = sendcounts[myrank];
    // Creates the empty matrixes for the parts of M
    mParts = (double**)matrix_create(rows, n, sizeof(double));
    // Scatters the matrix parts through all the processes
    MPI_Scatterv(m, sendcounts, displs, MPI_DOUBLE, mParts, rows, MPI_DOUBLE, root, MPI_COMM_WORLD);

    // This is where I get the Segmentation Fault
    if (myrank == 1) matrix_print(mParts, rows, n, "mParts");

    MPI_Finalize();
}

当我尝试读取分散的数据时,我收到了分段错误,表明分散操作不起作用。我已经使用一维数组做到了这一点并且有效。但是对于二维数组,事情变得有点棘手。

你能帮我找到这个错误吗?

由于

1 个答案:

答案 0 :(得分:2)

MPI_Scatterv需要指向数据的指针,数据应该在内存中连续。你的程序在第二部分没问题,但MPI_Scatterv收到指向数据指针的指针。因此改变它是一件好事:

MPI_Scatterv(&m[0][0], sendcounts, displs, MPI_DOUBLE, &mParts[0][0], sendcounts[myrank], MPI_DOUBLE, root, MPI_COMM_WORLD);

sendcountsdispls还有一些要改变的地方:要转到2D,这些计数应乘以nMPI_Scatterv中的接收次数不再是rows,而是sendcouts[myrank]

以下是最终代码:

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>

#define BLOCK_LOW(id,p,n) ((id)*(n)/(p))
#define BLOCK_HIGH(id,p,n) ((id+1)*(n)/(p) - 1)
#define BLOCK_SIZE(id,p,n) ((id+1)*(n)/(p) - (id)*(n)/(p))
#define BLOCK_OWNER(index,p,n) (((p)*((index)+1)-1)/(n))

void **matrix_create(size_t m, size_t n, size_t size) {
    size_t i; 
    void **p= (void **) malloc(m*n*size+ m*sizeof(void *));
    char *c=  (char*) (p+m);
    for(i=0; i<m; ++i)
        p[i]= (void *) c+i*n*size;
    return p;
}

void matrix_print(double **M, size_t m, size_t n, char *name) {
    size_t i,j;
    printf("%s=[",name);
    for(i=0; i<m; ++i) {
        printf("\n  ");
        for(j=0; j<n; ++j)
            printf("%f  ",M[i][j]);
    }
    printf("\n];\n");
}

main(int argc, char *argv[]) {

    int npes, myrank, root = 0, n = 7, rows, i, j, *sendcounts, *displs;
    double **m, **mParts;

    MPI_Status status;
    MPI_Init(&argc, &argv);
    MPI_Comm_size(MPI_COMM_WORLD,&npes);
    MPI_Comm_rank(MPI_COMM_WORLD,&myrank);

    // Matrix M is generated in the root process (process 0)
    if (myrank == root) {
        m = (double**)matrix_create(n, n, sizeof(double));
        for (i = 0; i < n; ++i)
            for (j = 0; j < n; ++j)
                m[i][j] = (double)(n * i + j);
    }

    // Array containing the numbers of rows for each process
    sendcounts = malloc(n * sizeof(int));
    // Array containing the displacement for each data chunk
    displs = malloc(n * sizeof(int));
    // For each process ...
    for (j = 0; j < npes; j++) {
        // Sets each number of rows
        sendcounts[j] = BLOCK_SIZE(j, npes, n)*n;
        // Sets each displacement
        displs[j] = BLOCK_LOW(j, npes, n)*n;
    }
    // Each process gets the number of rows that he is going to get
    rows = sendcounts[myrank]/n;
    // Creates the empty matrixes for the parts of M
    mParts = (double**)matrix_create(rows, n, sizeof(double));
    // Scatters the matrix parts through all the processes
    MPI_Scatterv(&m[0][0], sendcounts, displs, MPI_DOUBLE, &mParts[0][0], sendcounts[myrank], MPI_DOUBLE, root, MPI_COMM_WORLD);

    // This is where I get the Segmentation Fault
    if (myrank == 1) matrix_print(mParts, rows, n, "mParts");

    MPI_Finalize();
}

如果您想了解有关2D数组和MPI的更多信息,请查看here

另请参阅PETSc库herethere的DMDA结构