From 35d3e18deb218323134c342c9a0c24fc203b78c4 Mon Sep 17 00:00:00 2001 From: Hanqi Guo Date: Wed, 13 Nov 2019 09:38:17 -0600 Subject: [PATCH 1/2] added a gather() function to gather std::map into the root process; added StringBuffer as an alternative to MemoryBuffer, in order to ease the serialization to std::string; serializeToString() is used in the new gather() function. --- include/diy/mpi/collectives.hpp | 58 +++++++++++++++++++++++++++++++++ include/diy/serialization.hpp | 49 ++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/include/diy/mpi/collectives.hpp b/include/diy/mpi/collectives.hpp index 226a65f89..bfebc15cb 100644 --- a/include/diy/mpi/collectives.hpp +++ b/include/diy/mpi/collectives.hpp @@ -1,6 +1,9 @@ #include +#include +#include #include "../constants.h" // for DIY_UNUSED. +#include "../serialization.hpp" // for serialization #include "operations.hpp" namespace diy @@ -363,6 +366,61 @@ namespace mpi Collectives::gather(comm, in, root); } + //! Gathering std::map into the root process; K and V are serialized into string + // and thus no datatype needs to be created for K and V + template // merging an std::map to root using gather + inline + void gather(const communicator& comm, const std::map& in, std::map &out, int root) + { +#ifndef DIY_NO_MPI + // serialize input map + std::string serialized_in; + if (comm.rank() != root) // avoid serializing data from the root proc + serializeToString(in, serialized_in); + + // gathering length of serialized data + int length_serialized_in = serialized_in.size(); + std::vector all_length_serialized_in(comm.size(), 0); + MPI_Gather(&length_serialized_in, 1, MPI_INT, + &all_length_serialized_in[0], 1, MPI_INT, + root, comm); + + // preparing buffer and displacements for MPI_Gatherv + std::string buffer; + std::vector displs; + if (comm.rank() == root) { + buffer.resize(std::accumulate( // prepare buffer + all_length_serialized_in.begin(), + all_length_serialized_in.end(), 0)); + + displs.resize(all_length_serialized_in.size(), 0); // prepare displs + for (int i = 1; i < comm.size(); i ++) + displs[i] = displs[i-1] + all_length_serialized_in[i-1]; + } + + // call MPI_Gatherv + MPI_Gatherv(serialized_in.data(), serialized_in.size(), MPI_CHAR, + &buffer[0], all_length_serialized_in.data(), displs.data(), MPI_CHAR, + root, comm); + + // unserailization (root proc only) + if (comm.rank() == root) { + out = in; + StringBuffer sb(buffer); + while (sb) { + std::map map; + load(sb, map); + for (const auto &kv : map) + out.insert(kv); + } + } +#else + DIY_UNUSED(comm); + DIY_UNUSED(root); + out = in; +#endif + } + //! all_gather from all processes in `comm`. //! `out` is resized to `comm.size()` and filled with //! elements from the respective ranks. diff --git a/include/diy/serialization.hpp b/include/diy/serialization.hpp index 6109b69b4..63285f9d6 100644 --- a/include/diy/serialization.hpp +++ b/include/diy/serialization.hpp @@ -65,6 +65,38 @@ namespace diy size_t position; std::vector buffer; }; + + + //! An alternative memory buffer based on std::string + struct StringBuffer : public BinaryBuffer { + std::string &str; + size_t pos; + + explicit StringBuffer(std::string& str_, size_t pos_=0) : str(str_), pos(pos_) {} + void clear() {str.clear(); pos = 0;} + void reset() {pos = 0;} + + operator bool() const {return pos < str.size();} + + inline void save_binary(const char *x, size_t count) { + if (pos + count > str.size()) str.resize(pos + count); + memcpy((char*)(str.data()+pos), x, count); + pos += count; + } + + inline void append_binary(const char *x, size_t count) { + str.append(x, count); + } + + inline void load_binary(char *x, size_t count) { + memcpy(x, str.data()+pos, count); + pos += count; + } + + inline void load_binary_back(char *x, size_t count) { + memcpy(x, str.data()+str.size()-count, count); + } + }; namespace detail { @@ -126,6 +158,23 @@ namespace diy //@} + //! serialize a data structure into string + template + void serializeToString(const T& obj, std::string& buf) + { + buf.clear(); + diy::StringBuffer bb(buf); + diy::save(bb, obj); + } + + //! unserialize a data structure from string + template + void unserializeFromString(const std::string& buf, T& obj) + { + std::string buf1(buf); + diy::StringBuffer bb(buf1); + diy::load(bb, obj); + } namespace detail { From 3a3473e8289872903de2763792cc570817a00a3f Mon Sep 17 00:00:00 2001 From: Hanqi Guo Date: Wed, 13 Nov 2019 09:53:10 -0600 Subject: [PATCH 2/2] serialization for std::deque --- include/diy/serialization.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/diy/serialization.hpp b/include/diy/serialization.hpp index 63285f9d6..18e3b7a73 100644 --- a/include/diy/serialization.hpp +++ b/include/diy/serialization.hpp @@ -311,6 +311,29 @@ namespace diy } }; + // save/load for std::deque + template + struct Serialization< std::deque > { + typedef std::deque deque; + + static void save(BinaryBuffer& bb, const deque& q) { + size_t s = q.size(); + diy::save(bb, q); + for (typename std::deque::const_iterator it = q.begin(); it != q.end(); it ++) + diy::save(bb, *it); + } + + static void load(BinaryBuffer& bb, deque& q) { + size_t s; + diy::load(bb, s); + for (int i = 0; i < s; i ++) { + T p; + diy::load(bb, p); + q.emplace_back(p); + } + } + }; + // save/load for std::pair template struct Serialization< std::pair >