diff --git a/game-of-life/CMakeLists.txt b/game-of-life/CMakeLists.txt new file mode 100644 index 0000000..6e257cf --- /dev/null +++ b/game-of-life/CMakeLists.txt @@ -0,0 +1,10 @@ +project(GOL) +cmake_minimum_required(VERSION 2.8) + +find_package(MPI REQUIRED) + +set(CMAKE_CXX_FLAGS "-Wall -Werror -Wextra -pedantic -std=c++1y") + +add_executable(gol game-of-life.cc client.cc client.hh grid.hh) +target_include_directories(gol PRIVATE ${MPI_CXX_INCLUDE_PATH}) +target_link_libraries(gol ${MPI_CXX_LIBRARIES}) diff --git a/game-of-life/client.cc b/game-of-life/client.cc new file mode 100644 index 0000000..6932a7f --- /dev/null +++ b/game-of-life/client.cc @@ -0,0 +1,55 @@ +#include "client.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); + } +}; + +/* -------------------------------------------------------------------------- */ +GridSender::GridSender(const Grid &grid, const std::string &server_name, + int port) + : _grid(grid) { + 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 + + 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); +} + +/* -------------------------------------------------------------------------- */ +GridSender::~GridSender() { close(sockfd); } + +/* -------------------------------------------------------------------------- */ +void GridSender::send() { + error_handler(::send(sockfd, _grid.data(), _grid.size(), 0), + "Could not send the grid"); +} diff --git a/game-of-life/client.hh b/game-of-life/client.hh new file mode 100644 index 0000000..c37ebbc --- /dev/null +++ b/game-of-life/client.hh @@ -0,0 +1,15 @@ +#include "grid.hh" +#include + +class GridSender { +public: + GridSender(const Grid &grid, const std::string &server, int port); + + ~GridSender(); + + void send(); + +private: + const Grid &_grid; + int sockfd; +}; diff --git a/game-of-life/game-of-life.cc b/game-of-life/game-of-life.cc new file mode 100644 index 0000000..25dd212 --- /dev/null +++ b/game-of-life/game-of-life.cc @@ -0,0 +1,70 @@ +#include "grid.hh" +#include "client.hh" + +#include +#include + +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); + + int nprocs[2] = {0, 0}; + MPI_Dims_create(nproc, 2, nprocs); + + 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] = {1080, 1920}; + 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); + } + + Grid grid(local_dims[0], local_dims[1], 1); + grid.setOffsets(offsets); + + Grid old_grid(grid); + + GridSender sender(old_grid, "127.0.0.1", 4321); + + std::cout << "Grid " < 3) + grid(i, j) = 0; + } + } + + grid.swap(old_grid); + sender.send(); + } + + MPI_Finalize(); + + return 0; +} diff --git a/game-of-life/gol.py b/game-of-life/gol.py new file mode 100755 index 0000000..3c1e84f --- /dev/null +++ b/game-of-life/gol.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 + +""" +Example of a simple TCP server that is written in (mostly) coroutine +style and uses asyncio.streams.start_server() and +asyncio.streams.open_connection(). +Note that running this example starts both the TCP server and client +in the same process. It listens on port 12345 on 127.0.0.1, so it will +fail if this port is currently in use. +""" + +import sys +import socket +import asyncio +import asyncio.streams +import matplotlib.pyplot as plt +import numpy as np + + +class MyServer: + """ + This is just an example of how a TCP server might be potentially + structured. This class has basically 3 methods: start the server, + handle a client, and stop the server. + Note that you don't have to follow this structure, it is really + just an example or possible starting point. + """ + + def __init__(self): + self.server = None # encapsulates the server sockets + + # this keeps track of all the clients that connected to our + # server. It can be useful in some cases, for instance to + # kill client connections or to broadcast some data to all + # clients... + self.clients = {} # task -> (reader, writer) + + def _accept_client(self, client_reader, client_writer): + """ + This method accepts a new client connection and creates a Task + to handle this client. self.clients is updated to keep track + of the new client. + """ + + # start a new Task to handle this specific client connection + task = asyncio.Task(self._handle_client(client_reader, client_writer)) + self.clients[task] = (client_reader, client_writer) + print("new client") + + def client_done(task): + print("client task done:", task, file=sys.stderr) + del self.clients[task] + + task.add_done_callback(client_done) + + @asyncio.coroutine + def _handle_client(self, client_reader, client_writer): + """ + This method actually does the work to handle the requests for + a specific client. The protocol is line oriented, so there is + a main loop that reads a line with a request and then sends + out one or more lines back to the client with the result. + """ + while True: + data = (yield from client_reader.read()) + if not data: # an empty string means the client disconnected + break + img = np.frombuffer(data, dtype=np.uint8) + img.reshape((1080,1920,100)) + plt.imshow(img[:,:,99]) + plt.show() + + def start(self, loop, port = 4321): + """ + Starts the TCP server, so that it listens on port 12345. + For each client that connects, the accept_client method gets + called. This method runs the loop until the server sockets + are ready to accept connections. + """ + self.server = loop.run_until_complete( + asyncio.streams.start_server(self._accept_client, + '0.0.0.0', port, + loop=loop)) + + def stop(self, loop): + """ + Stops the TCP server, i.e. closes the listening socket(s). + This method runs the loop until the server sockets are closed. + """ + if self.server is not None: + self.server.close() + loop.run_until_complete(self.server.wait_closed()) + self.server = None + + +def main(): + loop = asyncio.get_event_loop() + + # creates a server and starts listening to TCP connections + server = MyServer() + server.start(loop) + + try: + loop.run_forever() + finally: + server.stop() + loop.close() + +if __name__ == '__main__': + main() diff --git a/game-of-life/grid.hh b/game-of-life/grid.hh new file mode 100644 index 0000000..b7076e4 --- /dev/null +++ b/game-of-life/grid.hh @@ -0,0 +1,48 @@ +#include + +#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}, _offset{0, 0}, _ghosts(ghosts), + _grid(new T[(m + 2 * _ghosts) * (n + 2 * _ghosts)]) {} + + Grid(const Grid &other) + : _dims{other._dims[0], other._dims[1]}, + _offset{other._offset[0], other._offset[1]}, _ghosts(other._ghosts), + _grid(new T[(_dims[0] + 2 * _ghosts) * (_dims[1] + 2 * _ghosts)]) {} + + ~Grid() { delete[] _grid; } + + T &operator()(std::size_t i, std::size_t j) { + return *(_grid + (i + _ghosts) * _dims[1] + (j + _ghosts)); + } + + void swap(Grid &other) { + T *tmp = _grid; + _grid = other._grid; + other._grid = tmp; + } + + void setOffsets(std::size_t offset[2]) { + _offset[0] = offset[0]; + _offset[1] = offset[1]; + } + + std::size_t size(std::size_t i) const { return _dims[i]; } + std::size_t size() const { return _dims[0] * _dims[1]; } + + const T * data() const { return _grid; } + +private: + std::size_t _dims[2]; + std::size_t _offset[2]; + std::size_t _ghosts; + T *_grid; +}; + +#endif /* GRID_H */