Content

Lösungsvorschlag

Grid Klasse

#ifndef HPC_MPI_GRID_H
#define HPC_MPI_GRID_H 1

#include <mpi.h>

namespace hpc { namespace mpi {

struct Grid
{
    Grid()
    {
        int dims[2]    = {0, 0};
        int periods[2] = {false, false};

        /* create two-dimensional Cartesian grid for our prcesses */
        MPI_Comm_size(MPI_COMM_WORLD, &nof_processes);
        MPI_Dims_create(nof_processes, 2, dims);
        MPI_Cart_create(MPI_COMM_WORLD,
                        2,        // number of dimensions
                        dims,     // actual dimensions
                        periods,  // both dimensions are non-periodical
                        true,     // reorder is permitted
                        &comm     // newly created communication domain
                       );
        MPI_Comm_rank(MPI_COMM_WORLD, &rank); // update rank (could have changed)
        numNodeRows = dims[0];
        numNodeCols = dims[1];

        /* get our position within the grid */
        int coords[2];
        MPI_Cart_coords(comm, rank, 2, coords);
        nodeRow = coords[0];
        nodeCol = coords[1];
    }

    int         numNodeRows, numNodeCols;
    int         nodeRow, nodeCol;
    int         nof_processes, rank;

    MPI_Comm    comm;
};

} } // namespaces mpi, hpc

#endif // HPC_MPI_GRID_H

Klasse für eine verteilte GeMatrix

#ifndef HPC_MPI_GEMATRIX_H
#define HPC_MPI_GEMATRIX_H 1

#include <cassert>
#include <memory>
#include <type_traits>
#include <mpi.h>
#include <hpc/aux/ceildiv.h>
#include <hpc/aux/slices.h>
#include <hpc/matvec/gematrix.h>
#include <hpc/matvec/isgematrix.h>
#include <hpc/matvec/copy.h>
#include <hpc/mpi/fundamental.h>
#include <hpc/mpi/grid.h>

namespace hpc { namespace mpi {

template <typename T, typename I>
struct GeMatrix
{
    typedef T   ElementType;
    typedef I   Index;

    using StorageOrder = hpc::matvec::StorageOrder;

    GeMatrix(Index numRows, Index numCols, Grid grid)
        : numRows(numRows), numCols(numCols), grid(grid),
          buffer(getNumRows(grid.nodeRow), getNumCols(grid.nodeCol))
    {
    }

    Index
    getRowOffset(Index nodeRow) const
    {
        Index mb = (numRows+grid.numNodeRows-1)/grid.numNodeRows;
        return mb*nodeRow;
    }

    Index
    getColOffset(Index nodeCol) const
    {
        Index nb = (numCols+grid.numNodeCols-1)/grid.numNodeCols;
        return nb*nodeCol;
    }

    Index
    getNumRows(Index nodeRow) const
    {
        return numRows - getRowOffset(nodeRow);
    }

    Index
    getNumCols(Index nodeCol) const
    {
        return numCols - getColOffset(nodeCol);
    }

    Index                       numRows, numCols;
    Grid                        grid;
    hpc::matvec::GeMatrix<T,I>  buffer;
};

} } // namespaces mpi, hpc

#endif // HPC_MPI_GEMATRIX_H

Copy Funktion für Scatter und Gather

#ifndef HPC_MPI_COPY_H
#define HPC_MPI_COPY_H 1

#include <cassert>
#include <mpi.h>
#include <hpc/matvec/gematrix.h>
#include <hpc/matvec/isgematrix.h>
#include <hpc/matvec/copy.h>
#include <hpc/mpi/gematrix.h>
#include <hpc/mpi/matrix.h>

namespace hpc { namespace mpi {

// scatter
template <typename T, typename Index>
void
copy(const hpc::matvec::GeMatrix<T, Index> &A, GeMatrix<T, Index> &B)
{
    if (B.grid.rank == 0) {
        MPI_Request requests[B.grid.nof_processes-1];
        int ri = 0;
        for (int i = 0; i < B.grid.nof_processes; ++i) {
            int coords[2];
            MPI_Cart_coords(B.grid.comm, i, 2, coords);
            auto A_ = A(B.getRowOffset(coords[0]), B.getColOffset(coords[1]),
                        B.getNumRows(coords[0]), B.getNumCols(coords[1]));
            if (i == 0) {
                hpc::matvec::copy(A_, B.buffer);
            } else {
                MPI_Isend((void*)&A_(0, 0), 1, get_type(A_),
                          i, 0, B.grid.comm, &requests[ri++]);
            }
        }
        for (auto& request: requests) {
            MPI_Status status;
            MPI_Wait(&request, &status);
        }
    } else {
        MPI_Status status;
        T &B00 = B.buffer(0, 0);
        MPI_Recv((void*)&B00, 1, get_type(B.buffer), 0, 0, B.grid.comm, &status);
    }
}

// gather
template <typename T, typename Index>
void
copy(const GeMatrix<T, Index> &A, hpc::matvec::GeMatrix<T, Index> &B)
{
    if (A.grid.rank == 0) {
        MPI_Request requests[A.grid.nof_processes-1];
        int ri = 0;
        for (int i = 0; i < A.grid.nof_processes; ++i) {
            int coords[2];
            MPI_Cart_coords(A.grid.comm, i, 2, coords);
            auto B_ = B(A.getRowOffset(coords[0]), A.getColOffset(coords[1]),
                        A.getNumRows(coords[0]), A.getNumCols(coords[1]));
            if (i == 0) {
                hpc::matvec::copy(A.buffer, B_);
            } else {
                MPI_Irecv(&B_(0, 0), 1, get_type(B_),
                          i, 0, A.grid.comm, &requests[ri++]);
            }
        }
        for (auto& request: requests) {
            MPI_Status status;
            MPI_Wait(&request, &status);
        }
    } else {
        const T &A00 = A.buffer(0, 0);
        MPI_Send((void *)&A00, 1, get_type(A.buffer), 0, 0, A.grid.comm);
    }
}

} } // namespaces mpi, hpc

#endif // HPC_MPI_COPY_H

Test-Programm

#include <hpc/matvec/print.h>
#include <hpc/mpi/grid.h>
#include <hpc/mpi/gematrix.h>
#include <hpc/mpi/copy.h>
#include <mpi.h>
#include <cstdio>


int
main(int argc, char** argv)
{
    MPI_Init(&argc, &argv);

    typedef std::size_t     Index;

    hpc::mpi::Grid          grid;

    Index numRows = 10;
    Index numCols = 10;

    hpc::matvec::GeMatrix<double, Index>    A(numRows, numCols);
    hpc::matvec::GeMatrix<double, Index>    B(numRows, numCols);
    hpc::mpi::GeMatrix<double, Index>       dA(numRows, numCols, grid);

    if (grid.nodeRow==0 && grid.nodeCol==0) {
        for (Index i=0; i<numRows; ++i) {
            for (Index j=0; j<numCols; ++j) {
                A(i,j) = i + j*numRows;
            }
        }
    }

    if (grid.nodeRow==0 && grid.nodeCol==0) {
        hpc::matvec::print(A, "A");
    }

    // scatter
    hpc::mpi::copy(A, dA);

    // gather
    hpc::mpi::copy(dA, B);

    if (grid.nodeRow==0 && grid.nodeCol==0) {
        hpc::matvec::print(B, "B");
    }


    MPI_Finalize();
}

Ausführen des Tests

$shell> mpic++ -O3 -Wall -std=c++11 -I../.. mpi_gematrix.cc
$shell> mpirun -np 4 ./a.out
A = 
        0.0       10.0       20.0       30.0       40.0       50.0       60.0       70.0       80.0       90.0
        1.0       11.0       21.0       31.0       41.0       51.0       61.0       71.0       81.0       91.0
        2.0       12.0       22.0       32.0       42.0       52.0       62.0       72.0       82.0       92.0
        3.0       13.0       23.0       33.0       43.0       53.0       63.0       73.0       83.0       93.0
        4.0       14.0       24.0       34.0       44.0       54.0       64.0       74.0       84.0       94.0
        5.0       15.0       25.0       35.0       45.0       55.0       65.0       75.0       85.0       95.0
        6.0       16.0       26.0       36.0       46.0       56.0       66.0       76.0       86.0       96.0
        7.0       17.0       27.0       37.0       47.0       57.0       67.0       77.0       87.0       97.0
        8.0       18.0       28.0       38.0       48.0       58.0       68.0       78.0       88.0       98.0
        9.0       19.0       29.0       39.0       49.0       59.0       69.0       79.0       89.0       99.0

B = 
        0.0       10.0       20.0       30.0       40.0       50.0       60.0       70.0       80.0       90.0
        1.0       11.0       21.0       31.0       41.0       51.0       61.0       71.0       81.0       91.0
        2.0       12.0       22.0       32.0       42.0       52.0       62.0       72.0       82.0       92.0
        3.0       13.0       23.0       33.0       43.0       53.0       63.0       73.0       83.0       93.0
        4.0       14.0       24.0       34.0       44.0       54.0       64.0       74.0       84.0       94.0
        5.0       15.0       25.0       35.0       45.0       55.0       65.0       75.0       85.0       95.0
        6.0       16.0       26.0       36.0       46.0       56.0       66.0       76.0       86.0       96.0
        7.0       17.0       27.0       37.0       47.0       57.0       67.0       77.0       87.0       97.0
        8.0       18.0       28.0       38.0       48.0       58.0       68.0       78.0       88.0       98.0
        9.0       19.0       29.0       39.0       49.0       59.0       69.0       79.0       89.0       99.0
$shell>