diff --git a/game-of-life/CMakeLists.txt b/game-of-life/CMakeLists.txt index ba5d195..cd8b3f7 100644 --- a/game-of-life/CMakeLists.txt +++ b/game-of-life/CMakeLists.txt @@ -1,18 +1,18 @@ project(GOL) cmake_minimum_required(VERSION 2.8) find_package(MPI REQUIRED) set(CMAKE_CXX_FLAGS "-Wall -Werror -Wextra -pedantic") set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) set(CMAKE_CXX_STANDARD 14) find_package(Qt5Network) -add_executable(gol game-of-life.cc client.cc client.hh grid.hh) +add_executable(gol game-of-life.cc client.cc client.hh grid.hh grid.cc simu.hh simu_gol.hh) target_include_directories(gol PRIVATE ${MPI_CXX_INCLUDE_PATH}) target_link_libraries(gol ${MPI_CXX_LIBRARIES} Qt5::Network) add_subdirectory(gol_gui) diff --git a/game-of-life/client.cc b/game-of-life/client.cc index 8e0765a..4bc3018 100644 --- a/game-of-life/client.cc +++ b/game-of-life/client.cc @@ -1,123 +1,87 @@ #include "client.hh" #include <QByteArray> #include <QDataStream> #include <QTcpSocket> #include <iostream> -// static auto error_handler = [](auto var, const std::string &message) -> void -// { -// if (var < 0) { -// std::cerr << message << " -- " << std::strerror(errno) << std::endl; -// std::exit(1); -// } -// }; enum { READ_IMAGE = -1, READ_IMAGE_WAIT = -2, WAIT = -3, CONFIGURE = -4, }; /* -------------------------------------------------------------------------- */ -GridSender::GridSender(const Grid & grid, int prank, int psize, - const std::string & server_name, int port) - : _grid(grid), prank(prank) { - // std::stringstream sstr; - // sstr << port; - // struct addrinfo hints; - // struct addrinfo *servinfo; - // memset(&hints, 0, sizeof hints); - // hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6 - // hints.ai_socktype = SOCK_STREAM; // TCP stream sockets +GridSender::GridSender(const Grid & grid) : _grid(grid), prank(0), psize(0) {} - // error_handler( - // getaddrinfo(server_name.c_str(), sstr.str().c_str(), &hints, - // &servinfo), - // "Could not retrieve server info"); - - // error_handler(sockfd = socket(servinfo->ai_family, servinfo->ai_socktype, - // servinfo->ai_protocol), - // "Could not open a socket"); - - // error_handler(connect(sockfd, servinfo->ai_addr, servinfo->ai_addrlen), - // "Could not connect to server"); - // freeaddrinfo(servinfo); +void GridSender::setParallelContext(int prank, int psize) { + this->prank = prank; + this->psize = psize; +} +void GridSender::connect(const std::string & server_name, int port) { socket = new QTcpSocket(); qDebug() << prank << "Connecting to" << server_name.c_str(); socket->connectToHost(QString(server_name.c_str()), port); socket->waitForConnected(); qDebug() << prank << "Connected"; char command = CONFIGURE; socket->putChar(command); QByteArray data; QDataStream buffer(&data, QIODevice::WriteOnly); buffer << prank; buffer << psize; - buffer << int(grid.size(0)); - buffer << int(grid.size(1)); - buffer << int(grid.offset(0)); - buffer << int(grid.offset(1)); - buffer << int(grid.ghost()); + buffer << int(_grid.size(0)); + buffer << int(_grid.size(1)); + buffer << int(_grid.globalSize(0)); + buffer << int(_grid.globalSize(1)); + buffer << int(_grid.offset(0)); + buffer << int(_grid.offset(1)); + buffer << int(_grid.ghost()); socket->write(data); qDebug() << prank << "Waiting for connection confirmation..."; socket->waitForReadyRead(); socket->getChar(&command); qDebug() << prank << "command" << int(command) << "ok!"; - - // error_handler(::send(sockfd, reinterpret_cast<char *>(buffer), 7 * - // sizeof(int), 0), - // "Could not send the grid"); - - // delete [] buffer; } /* -------------------------------------------------------------------------- */ GridSender::~GridSender() { socket->disconnectFromHost(); socket->waitForDisconnected(); } /* -------------------------------------------------------------------------- */ void GridSender::send(bool wait) { char command = wait ? READ_IMAGE_WAIT : READ_IMAGE; socket->putChar(command); - // error_handler(::send(sockfd, &command, 1, 0), - // "Could not send the grid"); + qDebug() << prank << "Sending" << _grid.fullSize() << "chars"; socket->write(_grid.data(), _grid.fullSize()); - // error_handler(::send(sockfd, _grid.data(), _grid.fullSize(), 0), - // "Could not send the grid"); + socket->waitForBytesWritten(); if (wait) { qDebug() << prank << "Waiting..."; - if(socket->waitForReadyRead()) { + if (socket->waitForReadyRead()) { socket->getChar(&command); - // error_handler(::recv(sockfd, &command, 1, MSG_WAITALL), - // "Could not recv wait message"); qDebug() << prank << "command" << int(command) << "ok!"; } else { - qFatal("Confirmation timeouted!!!"); + qFatal("Confirmation timeout!!!"); } } } void GridSender::wait() { char command = WAIT; socket->putChar(command); - qDebug() << prank << "Sending wait" << command; + qDebug() << prank << "Sending wait" << int(command); socket->waitForReadyRead(); socket->getChar(&command); qDebug() << prank << "command" << int(command) << "ok!"; - // error_handler(::send(sockfd, &command, 1, MSG_WAITALL), - // "Could not recv wait message"); - - // error_handler(::recv(sockfd, &command, 1, MSG_WAITALL), - // "Could not recv wait message"); } diff --git a/game-of-life/client.hh b/game-of-life/client.hh index 071fe38..7ba3250 100644 --- a/game-of-life/client.hh +++ b/game-of-life/client.hh @@ -1,20 +1,21 @@ #include "grid.hh" #include <string> class QTcpSocket; class GridSender { public: - GridSender(const Grid &grid, int prank, int psize, const std::string &server, - int port); + GridSender(const Grid & grid); ~GridSender(); + void setParallelContext(int prank, int psize); + void connect(const std::string & server, int port); void send(bool wait = true); void wait(); private: - const Grid &_grid; - int prank; + const Grid & _grid; + int prank, psize; QTcpSocket * socket; }; diff --git a/game-of-life/game-of-life.cc b/game-of-life/game-of-life.cc index 6ca31e4..ef38adc 100644 --- a/game-of-life/game-of-life.cc +++ b/game-of-life/game-of-life.cc @@ -1,77 +1,25 @@ -#include "grid.hh" -#include "client.hh" +#include "simu_gol.hh" #include <mpi.h> #include <iostream> int main(int argc, char **argv) { MPI_Init(&argc, &argv); - int nproc, rank; - MPI_Comm_size(MPI_COMM_WORLD, &nproc); - MPI_Comm_rank(MPI_COMM_WORLD, &rank); + long int dims[2] = {216, 384}; - int nprocs[2] = {0, 0}; - MPI_Dims_create(nproc, 2, nprocs); + GOL simu(dims, 1, true, MPI_COMM_WORLD, "127.0.0.1"); - int periods[2] = {true, true}; - MPI_Comm cart_comm; - MPI_Cart_create(MPI_COMM_WORLD, 2, nprocs, periods, true, &cart_comm); - - std::size_t dims[2] = {120, 160}; - std::size_t local_dims[2]; - std::size_t offsets[2] = {0, 0}; - - for (std::size_t i = 0; i < 2; ++i) { - int mod = dims[i] % nprocs[i]; - - local_dims[i] = dims[i] / nprocs[i]; - offsets[i] = local_dims[i] * rank + (rank < mod ? rank : mod); - - local_dims[i] += (rank < mod ? 1 : 0); + simu.initialState(); + while(true) { + simu.send(); + simu.determineCells(); + simu.swap(); } - - Grid grid(local_dims[0], local_dims[1], 1); - grid.setOffsets(offsets); - - Grid old_grid(grid); - - GridSender sender(old_grid, rank, nproc, "127.0.0.1", 4321); - - for (std::size_t i = 0; i < grid.size(0); i++) { - old_grid(i, grid.size(1)/2) = 1; - } - sender.send(); - - std::cout << "Grid " <<old_grid.size() << " -- [" << old_grid.size(0) << ", " - << old_grid.size(1) << "]" << std::endl; - - for (std::size_t k = 0; k < 100; k++) { - // for (std::size_t i = 0; i < grid.size(0); i++) { - // for (std::size_t j = 0; j < grid.size(1); j++) { - // double neighbours = -old_grid(i, j); - // for (char v = -1; v < 1; ++v) - // for (char w = -1; w < 1; ++w) - // neighbours += old_grid(i + v, j + w); - - // if (neighbours < 2) - // grid(i, j) = 0; - // if (neighbours == 2) - // grid(i, j) = old_grid(i, j); - // if (neighbours == 3) - // grid(i, j) = 1; - // if (neighbours > 3) - // grid(i, j) = 0; - // } - // } - - // grid.swap(old_grid); - sender.send(); - } - - sender.wait(); + simu.send(); + simu.getSender().wait(); MPI_Finalize(); return 0; } diff --git a/game-of-life/gol_gui/data_thread.cc b/game-of-life/gol_gui/data_thread.cc index 15c4983..604a343 100644 --- a/game-of-life/gol_gui/data_thread.cc +++ b/game-of-life/gol_gui/data_thread.cc @@ -1,139 +1,154 @@ #include "data_thread.hh" #include "grid.hh" #include <QDataStream> #include <QHostAddress> +#include <QTcpSocket> #include <QThread> #include <iostream> DataThread::DataThread(int socket_desc, Grid & grid, QObject * parent) - : QThread(parent), socket(parent), socket_desc(socket_desc), grid(grid), partial_read(0), i(0), j(0) { - connect(&socket, SIGNAL(readyRead()), this, SLOT(readFrame())); - connect(&socket, SIGNAL(disconnected()), this, SLOT(quit())); - - if (!socket.setSocketDescriptor(socket_desc)) { - emit error(socket.error()); - return; - } -} + : QThread(parent), socket(nullptr), socket_desc(socket_desc), grid(grid), + partial_read(0), i(0), j(0) {} DataThread::~DataThread() { qDebug() << "DataThread " << prank << ": sayonara"; - socket.abort(); } void DataThread::run() { + socket = new QTcpSocket; + + if (!socket->setSocketDescriptor(socket_desc)) { + emit error(socket->error()); + return; + } + + connect(socket, SIGNAL(readyRead()), currentThread(), SLOT(readFrame()), + Qt::DirectConnection); + connect(socket, SIGNAL(disconnected()), currentThread(), SLOT(quit()), + Qt::DirectConnection); + qDebug() << "Ready to roll" << currentThreadId(); exec(); + + socket->disconnectFromHost(); + grid.unconnectPiece(); } enum { READ_IMAGE = -1, READ_IMAGE_WAIT = -2, WAIT = -3, CONFIGURE = -4, }; void DataThread::readFrame() { - qDebug() << "Readable data" << socket.bytesAvailable(); + qDebug() << "DataThread" << currentThreadId() << "Readable data" + << socket->bytesAvailable(); - if(partial_read == 0) { - QByteArray data = socket.read(1); + if (partial_read == 0) { + QByteArray data = socket->read(1); command = data[0]; qDebug() << "DataThread" << currentThreadId() << "Got command" << int(command); } switch (command) { case READ_IMAGE: case READ_IMAGE_WAIT: { - int max_size = (size[0] + 2 * ghost) * (size[1] + 2 * ghost); + int max_size = (lsize[0] + 2 * ghost) * (lsize[1] + 2 * ghost); int buffer_size = max_size - partial_read; qDebug() << "DataThread" << currentThreadId() << "Receiving" << buffer_size << "chars"; - QByteArray data = socket.read(buffer_size); + QByteArray data = socket->read(buffer_size); qDebug() << "DataThread" << currentThreadId() << "Received" << data.size() << "chars"; QDataStream buffer(&data, QIODevice::ReadOnly); // buffer.setByteOrder(QDataStream::LittleEndian); if (partial_read == 0) { qDebug() << "DataThread" << currentThreadId() << ": getting a piece..."; grid.getPiece(); i = 0; j = 0; qDebug() << "DataThread" << currentThreadId() << ": got a piece"; } - for(int d = partial_read; d < partial_read + data.size(); ++d) { + for (int d = 0; d < data.size(); ++d) { quint8 val; buffer >> val; - bool is_ghost = i < ghost || j < ghost || i >= (size[0] + ghost) || - j >= (size[1] + ghost); - if (!is_ghost) - grid(i - ghost, j - ghost) = val * (prank + 1); - + bool is_ghost = i < ghost || j < ghost || i >= (lsize[0] + ghost) || + j >= (lsize[1] + ghost); + if (!is_ghost) { + grid(i - ghost + offset[0], j - ghost + offset[1]) = val * (prank + 1); + } j += 1; - if (j == (size[1] + 2*ghost)) { + if (j == (lsize[1] + 2 * ghost)) { i += 1; j = 0; } } partial_read += data.size(); - if(partial_read == max_size) { + if (partial_read == max_size) { qDebug() << "DataThread" << currentThreadId() << prank << ": data ready"; partial_read = 0; + i = 0; + j = 0; emit dataReady(); - if(command == READ_IMAGE_WAIT) { - qDebug() << "DataThread" << currentThreadId() << "Unlock client" << int(command); - socket.putChar(command); + if (command == READ_IMAGE_WAIT) { + qDebug() << "DataThread" << currentThreadId() << "Unlock client" + << int(command); + socket->putChar(command); } } break; } - case WAIT : { + case WAIT: { qDebug() << "DataThread" << currentThreadId() << "Unlock client"; - socket.putChar(command); + socket->putChar(command); break; } case CONFIGURE: { - QByteArray data = socket.read(7 * sizeof(int)); + QByteArray data = socket->read(9 * sizeof(int)); QDataStream stream(&data, QIODevice::ReadOnly); // stream.setByteOrder(QDataStream::LittleEndian); stream >> prank; stream >> psize; - stream >> size[0]; - stream >> size[1]; + stream >> lsize[0]; + stream >> lsize[1]; + stream >> gsize[0]; + stream >> gsize[1]; stream >> offset[0]; stream >> offset[1]; stream >> ghost; if (prank == 0) { - grid.resize(size[0], size[1]); + grid.resize(gsize[0], gsize[1]); grid.setPiece(psize); grid.releasePieces(); - qDebug() << "DataThread" << currentThreadId() << "Grid has" - << grid.getNbPieces() << "piece(s)"; + qDebug() << "DataThread" << currentThreadId() << "Grid (" << gsize[0] + << "x" << gsize[1] << ") has" << grid.getNbPieces() + << "piece(s)"; } qDebug() << "DataThread" << currentThreadId() << ":" << prank << "/" - << psize << "-" << socket.peerAddress().toString() - << "- local grid size" << size[0] << "x" << size[1] << "+" + << psize << "-" << socket->peerAddress().toString() + << "- local grid size" << lsize[0] << "x" << lsize[1] << "+" << offset[0] << "-" << offset[1]; qDebug() << "DataThread" << currentThreadId() << "Unlock client"; - socket.putChar(command); + socket->putChar(command); break; } default: qDebug() << "DataThread" << currentThreadId() << "Unknown command" << int(command); return; } } diff --git a/game-of-life/gol_gui/data_thread.hh b/game-of-life/gol_gui/data_thread.hh index 7d5c69c..bce2499 100644 --- a/game-of-life/gol_gui/data_thread.hh +++ b/game-of-life/gol_gui/data_thread.hh @@ -1,41 +1,42 @@ #ifndef DATA_THREAD_H #define DATA_THREAD_H #include <QThread> #include <QTcpSocket> class Grid; class DataThread : public QThread { Q_OBJECT public: DataThread(int socket_desc, Grid & grid, QObject *parent); ~DataThread(); void run() Q_DECL_OVERRIDE; signals: void error(QTcpSocket::SocketError socketError); void dataReady(); protected slots: void readFrame(); private: - QTcpSocket socket; + QTcpSocket * socket; int socket_desc; Grid & grid; int prank, psize; - int size[2]; + int lsize[2]; + int gsize[2]; int offset[2]; int ghost; bool lock_write; int partial_read; int i; int j; char command; }; #endif /* DATA_THREAD_H */ diff --git a/game-of-life/gol_gui/grid.hh b/game-of-life/gol_gui/grid.hh index c1ffd4b..bbe4d5d 100644 --- a/game-of-life/gol_gui/grid.hh +++ b/game-of-life/gol_gui/grid.hh @@ -1,59 +1,54 @@ #ifndef GRID_HH #define GRID_HH #include <QImage> #include <QSemaphore> +#include <QMutex> +#include <QMutexLocker> #include <cstddef> #include <vector> -// class Pixel { -// public: -// Pixel(QImage & data, int x, int y) : data(data), x(x), y(y) {} - -// operator uint32_t () { return data.pixelIndex(x, y); } - -// Pixel & operator=(uint32_t i) { data.setPixel(x, y, i); return *this; } - -// private: -// QImage & data; -// int x, y; -// }; - class Grid { typedef uint32_t T; public: Grid(size_t m = 0, size_t n = 0) - : pieces(0), nb_pieces(0), data(n*m), - _size{m, n} { + : pieces(0), nb_pieces(0), data(n * m), _size{m, n} { for (size_t i = 0; i < n; ++i) { - data[(m / 2)*n + i] = 1; + data[(m / 2) * n + i] = 1; } } void resize(size_t m, size_t n) { _size[0] = m; _size[1] = n; - data.resize(n*m); + data.resize(n * m); } T & operator()(size_t i, size_t j) { return data[j + i * _size[1]]; } const T & operator()(size_t i, size_t j) const { return data[j + i * _size[1]]; } size_t getNbPieces() const { return nb_pieces; } size_t size(int i) const { return _size[i]; } void getPiece() { return pieces.acquire(1); } void setPiece(size_t nb_pieces) { this->nb_pieces = nb_pieces; } void releasePieces() { pieces.release(nb_pieces); } + void unconnectPiece() { + QMutexLocker locker(&_nb_pieces_lock); + --nb_pieces; + if(nb_pieces == 0) releasePieces(); + } private: QSemaphore pieces; size_t nb_pieces; std::vector<T> data; size_t _size[2]; + + QMutex _nb_pieces_lock; }; #endif /* GRID_HH */ diff --git a/game-of-life/gol_gui/helper.cpp b/game-of-life/gol_gui/helper.cpp index 0a8a2b0..9451c86 100644 --- a/game-of-life/gol_gui/helper.cpp +++ b/game-of-life/gol_gui/helper.cpp @@ -1,73 +1,73 @@ #include "helper.hh" -#include <QImage> #include <QBrush> +#include <QColor> #include <QDebug> +#include <QImage> #include <QPaintEvent> #include <QPainter> #include <QWidget> +#include <chrono> + +#include <unistd.h> + +//#define DELAY 50000us + Helper::Helper() { - // QLinearGradient gradient(QPointF(50, -20), QPointF(80, 20)); - // gradient.setColorAt(0.0, Qt::white); - // gradient.setColorAt(1.0, QColor(0xa6, 0xce, 0x39)); - borderPen = QPen(Qt::black); - borderPen.setWidth(1); - deadBrush = QBrush(QColor(64, 32, 64)); - cellBrush = QBrush(Qt::white); + border_pen = QPen(Qt::black); + border_pen.setWidth(1); + + cell_brushes.push_back(QBrush(QColor("darkslateblue"))); + cell_brushes.push_back(QBrush(Qt::white )); + cell_brushes.push_back(QBrush(Qt::cyan )); + cell_brushes.push_back(QBrush(Qt::green )); + cell_brushes.push_back(QBrush(Qt::red )); + cell_brushes.push_back(QBrush(Qt::yellow )); + cell_brushes.push_back(QBrush(Qt::blue )); + cell_brushes.push_back(QBrush(Qt::magenta)); + cell_brushes.push_back(QBrush(QColor("orange"))); } void Helper::paint(QPainter * painter, QPaintEvent * event, __attribute__((unused)) int elapsed) { +#if defined(DELAY) + static auto last_call = std::chrono::high_resolution_clock::now(); + + auto now = std::chrono::high_resolution_clock::now(); + using namespace std::chrono_literals; + std::chrono::duration<double, std::micro> diff = now - last_call; + if(diff < DELAY) { + qDebug() << "wait" << (100ms - diff).count() << "us"; + usleep((DELAY - diff).count()); + } +#endif + qDebug() << "Drawing image" << grid.size(1) << "x" << grid.size(0); - painter->fillRect(event->rect(), deadBrush); + painter->fillRect(event->rect(), cell_brushes[0]); if (grid.size(1) != 0 && grid.size(0) != 0) { - QImage bitmap(5 * grid.size(1), 5 * grid.size(0), QImage::Format_RGB32); - QPainter painting(&bitmap); - bitmap.fill(deadBrush.color()); + // QImage bitmap(5 * grid.size(1) + 1, 5 * grid.size(0) + 1, + // QImage::Format_RGB32); + QImage bitmap(5 * grid.size(1), 5 * grid.size(0), + QImage::Format_RGB32); - painting.setPen(borderPen); - - for (size_t i = 0; i < grid.size(0); ++i) { - painting.drawLine(0, i * 5, bitmap.width(), i * 5); - } - for (size_t j = 0; j < grid.size(1); ++j) { - painting.drawLine(j * 5, 0, j * 5, bitmap.height()); - } + QPainter painting(&bitmap); + //painting.setPen(border_pen); - painting.setBrush(cellBrush); for (size_t i = 0; i < grid.size(0); ++i) { for (size_t j = 0; j < grid.size(1); ++j) { - if (grid(i, j) != 0) { + painting.setBrush(cell_brushes[grid(i, j)]); painting.drawRect(5 * j, 5 * i, 5, 5); - } } } - // painter->translate(100, 100); - // painter->save(); - // painter->setBrush(circleBrush); - // painter->setPen(circlePen); - // painter->rotate(elapsed * 0.030); - - // qreal r = elapsed / 1000.0; - // int n = 30; - // for (int i = 0; i < n; ++i) { - // painter->rotate(30); - // qreal factor = (i + r) / n; - // qreal radius = 0 + 120.0 * factor; - // qreal circleRadius = 1 + factor * 20; - // painter->drawEllipse( - // QRectF(radius, -circleRadius, circleRadius * 2, circleRadius * 2)); - // } - // painter->restore(); - - // painter->setPen(textPen); - // painter->setFont(textFont); - // painter->drawText(QRect(-50, -50, 100, 100), Qt::AlignCenter, - // QStringLiteral("Qt")); painter->drawImage(event->rect(), bitmap, bitmap.rect()); } + +#if defined(DELAY) + last_call = std::chrono::system_clock::now(); +#endif + grid.releasePieces(); qDebug() << "Image drawn"; } diff --git a/game-of-life/gol_gui/helper.hh b/game-of-life/gol_gui/helper.hh index 910e7e9..95c4996 100644 --- a/game-of-life/gol_gui/helper.hh +++ b/game-of-life/gol_gui/helper.hh @@ -1,27 +1,27 @@ #ifndef HELPER_H #define HELPER_H #include "grid.hh" #include <QBrush> #include <QFont> #include <QPen> #include <QWidget> class Helper { public: Helper(); public: void paint(QPainter *painter, QPaintEvent *event, int elapsed); Grid & getGrid() { return grid; } private: QBrush background; - QBrush deadBrush, cellBrush; - QPen borderPen; + std::vector<QBrush> cell_brushes; + QPen border_pen; Grid grid; }; #endif diff --git a/game-of-life/gol_gui/window.cpp b/game-of-life/gol_gui/window.cpp index f27023d..a6b7d70 100644 --- a/game-of-life/gol_gui/window.cpp +++ b/game-of-life/gol_gui/window.cpp @@ -1,58 +1,58 @@ #include "window.hh" #include "gl_widget.hh" #include <QGridLayout> #include <QHostAddress> #include <QLabel> #include <QMessageBox> #include <QSocketNotifier> #include <iostream> #include <sys/socket.h> #include <unistd.h> int Window::sigabort_fd[2]; Window::Window() : server(helper.getGrid(), this) { setWindowTitle(tr("Game of Life")); - // setWindowFlags(Qt::Window | Qt::FramelessWindowHint); + setWindowFlags(Qt::Window | Qt::FramelessWindowHint); GLWidget * openGL = new GLWidget(&helper, this); QGridLayout * layout = new QGridLayout; layout->addWidget(openGL, 0, 0); setLayout(layout); if (!server.listen(QHostAddress::Any, 4321)) { QMessageBox::critical( this, tr("GOL Server"), tr("Unable to start the server: %1.").arg(server.errorString())); close(); return; } qInfo() << "Server listening on " << server.serverAddress().toString() << ":" << server.serverPort(); connect(&server, SIGNAL(dataReady()), openGL, SLOT(animate())); if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigabort_fd)) qFatal("Couldn't create ABORT socketpair"); sn_abort = new QSocketNotifier(sigabort_fd[1], QSocketNotifier::Read, this); connect(sn_abort, SIGNAL(activated(int)), this, SLOT(handleSigAbort())); } void Window::abortSignalHandler(int) { qCritical() << "Aborting"; char a = 1; ::write(sigabort_fd[0], &a, sizeof(a)); } void Window::handleSigAbort() { sn_abort->setEnabled(false); char tmp; ::read(sigabort_fd[1], &tmp, sizeof(tmp)); qDebug() << "Window: Abort the mission"; server.stop(); sn_abort->setEnabled(true); } diff --git a/game-of-life/grid.cc b/game-of-life/grid.cc new file mode 100644 index 0000000..d45997f --- /dev/null +++ b/game-of-life/grid.cc @@ -0,0 +1,137 @@ +#include "grid.hh" + +#include <mpi.h> + +typedef enum { + _north, + _south, + _east, + _west, + _north_east, + _north_west, + _south_east, + _south_west +} comm_tag_t; + +Grid::~Grid() { + delete[] _grid; + MPI_Type_free(&_column_t); + MPI_Type_free(&_line_t); + + MPI_Request * req = _requests; + for (int i = 0; i < 8 * 2; ++i, ++req) { + MPI_Request_free(req); + } + + delete [] _requests; +} + +void Grid::initalizeCommunicationRequests(MPI_Comm & comm, int coords[2]) { + _requests = new MPI_Request[8 * 2]; + + MPI_Type_vector(_dims[0], 1, _dims[1] + 2 * _ghosts, MPI_CHAR, &_column_t); + MPI_Type_vector(_dims[1], 1, 1, MPI_CHAR, &_line_t); + MPI_Type_commit(&_column_t); + MPI_Type_commit(&_line_t); + + MPI_Request * srequests = _requests; + MPI_Request * rrequests = _requests + 8; + { /* south east -> north west */ + int nrank; + int rcoords[2] = {coords[0] - 1, coords[1] - 1}; + MPI_Cart_rank(comm, rcoords, &nrank); + + char & send = operator()(0, 0); + char & recv = operator()(-_ghosts, -_ghosts); + MPI_Send_init(&send, 1, MPI_CHAR, nrank, _south_east, comm, srequests); + MPI_Recv_init(&recv, 1, MPI_CHAR, nrank, _north_west, comm, rrequests); + ++srequests; + ++rrequests; + } + + { /* south west -> north east */ + int nrank; + int rcoords[2] = {coords[0] - 1, coords[1] + 1}; + MPI_Cart_rank(comm, rcoords, &nrank); + char & send = operator()(0, _dims[1] - 1); + char & recv = operator()(-_ghosts, _dims[1] - 1 + _ghosts); + MPI_Send_init(&send, 1, MPI_CHAR, nrank, _south_west, comm, srequests); + MPI_Recv_init(&recv, 1, MPI_CHAR, nrank, _north_east, comm, rrequests); + ++srequests; + ++rrequests; + } + + { /* north west -> south east */ + int nrank; + int rcoords[2] = {coords[0] + 1, coords[1] + 1}; + MPI_Cart_rank(comm, rcoords, &nrank); + char & send = operator()(_dims[1] - 1 , _dims[1] - 1); + char & recv = operator()(_dims[1] - 1 + _ghosts, _dims[1] - 1 + _ghosts); + MPI_Send_init(&send, 1, MPI_CHAR, nrank, _north_west, comm, srequests); + MPI_Recv_init(&recv, 1, MPI_CHAR, nrank, _south_east, comm, rrequests); + ++srequests; + ++rrequests; + } + + { /* north_east -> south_west */ + int nrank; + int rcoords[2] = {coords[0] + 1, coords[1] - 1}; + MPI_Cart_rank(comm, rcoords, &nrank); + char & send = operator()(_dims[1] - 1 , 0); + char & recv = operator()(_dims[1] - 1 + _ghosts, -_ghosts); + MPI_Send_init(&send, 1, MPI_CHAR, nrank, _north_east, comm, srequests); + MPI_Recv_init(&recv, 1, MPI_CHAR, nrank, _south_west, comm, rrequests); + ++srequests; + ++rrequests; + } + + /* Sides */ + { /* south -> north */ + int nrank; + int rcoords[2] = {coords[0] - 1, coords[1]}; + MPI_Cart_rank(comm, rcoords, &nrank); + char & send = operator()( 0, 0); + char & recv = operator()(-_ghosts, 0); + MPI_Send_init(&send, 1, _line_t, nrank, _north, comm, srequests); + MPI_Recv_init(&recv, 1, _line_t, nrank, _south, comm, rrequests); + ++srequests; + ++rrequests; + } + + { /* west -> east */ + int nrank; + int rcoords[2] = {coords[0], coords[1] + 1}; + MPI_Cart_rank(comm, rcoords, &nrank); + char & send = operator()(0, _dims[1] - 1); + char & recv = operator()(0, _dims[1] - 1 + _ghosts); + MPI_Send_init(&send, 1, _column_t, nrank, _west, comm, srequests); + MPI_Recv_init(&recv, 1, _column_t, nrank, _east, comm, rrequests); + ++srequests; + ++rrequests; + } + + { /* north -> south */ + int nrank; + int rcoords[2] = {coords[0] + 1, coords[1]}; + MPI_Cart_rank(comm, rcoords, &nrank); + char & send = operator()(_dims[0] - 1 , 0); + char & recv = operator()(_dims[0] - 1 + _ghosts, 0); + MPI_Send_init(&send, 1, _column_t, nrank, _north, comm, srequests); + MPI_Recv_init(&recv, 1, _column_t, nrank, _south, comm, rrequests); + ++srequests; + ++rrequests; + } + + /* east -> west */ + { + int nrank; + int rcoords[2] = {coords[0], coords[1] - 1}; + MPI_Cart_rank(comm, rcoords, &nrank); + char & send = operator()(0, 0); + char & recv = operator()(0, -_ghosts); + MPI_Send_init(&send, 1, _column_t, nrank, _east, comm, srequests); + MPI_Recv_init(&recv, 1, _column_t, nrank, _west, comm, rrequests); + ++srequests; + ++rrequests; + } +} diff --git a/game-of-life/grid.hh b/game-of-life/grid.hh index f3c4a98..1cf4967 100644 --- a/game-of-life/grid.hh +++ b/game-of-life/grid.hh @@ -1,55 +1,81 @@ +#include <algorithm> #include <cstddef> +#include <iostream> +#include <memory> +#include <mpi.h> +#include <vector> #ifndef GRID_H #define GRID_H class Grid { typedef char T; public: - Grid(std::size_t m, std::size_t n, std::size_t ghosts) - : _dims{m, n}, _offsets{0, 0}, _ghosts(ghosts), - _grid(new T[(m + 2 * _ghosts) * (n + 2 * _ghosts)]) {} + Grid(const long int ldims[2], const long int gdims[2], + const long int offsets[2], int ghost) + : _dims{ldims[0], ldims[1]}, _gdims{gdims[0], gdims[1]}, + _offsets{offsets[0], offsets[1]}, _ghosts(ghost), + _grid(new T[(ldims[0] + 2 * _ghosts) * (ldims[1] + 2 * _ghosts)]), + _requests(NULL) { + std::uninitialized_fill_n(_grid, fullSize() * sizeof(T), T()); + } - Grid(const Grid &other) - : _dims{other._dims[0], other._dims[1]}, - _offsets{other._offsets[0], other._offsets[1]}, _ghosts(other._ghosts), - _grid(new T[(_dims[0] + 2 * _ghosts) * (_dims[1] + 2 * _ghosts)]) {} + Grid(const Grid & other) + : Grid(other._dims, other._gdims, other._offsets, other._ghosts) { + std::copy_n(other._grid, fullSize(), _grid); + } - ~Grid() { delete[] _grid; } + ~Grid(); - T &operator()(std::size_t i, std::size_t j) { - return *(_grid + (i + _ghosts) * _dims[1] + (j + _ghosts)); + T & operator()(long int i, long int j) { + return *(_grid + (i + _ghosts) * (_dims[1] + 2 * _ghosts) + (j + _ghosts)); } - void swap(Grid &other) { - T *tmp = _grid; + void swap(Grid & other) { + T * tmp = _grid; _grid = other._grid; other._grid = tmp; + + MPI_Request * tmp_reqs = _requests; + _requests = other._requests; + other._requests = tmp_reqs; } - void setOffsets(std::size_t offsets[2]) { + void setOffsets(long int offsets[2]) { _offsets[0] = offsets[0]; _offsets[1] = offsets[1]; } - std::size_t size(std::size_t i) const { return _dims[i]; } - std::size_t size() const { return _dims[0] * _dims[1]; } + void initalizeCommunicationRequests(MPI_Comm & comm, int coords[2]); - std::size_t fullSize(std::size_t i) const { return (_dims[i]+2*_ghosts); } - std::size_t fullSize() const { return (_dims[0]+2*_ghosts) * (_dims[1]+2*_ghosts); } + void commAll() { MPI_Startall(16, _requests); } + void waitRecv() { MPI_Waitall(8, _requests + 8, MPI_STATUS_IGNORE); } + void waitSend() { MPI_Waitall(8, _requests, MPI_STATUS_IGNORE); } - std::size_t offset(std::size_t i) const { return _offsets[i]; } + long int size(long int i) const { return _dims[i]; } + long int size() const { return _dims[0] * _dims[1]; } - std::size_t ghost() const { return _ghosts; } + long int globalSize(long int i) const { return _gdims[i]; } + long int globalSize() const { return _gdims[0] * _gdims[1]; } + + long int fullSize(long int i) const { return (_dims[i] + 2 * _ghosts); } + long int fullSize() const { + return (_dims[0] + 2 * _ghosts) * (_dims[1] + 2 * _ghosts); + } + long int offset(long int i) const { return _offsets[i]; } + long int ghost() const { return _ghosts; } const T * data() const { return _grid; } private: - std::size_t _dims[2]; - std::size_t _offsets[2]; - std::size_t _ghosts; - T *_grid; + long int _dims[2]; + long int _gdims[2]; + long int _offsets[2]; + long int _ghosts; + T * _grid; + MPI_Request * _requests; + MPI_Datatype _column_t, _line_t; }; #endif /* GRID_H */ diff --git a/game-of-life/simu.hh b/game-of-life/simu.hh new file mode 100644 index 0000000..6c26e74 --- /dev/null +++ b/game-of-life/simu.hh @@ -0,0 +1,114 @@ +#ifndef SIMU_HH +#define SIMU_HH + +#include "client.hh" +#include "grid.hh" + +#include <iostream> +#include <mpi.h> +#include <random> + +/* -------------------------------------------------------------------------- */ +class Simu { +public: + Simu(long int dims[2], int ghost, bool periodic = true, + MPI_Comm global_comm = MPI_COMM_WORLD, + const std::string & server = "127.0.0.1") + : _ghost(ghost), _world(global_comm), periodic(periodic) { + + int psize, prank; + MPI_Comm_size(_world, &psize); + MPI_Comm_rank(_world, &prank); + + int nprocs[2] = {0, 0}; + int periods[2] = {periodic, periodic}; + + MPI_Dims_create(psize, 2, nprocs); + MPI_Cart_create(MPI_COMM_WORLD, 2, nprocs, periods, true, &_neigbours); + MPI_Cart_coords(_neigbours, prank, 2, _coords); + + long int local_dims[2]; + long int offsets[2] = {0, 0}; + + for (int i = 0; i < 2; ++i) { + int mod = dims[i] % nprocs[i]; + + local_dims[i] = dims[i] / nprocs[i]; + offsets[i] = local_dims[i] * _coords[i] + (_coords[i] < mod ? _coords[i] : mod); + + local_dims[i] += (_coords[i] < mod ? 1 : 0); + } + + _grid = new Grid(local_dims, dims, offsets, ghost); + _old_grid = new Grid(*_grid); + + _grid->initalizeCommunicationRequests(_neigbours, _coords); + _old_grid->initalizeCommunicationRequests(_neigbours, _coords); + + if (prank == 0) { + std::cout << "Grid " << _old_grid->globalSize() << " -- [" + << _old_grid->globalSize(0) << ", " << _old_grid->globalSize(1) + << "]" << std::endl; + } + + for (int p = 0; p < psize; ++p) { + if (p == prank) + std::cout << "Local grid " << prank << " - " << _old_grid->size() + << " -- [" << _old_grid->size(0) << ", " << _old_grid->size(1) + << "]" + << "+ [" << _old_grid->offset(0) << ", " << _old_grid->offset(1) + << "]" << std::endl; + MPI_Barrier(_world); + } + + sender = new GridSender(*_old_grid); + sender->setParallelContext(prank, psize); + sender->connect(server, 4321); + } + + + virtual void determineCells() { + _old_grid->commAll(); + for (long int i = _ghost; i < _grid->size(0) - _ghost; i++) { + for (long int j = _ghost; j < _grid->size(1) - _ghost; j++) { + this->determineCell(i, j); + } + } + _old_grid->waitRecv(); + + + for (long int i = 0; i < _grid->size(0); i++) { + this->determineCell(i, 0); + this->determineCell(i, _grid->size(1) - 1); + } + + for (long int j = 0; j < _grid->size(1); j++) { + this->determineCell(0, j); + this->determineCell(_grid->size(0) - 1, j); + } + + _old_grid->waitSend(); + } + + virtual void determineCell(long int i, long int j) = 0; + virtual void initialState() = 0; + + void swap() { _grid->swap(*_old_grid); } + void send() { sender->send(); } + + GridSender & getSender() { return *sender; } + +protected: + long int _ghost; + Grid * _grid; + Grid * _old_grid; + GridSender * sender; + + MPI_Comm _world; + MPI_Comm _neigbours; + int _coords[2]; + + bool periodic; +}; + +#endif /* SIMU_HH */ diff --git a/game-of-life/simu_gol.hh b/game-of-life/simu_gol.hh new file mode 100644 index 0000000..aaca568 --- /dev/null +++ b/game-of-life/simu_gol.hh @@ -0,0 +1,44 @@ +#ifndef SIMU_GOL_HH +#define SIMU_GOL_HH + +#include "simu.hh" + +/* -------------------------------------------------------------------------- */ +class GOL : public Simu { +public: + GOL(long int dims[2], int ghost, bool periodic = true, + MPI_Comm global_comm = MPI_COMM_WORLD, + const std::string & server = "127.0.0.1") + : Simu(dims, ghost, periodic, global_comm, server) {} + + void determineCell(long int i, long int j) { + long int _i = i; + long int _j = j; + char neighbours = -(*_old_grid)(_i, _j); + for (char v = -1; v < 2; ++v) + for (char w = -1; w < 2; ++w) + neighbours += (*_old_grid)(_i + v, _j + w); + + char res = 0; + if (neighbours == 2) { + res = (*_old_grid)(i, j); + } else if (neighbours == 3) { + res = 1; + } + + (*_grid)(_i, _j) = res; + } + + void initialState() { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 4); + for (long int i = 0; i < _old_grid->size(0); i++) { + for (long int j = 0; j < _old_grid->size(1); j++) { + (*_old_grid)(i, j) = dis(gen) > 3 ? 1 : 0; + } + } + } +}; + +#endif /* SIMU_GOL_HH */