#pragma once #include #include #include #include #include #include #include #include #include #include "gsl/multi_span" #include "gsl/span" #include "Communicator.hpp" #include "MpiEnvironment.hpp" #include "Util.hpp" #include "state.hpp" class MpiWireworld { const MpiEnvironment& _env; const Configuration& _cfg; Size _tileSize; std::vector _memoryA; std::vector _memoryB; gsl::multi_span _model; gsl::multi_span _nextModel; // begin IO functions static auto ReadHeader(std::istream& input) { std::size_t noCols{}; std::size_t noRows{}; input >> noCols >> noRows; if (noCols < 1 || noRows < 1) throw std::logic_error("File header corrupt"); std::string dummy; // skip line break std::getline(input, dummy); const auto& headerSize = input.tellg(); return Size{noCols, noRows}; } static auto GetTileSize(Size globalSize, Size gridSize) { const auto& tileSizeCols = globalSize.Cols / gridSize.Cols; const auto& tileSizeRows = globalSize.Rows / gridSize.Rows; return Size{tileSizeCols, tileSizeRows}; } static void StridedRead(std::istream& input, std::size_t inputChunkLength, std::size_t count, std::size_t inputStride, gsl::span buf, std::size_t bufStride) { std::size_t writeOffset{0}; for (size_t i{0}; i < count; ++i) { input.read(reinterpret_cast(buf.data()) + writeOffset, inputChunkLength); input.seekg(inputStride - inputChunkLength, std::ios::cur); writeOffset += bufStride; } } static void ReadTiles(std::istream& input, Size srcSize, Size gridSize, std::size_t rank, gsl::span buf, std::size_t bufStride) { constexpr auto LF = 1; // linefeed chars const auto& tileSize = GetTileSize(srcSize, gridSize); const auto& tileX = rank % gridSize.Cols; const auto& tileY = rank / gridSize.Cols; // seek to the begin of the tile const auto& displacement = (srcSize.Cols + LF) * (tileSize.Rows * tileY) + (tileSize.Cols * tileX); input.seekg(displacement, std::ios::cur); const auto& tileStride = srcSize.Cols + LF; StridedRead(input, tileSize.Cols, tileSize.Rows, tileStride, buf, bufStride); } void ReadFile(const std::string& path, Size gridSize) { std::ifstream input(path); const auto& globalSize = ReadHeader(input); _tileSize = GetTileSize(globalSize, gridSize); _memoryA.resize((_tileSize.Cols + 2) * (_tileSize.Rows + 2)); _memoryB.resize((_tileSize.Cols + 2) * (_tileSize.Rows + 2)); _model = gsl::as_multi_span(_memoryA.data(), gsl::dim(_tileSize.Rows + 2), gsl::dim(_tileSize.Cols + 2)); _nextModel = gsl::as_multi_span(_memoryB.data(), gsl::dim(_tileSize.Rows + 2), gsl::dim(_tileSize.Cols + 2)); ReadTiles(input, globalSize, gridSize, _env.worldRank(), gsl::span(_memoryA.data() + _tileSize.Cols + 2 + 1, _memoryA.size() - 1), _tileSize.Cols + 2); /* // Trying to acomplish this with MPI // if (env.isMaster()) { // std::cout << "noCols: " << _noCols << '\n'; // std::cout << "noRows: " << _noRows << '\n'; //} // const auto& noDims = 2; // const auto& gsizes = std::array{ // _noCols + 1, // // _noRows, // //}; // const auto& distribs = std::array{ // MPI_DISTRIBUTE_BLOCK, // // MPI_DISTRIBUTE_BLOCK // //}; // const auto& dargs = std::array{ // MPI_DISTRIBUTE_DFLT_DARG, // // MPI_DISTRIBUTE_DFLT_DARG // //}; // const auto& psizes = std::array{ // 2, // // 4 // //}; //// const auto& noDims = 1; //// const auto& gsizes = std::array{_noCols}; //// const auto& distribs = std::array{MPI_DISTRIBUTE_BLOCK}; //// const auto& dargs = std::array{MPI_DISTRIBUTE_DFLT_DARG}; //// const auto& psizes = std::array{8}; ////MPI_Datatatype tiletype; ////int global_sizes[2] = { _noCols + 1, _noRows }; ////int tile_sizes[2] = { 10, 5 }; ////int tile_start[2] = { } ////MPI_Type_create_subarray(2, global_sizes, tile_sizes, ) // MPI_Datatype tiletype; ////MPI_Datatype linetype; ////MPI_Datatype linetype_ext; ////MPI_Type_contiguous(_noCols, MPI_CHAR, &linetype); ////MPI_Type_create_resized(linetype, 0, _noCols + 1, &linetype_ext); ////MPI_Type_commit(&linetype_ext); ////MPI_Type_free(&linetype); // // MPI_Type_create_darray(env.worldSize(), env.worldRank(), noDims, // gsizes.data(), distribs.data(), dargs.data(), // psizes.data(), MPI_ORDER_C, MPI_CHAR, // &tiletype); // MPI_Type_commit(&tiletype); // int tiletypeSize; // MPI_Type_size(tiletype, &tiletypeSize); // if (env.isMaster()) // std::cout << "tiletypeSize: " << tiletypeSize << '\n'; // MPI_File fh; // MPI_File_open(MPI_COMM_WORLD, path.c_str(), MPI_MODE_RDONLY, // MPI_INFO_NULL, &fh); // MPI_File_set_view(fh, _headerSize, MPI_CHAR, MPI_CHAR, "native", // MPI_INFO_NULL); // const auto& typeCols = 10; // how to get those? // const auto& typeRows = 5; MPI_File_read_all(fh, buf.data(), 1, tiletype, MPI_STATUS_IGNORE); MPI_File_close(&fh); std::replace(std::begin(buf), std::end(buf), '\n', 'L'); */ } // end IO functions public: MpiWireworld(const MpiEnvironment& env, const Configuration& cfg) : _env(env), _cfg(cfg) { const auto& gridSize = Size{cfg.GridColumns, cfg.GridRows}; ReadFile(_cfg.InputFilePath, gridSize); Communicator comm{env, {cfg.GridColumns, cfg.GridRows}, _tileSize}; comm.Communicate(_model); } friend std::ostream& operator<<(std::ostream& out, const MpiWireworld& g) { for (std::size_t x{0}; x < g._tileSize.Rows + 2; ++x) { out << '"'; for (std::size_t y{0}; y < g._tileSize.Cols + 2; ++y) { out << to_integral(g._model[x][y]); } out << "\"\n"; } return out; } void simulateStep() { for (std::size_t x{1}; x <= _tileSize.Rows; ++x) { for (std::size_t y{1}; y <= _tileSize.Cols; ++y) { auto nextState = _model[x][y]; switch (_model[x][y]) { case State::ElectronHead: nextState = State::ElectronTail; break; case State::ElectronTail: nextState = State::Conductor; break; case State::Conductor: { const std::array mooreNeighborhood = { _model[x - 1][y - 1], // _model[x + 0][y - 1], // _model[x + 1][y - 1], // _model[x - 1][y + 0], // _model[x + 1][y + 0], // _model[x - 1][y + 1], // _model[x + 0][y + 1], // _model[x + 1][y + 1] // }; const auto& headCount = std::count( std::begin(mooreNeighborhood), std::end(mooreNeighborhood), State::ElectronHead); nextState = (1 == headCount || headCount == 2) ? State::ElectronHead : State::Conductor; } break; default: break; } _nextModel[x][y] = nextState; } } std::swap(_model, _nextModel); } };