Index: boost/mpi/inplace.hpp =================================================================== --- boost/mpi/inplace.hpp (revision 0) +++ boost/mpi/inplace.hpp (revision 0) @@ -0,0 +1,57 @@ +// Copyright (C) 2005-2006 Alain Miniussi . + +// 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) + +// Message Passing Interface 1.1 -- Section 4. MPI Collectives + +/** @file inplace.hpp + * + * This header provides helpers to indicate to MPI collective operation + * that a buffer can be use both as an input and output. + */ +#ifndef BOOST_MPI_INPLACE_HPP +#define BOOST_MPI_INPLACE_HPP + +#include +#include + +namespace boost { namespace mpi { + +/** + * @brief Wrapper type to explicitly indicate that a input data + * can be overriden with an output value. + */ +template +struct inplace_t { + inplace_t(T& inout) : buffer(inout) {} + T& buffer; +}; + +template +struct inplace_t { + inplace_t(T* inout) : buffer(inout) {} + T* buffer; +}; + + +/** + * @brief Wrapp a input data to indicate that it can be overriden + * with an ouput value. + */ +template +inplace_t +inplace(T& inout) { + return inplace_t(inout); +} + +template +inplace_t +inplace(T* inout) { + return inplace_t(inout); +} +} } // end namespace boost::mpi + +#endif // BOOST_MPI_INPLACE_HPP + Index: boost/mpi/collectives.hpp =================================================================== --- boost/mpi/collectives.hpp (revision 82509) +++ boost/mpi/collectives.hpp (working copy) @@ -19,10 +19,10 @@ #define BOOST_MPI_COLLECTIVES_HPP #include +#include #include namespace boost { namespace mpi { - /** * @brief Gather the values stored at every process into vectors of * values from each process. @@ -104,6 +104,11 @@ * operation. If this parameter is omitted, the outgoing value will * instead be returned. * + * @param inout_value Combine the usage of both in_value and out_value. + * Contain in input the local value to be combined with the local + * values of every other process. Will also receive the result of the + * reduction operation. Local value will be overwritten. + * * @param op The binary operation that combines two values of type * @c T and returns a third value of type @c T. For types @c T that has * ssociated MPI data types, @c op will either be translated into @@ -138,6 +143,14 @@ Op op); /** + * \overload + */ +template +void +all_reduce(const communicator& comm, inplace_t inout_values, int n, + Op op); + +/** * @brief Send data from every process to every other process. * * @c all_to_all is a collective algorithm that transmits @c p values Index: boost/mpi/collectives/all_reduce.hpp =================================================================== --- boost/mpi/collectives/all_reduce.hpp (revision 82509) +++ boost/mpi/collectives/all_reduce.hpp (working copy) @@ -12,12 +12,15 @@ #ifndef BOOST_MPI_ALL_REDUCE_HPP #define BOOST_MPI_ALL_REDUCE_HPP +#include + +#include + // All-reduce falls back to reduce() + broadcast() in some cases. #include #include namespace boost { namespace mpi { - namespace detail { /********************************************************************** * Simple reduction with MPI_Allreduce * @@ -67,7 +70,17 @@ T* out_values, Op op, mpl::false_ /*is_mpi_op*/, mpl::false_ /*is_mpi_datatype*/) { - reduce(comm, in_values, n, out_values, op, 0); + if (in_values == MPI_IN_PLACE) { + // if in_values matches the in place tag, then the output + // buffer actually contains the input data. + // But we can just go back to the out of place + // implementation in this case. + // it's not clear how/if we can avoid the copy. + std::vector tmp_in( out_values, out_values + n); + reduce(comm, &(tmp_in[0]), n, out_values, op, 0); + } else { + reduce(comm, in_values, n, out_values, op, 0); + } broadcast(comm, out_values, n, 0); } } // end namespace detail @@ -83,6 +96,20 @@ template inline void +all_reduce(const communicator& comm, inplace_t inout_values, int n, Op op) +{ + all_reduce(comm, static_cast(MPI_IN_PLACE), n, inout_values.buffer, op); +} + +template +inline void +all_reduce(const communicator& comm, inplace_t inout_values, Op op) +{ + all_reduce(comm, static_cast(MPI_IN_PLACE), 1, &(inout_values.buffer), op); +} + +template +inline void all_reduce(const communicator& comm, const T& in_value, T& out_value, Op op) { detail::all_reduce_impl(comm, &in_value, 1, &out_value, op, Index: libs/mpi/test/all_reduce_test.cpp =================================================================== --- libs/mpi/test/all_reduce_test.cpp (revision 82509) +++ libs/mpi/test/all_reduce_test.cpp (working copy) @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,16 @@ return point(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z); } +// test lexical order +bool operator<(const point& p1, const point& p2) +{ + return (p1.x < p2.x + ? true + : (p1.x > p2.x + ? false + : p1.y < p2.y )); +} + namespace boost { namespace mpi { template <> @@ -67,22 +78,29 @@ template void -all_reduce_test(const communicator& comm, Generator generator, - const char* type_kind, Op op, const char* op_kind, - typename Generator::result_type init) +all_reduce_one_test(const communicator& comm, Generator generator, + const char* type_kind, Op op, const char* op_kind, + typename Generator::result_type init, bool in_place) { typedef typename Generator::result_type value_type; value_type value = generator(comm.rank()); using boost::mpi::all_reduce; + using boost::mpi::inplace; if (comm.rank() == 0) { std::cout << "Reducing to " << op_kind << " of " << type_kind << "..."; std::cout.flush(); } - value_type result_value = all_reduce(comm, value, op); - + value_type result_value; + if (in_place) { + all_reduce(comm, inplace(value), op); + result_value = value; + } else { + result_value = all_reduce(comm, value, op); + } + // Compute expected result std::vector generated_values; for (int p = 0; p < comm.size(); ++p) @@ -97,6 +115,69 @@ (comm.barrier)(); } +template +void +all_reduce_array_test(const communicator& comm, Generator generator, + const char* type_kind, Op op, const char* op_kind, + typename Generator::result_type init, bool in_place) +{ + typedef typename Generator::result_type value_type; + value_type value = generator(comm.rank()); + std::vector send(10, value); + + using boost::mpi::all_reduce; + using boost::mpi::inplace; + + if (comm.rank() == 0) { + char const* place = in_place ? "in place" : "out of place"; + std::cout << "Reducing (" << place << ") array to " << op_kind << " of " << type_kind << "..."; + std::cout.flush(); + } + std::vector result; + if (in_place) { + all_reduce(comm, inplace(&(send[0])), send.size(), op); + result.swap(send); + } else { + std::vector recv(10, value_type()); + all_reduce(comm, &(send[0]), send.size(), &(recv[0]), op); + result.swap(recv); + } + + // Compute expected result + std::vector generated_values; + for (int p = 0; p < comm.size(); ++p) + generated_values.push_back(generator(p)); + value_type expected_result = std::accumulate(generated_values.begin(), + generated_values.end(), + init, op); + + bool got_expected_result = (std::equal_range(result.begin(), result.end(), + expected_result) + == std::make_pair(result.begin(), result.end())); + BOOST_CHECK(got_expected_result); + if (got_expected_result && comm.rank() == 0) + std::cout << "OK." << std::endl; + + (comm.barrier)(); +} + +// Test the 4 families of all reduce: (value, array) X (in place, out of place) +template +void +all_reduce_test(const communicator& comm, Generator generator, + const char* type_kind, Op op, const char* op_kind, + typename Generator::result_type init) +{ + const bool in_place = true; + const bool out_of_place = false; + all_reduce_one_test(comm, generator, type_kind, op, op_kind, init, in_place); + all_reduce_one_test(comm, generator, type_kind, op, op_kind, init, out_of_place); + all_reduce_array_test(comm, generator, type_kind, op, op_kind, + init, in_place); + all_reduce_array_test(comm, generator, type_kind, op, op_kind, + init, out_of_place); +} + // Generates integers to test with all_reduce() struct int_generator { @@ -168,6 +249,11 @@ return x.value == y.value; } +bool operator<(const wrapped_int& x, const wrapped_int& y) +{ + return x.value < y.value; +} + // Generates wrapped_its to test with all_reduce() struct wrapped_int_generator { @@ -196,6 +282,8 @@ environment env(argc, argv); communicator comm; + const bool in_place = true; + const bool out_of_place = false; // Built-in MPI datatypes with built-in MPI operations all_reduce_test(comm, int_generator(), "integers", std::plus(), "sum", @@ -215,8 +303,8 @@ // Built-in MPI datatypes with user-defined operations all_reduce_test(comm, int_generator(17), "integers", secret_int_bit_and(), "bitwise and", -1); - - // Arbitrary types with user-defined, commutative operations. + + // Arbitrary types with user-defined, commutative operations. all_reduce_test(comm, wrapped_int_generator(17), "wrapped integers", std::plus(), "sum", wrapped_int(0)); Index: libs/mpi/doc/mpi.qbk =================================================================== --- libs/mpi/doc/mpi.qbk (revision 82509) +++ libs/mpi/doc/mpi.qbk (working copy) @@ -2,7 +2,7 @@ [authors [Gregor, Douglas], [Troyer, Matthias] ] [copyright 2005 2006 2007 Douglas Gregor, Matthias Troyer, Trustees of Indiana University] [purpose - An generic, user-friendly interface to MPI, the Message + A generic, user-friendly interface to MPI, the Message Passing Interface. ] [id mpi]