From 0d6a13bfc48a4f11e6df661c6e5bbf8cb136b021 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 31 May 2007 15:36:48 +0000 Subject: [PATCH] Add support for MPI groups [SVN r37831] --- build/Jamfile.v2 | 1 + doc/Jamfile.v2 | 1 + doc/mpi.qbk | 36 +-- include/boost/mpi.hpp | 1 + include/boost/mpi/communicator.hpp | 5 +- include/boost/mpi/group.hpp | 349 +++++++++++++++++++++++++++++ src/group.cpp | 115 ++++++++++ 7 files changed, 490 insertions(+), 18 deletions(-) create mode 100644 include/boost/mpi/group.hpp create mode 100644 src/group.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 9ad4dd9..8cc2ca8 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -25,6 +25,7 @@ lib boost_mpi computation_tree.cpp content_oarchive.cpp environment.cpp + group.cpp mpi_datatype_cache.cpp mpi_datatype_oarchive.cpp packed_iarchive.cpp diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index b9e4b53..29af3e2 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -19,6 +19,7 @@ doxygen mpi_autodoc ../../../boost/mpi/datatype_fwd.hpp ../../../boost/mpi/environment.hpp ../../../boost/mpi/exception.hpp + ../../../boost/mpi/group.hpp ../../../boost/mpi/nonblocking.hpp ../../../boost/mpi/operations.hpp ../../../boost/mpi/packed_iarchive.hpp diff --git a/doc/mpi.qbk b/doc/mpi.qbk index 6ed518c..04e60c2 100644 --- a/doc/mpi.qbk +++ b/doc/mpi.qbk @@ -1,6 +1,6 @@ [library Boost.MPI [authors [Gregor, Douglas], [Troyer, Matthias] ] - [copyright 2005 2006 Douglas Gregor, Matthias Troyer] + [copyright 2005 2006 2007 Douglas Gregor, Matthias Troyer, Trustees of Indiana University] [purpose An generic, user-friendly interface to MPI, the Message Passing Interface. @@ -1373,36 +1373,38 @@ are all instances of the [classref boost::mpi::communicator [[`MPI_COMM_EMPTY`] [a [classref boost::mpi::communicator `communicator`] that evaluates false]] ] -Boost.MPI currently has no support for groups of processes, as -specified in MPI 1.1. +Boost.MPI supports groups of processes through its [classref +boost::mpi::group `group`] class. [table Group operations and constants [[C Function/Constant] [Boost.MPI Equivalent]] - [[`MPI_GROUP_EMPTY`] [unsupported]] + [[`MPI_GROUP_EMPTY`] [a default-constructed [classref + boost::mpi::group `group`]]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node97.html#Node97 - `MPI_Group_size`]] [unsupported]] + `MPI_Group_size`]] [[memberref boost::mpi::group::size `group::size`]]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node97.html#Node97 - `MPI_Group_rank`]] [unsupported]] + `MPI_Group_rank`]] [memberref boost::mpi::group::rank `group::rank`]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node97.html#Node97 - `MPI_Group_translate_ranks`]] [unsupported]] + `MPI_Group_translate_ranks`]] [memberref boost::mpi::group::translate_ranks `group::translate_ranks`]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node97.html#Node97 - `MPI_Group_compare`]] [unsupported]] - [[`MPI_IDENT`] [unsupported]] - [[`MPI_SIMILAR`] [unsupported]] - [[`MPI_UNEQUAL`] [unsupported]] + `MPI_Group_compare`]] [operators `==` and `!=`]] + [[`MPI_IDENT`] [operators `==` and `!=`]] + [[`MPI_SIMILAR`] [operators `==` and `!=`]] + [[`MPI_UNEQUAL`] [operators `==` and `!=`]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node98.html#Node98 - `MPI_Comm_group`]] [unsupported]] + `MPI_Comm_group`]] [[classref + boost::mpi::group `group`] constructor]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node98.html#Node98 - `MPI_Group_union`]] [unsupported]] + `MPI_Group_union`]] [operator `|` for groups]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node98.html#Node98 - `MPI_Group_intersection`]] [unsupported]] + `MPI_Group_intersection`]] [operator `&` for groups]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node98.html#Node98 - `MPI_Group_difference`]] [unsupported]] + `MPI_Group_difference`]] [operator `-` for groups]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node98.html#Node98 - `MPI_Group_incl`]] [unsupported]] + `MPI_Group_incl`]] [[memberref boost::mpi::group::include `group::include`]]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node98.html#Node98 - `MPI_Group_excl`]] [unsupported]] + `MPI_Group_excl`]] [[memberref boost::mpi::group::include `group::exclude`]]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node98.html#Node98 `MPI_Group_range_incl`]] [unsupported]] [[[@http://www.mpi-forum.org/docs/mpi-11-html/node98.html#Node98 diff --git a/include/boost/mpi.hpp b/include/boost/mpi.hpp index 705d2f8..d59c16f 100644 --- a/include/boost/mpi.hpp +++ b/include/boost/mpi.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/mpi/communicator.hpp b/include/boost/mpi/communicator.hpp index d0bf763..dc9d538 100644 --- a/include/boost/mpi/communicator.hpp +++ b/include/boost/mpi/communicator.hpp @@ -759,7 +759,10 @@ class communicator void abort(int errcode) const; protected: - /* Function object that frees an MPI communicator and deletes the + /** + * INTERNAL ONLY + * + * Function object that frees an MPI communicator and deletes the * memory associated with it. Intended to be used as a deleter with * shared_ptr. */ diff --git a/include/boost/mpi/group.hpp b/include/boost/mpi/group.hpp new file mode 100644 index 0000000..1063a9d --- /dev/null +++ b/include/boost/mpi/group.hpp @@ -0,0 +1,349 @@ +// Copyright (C) 2007 Trustees of Indiana University + +// Authors: Douglas Gregor +// Andrew Lumsdaine + +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +/** @file group.hpp + * + * This header defines the @c group class, which allows one to + * manipulate and query groups of processes. + */ +#ifndef BOOST_MPI_GROUP_HPP +#define BOOST_MPI_GROUP_HPP + +#include +#include +#include +#include + +namespace boost { namespace mpi { + +/** + * INTERNAL ONLY + * + * Forward-declaration of @c communicator needed for the @c + * communicator constructor. + */ +class communicator; + +class group +{ +public: + /** + * @brief Constructs an empty group. + */ + group() : group_ptr() { } + + /** + * @brief Constructs a group from an @c MPI_Group. + * + * This routine allows one to construct a Boost.MPI @c group from a + * C @c MPI_Group. The @c group object can (optionally) adopt the @c + * MPI_Group, after which point the @c group object becomes + * responsible for freeing the @c MPI_Group when the last copy of @c + * group disappears. + * + * @param in_group The @c MPI_Group used to construct this @c group. + * + * @param adopt Whether the @c group should adopt the @c + * MPI_Group. When true, the @c group object (or one of its copies) + * will free the group (via @c MPI_Comm_free) when the last copy is + * destroyed. Otherwise, the user is responsible for calling @c + * MPI_Group_free. + */ + group(const MPI_Group& in_group, bool adopt); + + /** + * + * @brief Constructs a group from a communicator. + * + * This routine constructs a new group whose members are the + * processes within the given communicator, @p comm. Equivalent to + * calling @c MPI_Comm_group. + * + * @param comm The communicator whose group we are constructing. + */ + group(const communicator& comm); + + /** + * @brief Determine the rank of the calling process in the group. + * + * This routine is equivalent to @c MPI_Group_rank. + * + * @returns The rank of the calling process in the group, which will + * be a value in [0, size()). If the calling process is not in the + * group, returns an empty value. + */ + optional rank() const; + + /** + * @brief Determine the number of processes in the group. + * + * This routine is equivalent to @c MPI_Group_size. + * + * @returns The number of processes in the group. + */ + int size() const; + + /** + * @brief Translates the ranks from one group into the ranks of the + * same processes in another group. + * + * This routine translates each of the integer rank values in the + * iterator range @c [first, last) from the current group into rank + * values of the corresponding processes in @p to_group. The + * corresponding rank values are written via the output iterator @c + * out. When there is no correspondence between a rank in the + * current group and a rank in @c to_group, the value @c + * MPI_UNDEFINED is written to the output iterator. + * + * @param first Beginning of the iterator range of ranks in the + * current group. + * + * @param last Past the end of the iterator range of ranks in the + * current group. + * + * @param to_group The group that we are translating ranks to. + * + * @param out The output iterator to which the translated ranks will + * be written. + * + * @returns the output iterator, which points one step past the last + * rank written. + */ + template + OutputIterator translate_ranks(InputIterator first, InputIterator last, + const group& to_group, OutputIterator out); + + /** + * @brief Determines whether the group is non-empty. + * + * @returns True if the group is not empty, false if it is empty. + */ + operator bool() const { return (bool)group_ptr; } + + /** + * @brief Retrieves the underlying @c MPI_Group associated with this + * group. + * + * @returns The @c MPI_Group handle manipulated by this object. If + * this object represents the empty group, returns @c + * MPI_GROUP_EMPTY. + */ + operator MPI_Group() const + { + if (group_ptr) + return *group_ptr; + else + return MPI_GROUP_EMPTY; + } + + /** + * @brief Creates a new group including a subset of the processes + * in the current group. + * + * This routine creates a new @c group which includes only those + * processes in the current group that are listed in the integer + * iterator range @c [first, last). Equivalent to @c + * MPI_Group_incl. + * + * @c first The beginning of the iterator range of ranks to include. + * + * @c last Past the end of the iterator range of ranks to include. + * + * @returns A new group containing those processes with ranks @c + * [first, last) in the current group. + */ + template + group include(InputIterator first, InputIterator last); + + /** + * @brief Creates a new group from all of the processes in the + * current group, exluding a specific subset of the processes. + * + * This routine creates a new @c group which includes all of the + * processes in the current group except those whose ranks are + * listed in the integer iterator range @c [first, + * last). Equivalent to @c MPI_Group_excl. + * + * @c first The beginning of the iterator range of ranks to exclude. + * + * @c last Past the end of the iterator range of ranks to exclude. + * + * @returns A new group containing all of the processes in the + * current group except those processes with ranks @c [first, last) + * in the current group. + */ + template + group exclude(InputIterator first, InputIterator last); + + +protected: + /** + * INTERNAL ONLY + * + * Function object that frees an MPI communicator and deletes the + * memory associated with it. Intended to be used as a deleter with + * shared_ptr. + */ + struct group_free + { + void operator()(MPI_Group* comm) const + { + int finalized; + BOOST_MPI_CHECK_RESULT(MPI_Finalized, (&finalized)); + if (!finalized) + BOOST_MPI_CHECK_RESULT(MPI_Group_free, (comm)); + delete comm; + } + }; + + /** + * The underlying MPI group. This is a shared pointer, so the actual + * MPI group which will be shared among all related instances of the + * @c group class. When there are no more such instances, the group + * will be automatically freed. + */ + shared_ptr group_ptr; +}; + +/** + * @brief Determines whether two process groups are identical. + * + * Equivalent to calling @c MPI_Group_compare and checking whether the + * result is @c MPI_IDENT. + * + * @returns True when the two process groups contain the same + * processes in the same order. + */ +bool operator==(const group& g1, const group& g2); + +/** + * @brief Determines whether two process groups are not identical. + * + * Equivalent to calling @c MPI_Group_compare and checking whether the + * result is not @c MPI_IDENT. + * + * @returns False when the two process groups contain the same + * processes in the same order. + */ +inline bool operator!=(const group& g1, const group& g2) +{ + return !(g1 == g2); +} + +/** + * @brief Computes the union of two process groups. + * + * This routine returns a new @c group that contains all processes + * that are either in group @c g1 or in group @c g2 (or both). The + * processes that are in @c g1 will be first in the resulting group, + * followed by the processes from @c g2 (but not also in @c + * g1). Equivalent to @c MPI_Group_union. + */ +group operator|(const group& g1, const group& g2); + +/** + * @brief Computes the intersection of two process groups. + * + * This routine returns a new @c group that contains all processes + * that are in group @c g1 and in group @c g2, ordered in the same way + * as @c g1. Equivalent to @c MPI_Group_intersection. + */ +group operator&(const group& g1, const group& g2); + +/** + * @brief Computes the difference between two process groups. + * + * This routine returns a new @c group that contains all processes + * that are in group @c g1 but not in group @c g2, ordered in the same way + * as @c g1. Equivalent to @c MPI_Group_difference. + */ +group operator-(const group& g1, const group& g2); + +/************************************************************************ + * Implementation details * + ************************************************************************/ +template +OutputIterator +group::translate_ranks(InputIterator first, InputIterator last, + const group& to_group, OutputIterator out) +{ + std::vector in_array(first, last); + if (in_array.empty()) + return out; + + std::vector out_array(in_array.size()); + BOOST_MPI_CHECK_RESULT(MPI_Group_translate_ranks, + ((MPI_Group)*this, + in_array.size(), + &in_array[0], + (MPI_Group)to_group, + &out_array[0])); + + for (std::vector::size_type i = 0, n = out_array.size(); i < n; ++i) + *out++ = out_array[i]; + return out; +} + +/** + * INTERNAL ONLY + * + * Specialization of translate_ranks that handles the one case where + * we can avoid any memory allocation or copying. + */ +template<> +int* +group::translate_ranks(int* first, int* last, const group& to_group, int* out); + +template +group group::include(InputIterator first, InputIterator last) +{ + if (first == last) + return group(); + + std::vector ranks(first, last); + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_incl, + ((MPI_Group)*this, ranks.size(), &ranks[0], &result)); + return group(result, /*adopt=*/true); +} + +/** + * INTERNAL ONLY + * + * Specialization of group::include that handles the one case where we + * can avoid any memory allocation or copying before creating the + * group. + */ +template<> group group::include(int* first, int* last); + +template +group group::exclude(InputIterator first, InputIterator last) +{ + if (first == last) + return group(); + + std::vector ranks(first, last); + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_excl, + ((MPI_Group)*this, ranks.size(), &ranks[0], &result)); + return group(result, /*adopt=*/true); +} + +/** + * INTERNAL ONLY + * + * Specialization of group::exclude that handles the one case where we + * can avoid any memory allocation or copying before creating the + * group. + */ +template<> group group::exclude(int* first, int* last); + +} } // end namespace boost::mpi + +#endif // BOOST_MPI_GROUP_HPP diff --git a/src/group.cpp b/src/group.cpp new file mode 100644 index 0000000..34afb15 --- /dev/null +++ b/src/group.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2007 Trustees of Indiana University + +// Authors: Douglas Gregor +// Andrew Lumsdaine + +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +#include +#include + +namespace boost { namespace mpi { + +group::group(const MPI_Group& in_group, bool adopt) +{ + if (in_group != MPI_GROUP_EMPTY) { + if (adopt) group_ptr.reset(new MPI_Group(in_group), group_free()); + else group_ptr.reset(new MPI_Group(in_group)); + } +} + +group::group(const communicator& comm) +{ + MPI_Group gr; + BOOST_MPI_CHECK_RESULT(MPI_Comm_group, ((MPI_Comm)comm, &gr)); + + if (gr != MPI_GROUP_EMPTY) + group_ptr.reset(new MPI_Group(gr), group_free()); +} + +optional group::rank() const +{ + if (!group_ptr) + return optional(); + + int rank; + BOOST_MPI_CHECK_RESULT(MPI_Group_rank, (*group_ptr, &rank)); + if (rank == MPI_UNDEFINED) + return optional(); + else + return rank; +} + +int group::size() const +{ + if (!group_ptr) + return 0; + + int size; + BOOST_MPI_CHECK_RESULT(MPI_Group_size, (*group_ptr, &size)); + return size; +} + +bool operator==(const group& g1, const group& g2) +{ + int result; + BOOST_MPI_CHECK_RESULT(MPI_Group_compare, + ((MPI_Group)g1, (MPI_Group)g2, &result)); + return result == MPI_IDENT; +} + +group operator|(const group& g1, const group& g2) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_union, + ((MPI_Group)g1, (MPI_Group)g2, &result)); + return group(result, /*adopt=*/true); +} + +group operator&(const group& g1, const group& g2) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_intersection, + ((MPI_Group)g1, (MPI_Group)g2, &result)); + return group(result, /*adopt=*/true); +} + +group operator-(const group& g1, const group& g2) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_difference, + ((MPI_Group)g1, (MPI_Group)g2, &result)); + return group(result, /*adopt=*/true); +} + +template<> +int* +group::translate_ranks(int* first, int* last, const group& to_group, int* out) +{ + BOOST_MPI_CHECK_RESULT(MPI_Group_translate_ranks, + ((MPI_Group)*this, + last-first, + first, + (MPI_Group)to_group, + out)); + return out + (last - first); +} + +template<> group group::include(int* first, int* last) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_incl, + ((MPI_Group)*this, last - first, first, &result)); + return group(result, /*adopt=*/true); +} + +template<> group group::exclude(int* first, int* last) +{ + MPI_Group result; + BOOST_MPI_CHECK_RESULT(MPI_Group_excl, + ((MPI_Group)*this, last - first, first, &result)); + return group(result, /*adopt=*/true); +} + +} } // end namespace boost::mpi