Index: boost/mpi/inplace.hpp =================================================================== --- boost/mpi/inplace.hpp (revision 0) +++ boost/mpi/inplace.hpp (revision 0) @@ -0,0 +1,63 @@ +// 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. + * @param inout the contributing input value, it will be overriden + * with the output value where one is expected. If it is a pointer, + * the number of elements will be provided separatly. + * @returns The wrapped value or pointer. + */ +template +inplace_t +inplace(T& inout) { + return inplace_t(inout); +} +/** + * \overload + */ +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 85156) +++ 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. @@ -94,12 +94,15 @@ * * @param comm The communicator over which the reduction will * occur. - * - * @param in_value The local value to be combined with the local + * @param value The local value to be combined with the local * values of every other process. For reducing arrays, @c in_values * is a pointer to the local values to be reduced and @c n is the * number of values to reduce. See @c reduce for more information. * + * If wrapped in a @c inplace_t object, combine the usage of both + * input and $c out_value and the local value will be overwritten + * (a convenience function @c inplace is provided for the wrapping). + * * @param out_value Will receive the result of the reduction * operation. If this parameter is omitted, the outgoing value will * instead be returned. @@ -116,26 +119,39 @@ * gives the implementation additional lattitude to optimize the * reduction operation. * + * @param n Indicated the size of the buffers of array type. * @returns If no @p out_value parameter is supplied, returns the * result of the reduction operation. */ template void -all_reduce(const communicator& comm, const T& in_value, T& out_value, Op op); - +all_reduce(const communicator& comm, const T* value, int n, T* out_value, + Op op); /** * \overload */ template -T all_reduce(const communicator& comm, const T& in_value, Op op); +void +all_reduce(const communicator& comm, const T& value, T& out_value, Op op); +/** + * \overload + */ +template +T all_reduce(const communicator& comm, const T& value, Op op); /** * \overload */ template void -all_reduce(const communicator& comm, const T* in_values, int n, T* out_values, +all_reduce(const communicator& comm, inplace_t value, int n, Op op); +/** + * \overload + */ +template +void +all_reduce(const communicator& comm, inplace_t value, Op op); /** * @brief Send data from every process to every other process. Index: boost/mpi/collectives/all_reduce.hpp =================================================================== --- boost/mpi/collectives/all_reduce.hpp (revision 85156) +++ 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 85156) +++ 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 85156) +++ 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] Index: libs/mpi/doc/Jamfile.v2 =================================================================== --- libs/mpi/doc/Jamfile.v2 (revision 85156) +++ libs/mpi/doc/Jamfile.v2 (working copy) @@ -31,6 +31,7 @@ ../../../boost/mpi/status.hpp ../../../boost/mpi/request.hpp ../../../boost/mpi/timer.hpp + ../../../boost/mpi/inplace.hpp ../../../boost/mpi/python.hpp ] : MACRO_EXPANSION=YES Property changes on: tools/boostbook/setup_boostbook.sh ___________________________________________________________________ Added: svn:executable + *