diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 96469b7..dd1cd5b 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -11,6 +11,7 @@ doxygen mpi_autodoc : [ glob ../../../boost/mpi.hpp ../../../boost/mpi/allocator.hpp + ../../../boost/mpi/cartesian_communicator.hpp ../../../boost/mpi/collectives.hpp ../../../boost/mpi/collectives_fwd.hpp ../../../boost/mpi/communicator.hpp diff --git a/include/boost/mpi/cartesian_communicator.hpp b/include/boost/mpi/cartesian_communicator.hpp index a722648..1606e4e 100644 --- a/include/boost/mpi/cartesian_communicator.hpp +++ b/include/boost/mpi/cartesian_communicator.hpp @@ -22,6 +22,7 @@ #include #include +#include // Headers required to implement cartesian topologies #include @@ -29,7 +30,66 @@ namespace boost { namespace mpi { -class cartesian_topology; +struct cartesian_dimension { + + int size; + bool periodic; + cartesian_dimension(int sz = 0, bool p = false) : size(sz), periodic(p) {} +private: + friend class boost::serialization::access; + template + void serialize(Archive & ar, const unsigned int version) + { + ar & size & periodic; + } + +}; + +template <> +struct is_mpi_datatype : mpl::true_ { }; + +bool +operator==(cartesian_dimension const& d1, cartesian_dimension const& d2) { + return d1.size == d2.size && d1.periodic == d2.periodic; +} + +bool +operator!=(cartesian_dimension const& d1, cartesian_dimension const& d2) { + return !(d1 == d2); +} + +std::ostream& operator<<(std::ostream& out, cartesian_dimension const& d); + +class BOOST_MPI_DECL cartesian_topology + : private std::vector { + friend class cartesian_communicator; + typedef std::vector super; + public: + using super::operator[]; + using super::size; + using super::begin; + using super::end; + using super::swap; + + cartesian_topology(int sz) + : super(sz) {} + + template + explicit cartesian_topology(array const& dims) + : super(NDIM) { + std::copy(dims.begin(), dims.end(), begin()); + } + + template + cartesian_topology(DimIter dim_iter, PerIter period_iter, int ndim) + : super(ndim) { + for(int i = 0; i < ndim; ++i) { + (*this)[i] = cartesian_dimension(*dim_iter++, *period_iter++); + } + } + + void split(std::vector& dims, std::vector& periodics) const; +}; /** * @brief An MPI communicator with a cartesian topology. @@ -103,23 +163,19 @@ public: * @param comm The communicator that the new, cartesian communicator * will be based on. * - * @param dims the dimension of the new communicator. The size indicate - * the number of dimension. Some value can be set to zero, in which case + * @param dims the cartesian dimension of the new communicator. The size indicate + * the number of dimension. Some dimensions be set to zero, in which case * the corresponding dimension value is left to the system. * - * @param periodic must be the same size as dims. Each value indicate if - * the corresponding dimension is cyclic. - * * @param reorder Whether MPI is permitted to re-order the process * ranks within the returned communicator, to better optimize * communication. If false, the ranks of each process in the * returned process will match precisely the rank of that process * within the original communicator. */ - cartesian_communicator(const communicator& comm, - const std::vector& dims, - const std::vector& periodic, - bool reorder = false); + cartesian_communicator(const communicator& comm, + const cartesian_topology& dims, + bool reorder = false); /** * Create a new cartesian communicator whose topology is a subset of @@ -142,10 +198,10 @@ public: * Return the rank of the process at the given coordinates. * @param coords the coordinates. the size must match the communicator's topology. */ - int rank(std::vector const& coords) const; + int rank(const std::vector& coords) const; /** - * Provides the coordinates of the process with the given rank. - * @param rk the ranks in this communicator. + * Provides the coordinates of a process with the given rank. + * @param rk the rank in this communicator. * @param cbuf a buffer were to store the coordinates. * @returns a reference to cbuf. */ @@ -160,29 +216,31 @@ public: * Retrieve the topology. * */ - void topology( std::vector& dims, - std::vector& periodic, - std::vector& coords ) const; + void topology( cartesian_topology& dims, std::vector& coords ) const; }; -std::vector& cartesian_dimensions(int sz, std::vector& dims); +/** + * Given en number of processes, and a partially filled sequence + * of dimension, try to complete the dimension sequence. + * @param nb_proc the numer of mpi processes.fill a sequence of dimension. + * @param dims a sequence of positive or null dimensions. Non zero dimension + * will be left untouched. + */ +std::vector& cartesian_dimensions(int nb_proc, std::vector& dims); +/** + * Given en communicator and a partially filled sequence + * of dimension, try to complete the dimension sequence to produce an acceptable + * cartesian topology. + * @param comm the prospective parent communicator. + * @param dims a sequence of positive or null dimensions. Non zero dimension + * will be left untouched. + */ inline -std::vector& cartesian_dimensions(communicator const& comm, std::vector& dims) { +std::vector& cartesian_dimensions(const communicator& comm, std::vector& dims) { return cartesian_dimensions(comm.size(), dims); } -class BOOST_MPI_DECL cartesian_topology { - public: - cartesian_topology(cartesian_communicator const& comm); - - private: - cartesian_communicator const& comm_ref; - -}; - } } // end namespace boost::mpi - - #endif // BOOST_MPI_CARTESIAN_COMMUNICATOR_HPP diff --git a/src/cartesian_communicator.cpp b/src/cartesian_communicator.cpp index 03d5652..d019128 100644 --- a/src/cartesian_communicator.cpp +++ b/src/cartesian_communicator.cpp @@ -12,23 +12,37 @@ namespace boost { namespace mpi { -cartesian_communicator::cartesian_communicator(const communicator& comm, - const std::vector& dims, - const std::vector& periodic, - bool reorder ) +std::ostream& +operator<<(std::ostream& out, cartesian_dimension const& d) { + out << '(' << d.size << ','; + if (d.periodic) { + out << "periodic"; + } else { + out << "bounded"; + } + out << ')'; + return out; +} + +cartesian_communicator::cartesian_communicator(const communicator& comm, + const cartesian_topology& topology, + bool reorder ) : communicator() { - BOOST_ASSERT(dims.size() == periodic.size()); - MPI_Comm newcomm; - std::vector local_dims = dims; - // Fill the gaps, if any - if (std::count(local_dims.begin(), local_dims.end(), 0) > 0) { - cartesian_dimensions(comm, local_dims); + std::vector dims(topology.size()); + std::vector periodic(topology.size()); + for(int i = 0; i < topology.size(); ++i) { + dims[i] = topology[i].size; + periodic[i] = topology[i].periodic; } - std::vector p(periodic.begin(), periodic.end()); + // Fill the gaps, if any + if (std::count(dims.begin(), dims.end(), 0) > 0) { + cartesian_dimensions(comm, dims); + } + MPI_Comm newcomm; BOOST_MPI_CHECK_RESULT(MPI_Cart_create, ((MPI_Comm)comm, dims.size(), - local_dims.data(), p.data(), + dims.data(), periodic.data(), int(reorder), &newcomm)); if(newcomm != MPI_COMM_NULL) { comm_ptr.reset(new MPI_Comm(newcomm), comm_free()); @@ -64,7 +78,7 @@ cartesian_communicator::ndims() const { } int -cartesian_communicator::rank(std::vector const& coords ) const { +cartesian_communicator::rank(const std::vector& coords ) const { int r = -1; BOOST_ASSERT(coords.size() == ndims()); BOOST_MPI_CHECK_RESULT(MPI_Cart_rank, @@ -72,7 +86,7 @@ cartesian_communicator::rank(std::vector const& coords ) const { &r)); return r; } - + std::vector& cartesian_communicator::coords(int rk, std::vector& cbuf) const { cbuf.resize(ndims()); @@ -80,7 +94,7 @@ cartesian_communicator::coords(int rk, std::vector& cbuf) const { (MPI_Comm(*this), rk, cbuf.size(), cbuf.data() )); return cbuf; } - + std::vector cartesian_communicator::coords(int rk) const { std::vector coords; @@ -89,17 +103,29 @@ cartesian_communicator::coords(int rk) const { } void -cartesian_communicator::topology( std::vector& dims, - std::vector& periodic, - std::vector& coords ) const { +cartesian_communicator::topology( cartesian_topology& topo, + std::vector& coords ) const { int ndims = this->ndims(); - dims.resize(ndims); - periodic.resize(ndims); + topo.resize(ndims); coords.resize(ndims); + std::vector cdims(ndims); std::vector cperiods(ndims); BOOST_MPI_CHECK_RESULT(MPI_Cart_get, - (MPI_Comm(*this), ndims, dims.data(), cperiods.data(), coords.data())); - std::copy(cperiods.begin(), cperiods.end(), periodic.begin()); + (MPI_Comm(*this), ndims, cdims.data(), cperiods.data(), coords.data())); + cartesian_topology res(cdims.begin(), cperiods.begin(), ndims); + topo.swap(res); +} + +void +cartesian_topology::split(std::vector& dims, std::vector& periodics) const { + int ndims = size(); + dims.resize(ndims); + periodics.resize(ndims); + for(int i = 0; i < ndims; ++i) { + cartesian_dimension const& d = (*this)[i]; + dims[i] = d.size; + periodics[i] = d.periodic; + } } std::vector& diff --git a/test/cartesian_topology_test.cpp b/test/cartesian_topology_test.cpp index 4143713..ba52b8d 100644 --- a/test/cartesian_topology_test.cpp +++ b/test/cartesian_topology_test.cpp @@ -22,29 +22,18 @@ namespace mpi = boost::mpi; -struct bvecmin { - std::vector operator()(std::vector const& v1, - std::vector const& v2) const { - BOOST_ASSERT(v1.size() == v2.size()); - std::vector res(v1.size()); - std::transform(v1.begin(), v1.end(), v2.begin(), res.begin(), - std::logical_and()); - return res; +struct topo_minimum { + mpi::cartesian_dimension + operator()(mpi::cartesian_dimension const& d1, + mpi::cartesian_dimension const& d2 ) const { + return mpi::cartesian_dimension(std::min(d1.size, d2.size), + d1.periodic && d2.periodic); } }; -std::string topology_description( std::vector const& dims, - std::vector const& periodic ) { +std::string topology_description( mpi::cartesian_topology const& topo ) { std::ostringstream out; - for(int i = 0; i < dims.size(); ++i) { - out << "(" << dims[i] << ','; - if (periodic[i]) { - out << "cyclic"; - } else { - out << "bounded"; - } - out << ") "; - } + std::copy(topo.begin(), topo.end(), std::ostream_iterator(out, " ")); out << std::flush; return out.str(); } @@ -72,25 +61,19 @@ void test_coordinates_consistency( mpi::cartesian_communicator const& cc, void test_topology_consistency( mpi::cartesian_communicator const& cc) { - std::vector idims(cc.ndims()); - std::vector iperiodic(cc.ndims()); - std::vector odims(cc.ndims()); - std::vector operiodic; + mpi::cartesian_topology itopo(cc.ndims()); + mpi::cartesian_topology otopo(cc.ndims()); std::vector coords(cc.ndims()); - cc.topology(idims, iperiodic, coords); + cc.topology(itopo, coords); // Check that everyone agrees on the dimensions mpi::all_reduce(cc, - &(idims[0]), idims.size(), &(odims[0]), - mpi::minimum()); - BOOST_CHECK(std::equal(idims.begin(), idims.end(), - odims.begin())); - // Check that everyone agree on the periodicities - mpi::all_reduce(cc, iperiodic, operiodic, bvecmin()); - BOOST_CHECK(std::equal(iperiodic.begin(), iperiodic.end(), - operiodic.begin())); + &(itopo[0]), itopo.size(), &(otopo[0]), + topo_minimum()); + BOOST_CHECK(std::equal(itopo.begin(), itopo.end(), + otopo.begin())); if (cc.rank() == 0) { - std::cout << topology_description(odims, operiodic) << '\n'; + std::cout << topology_description(otopo) << '\n'; } test_coordinates_consistency( cc, coords ); } @@ -100,16 +83,16 @@ int test_main(int argc, char* argv[]) mpi::environment env(argc, argv); mpi::communicator world; - std::vector dims(3); - std::vector periodic(3); + mpi::cartesian_topology topo(3); + if (world.size() == 24) { - dims[0] = 2; dims[1] = 3; dims[2] = 4; + topo[0].size = 2; topo[1].size = 3; topo[2].size = 4; } else { - dims[0] = 0; dims[1] = 0; dims[2] = 0; + topo[0].size = 0; topo[1].size = 3; topo[2].size = 0; } - periodic[0] = true; periodic[1] = false; periodic[2] = true; + topo[0].periodic = true; topo[1].periodic = false; topo[2].periodic = true; - mpi::cartesian_communicator cc(world, dims, periodic, true); + mpi::cartesian_communicator cc(world, topo, true); BOOST_CHECK(cc.has_cartesian_topology()); BOOST_CHECK(cc.ndims() == 3); for( int r = 0; r < cc.size(); ++r) {