|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r85527 - in trunk: boost/mpi boost/mpi/collectives libs/mpi/doc libs/mpi/example libs/mpi/test
From: troyer_at_[hidden]
Date: 2013-08-30 10:30:09
Author: troyer
Date: 2013-08-30 10:30:09 EDT (Fri, 30 Aug 2013)
New Revision: 85527
URL: http://svn.boost.org/trac/boost/changeset/85527
Log:
Various additiond and patches for Boost.MPI
Added:
trunk/boost/mpi/inplace.hpp (contents, props changed)
trunk/libs/mpi/example/global_min.cpp (contents, props changed)
trunk/libs/mpi/example/in_place_global_min.cpp (contents, props changed)
Text files modified:
trunk/boost/mpi/collectives.hpp | 30 +++++++++--
trunk/boost/mpi/collectives/all_reduce.hpp | 31 +++++++++++
trunk/boost/mpi/inplace.hpp | 63 ++++++++++++++++++++++++
trunk/libs/mpi/doc/Jamfile.v2 | 1
trunk/libs/mpi/doc/mpi.qbk | 66 +++++++++++++++++++++++++
trunk/libs/mpi/example/global_min.cpp | 31 ++++++++++++
trunk/libs/mpi/example/in_place_global_min.cpp | 29 +++++++++++
trunk/libs/mpi/test/all_reduce_test.cpp | 102 +++++++++++++++++++++++++++++++++++++--
8 files changed, 335 insertions(+), 18 deletions(-)
Modified: trunk/boost/mpi/collectives.hpp
==============================================================================
--- trunk/boost/mpi/collectives.hpp Fri Aug 30 09:18:37 2013 (r85526)
+++ trunk/boost/mpi/collectives.hpp 2013-08-30 10:30:09 EDT (Fri, 30 Aug 2013) (r85527)
@@ -19,10 +19,10 @@
#define BOOST_MPI_COLLECTIVES_HPP
#include <boost/mpi/communicator.hpp>
+#include <boost/mpi/inplace.hpp>
#include <vector>
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<typename T, typename Op>
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<typename T, typename Op>
+void
+all_reduce(const communicator& comm, const T& value, T& out_value, Op op);
/**
* \overload
*/
template<typename T, typename Op>
-T all_reduce(const communicator& comm, const T& in_value, Op op);
+T all_reduce(const communicator& comm, const T& value, Op op);
/**
* \overload
*/
template<typename T, typename Op>
void
-all_reduce(const communicator& comm, const T* in_values, int n, T* out_values,
+all_reduce(const communicator& comm, inplace_t<T*> value, int n,
Op op);
+/**
+ * \overload
+ */
+template<typename T, typename Op>
+void
+all_reduce(const communicator& comm, inplace_t<T> value, Op op);
/**
* @brief Send data from every process to every other process.
Modified: trunk/boost/mpi/collectives/all_reduce.hpp
==============================================================================
--- trunk/boost/mpi/collectives/all_reduce.hpp Fri Aug 30 09:18:37 2013 (r85526)
+++ trunk/boost/mpi/collectives/all_reduce.hpp 2013-08-30 10:30:09 EDT (Fri, 30 Aug 2013) (r85527)
@@ -12,12 +12,15 @@
#ifndef BOOST_MPI_ALL_REDUCE_HPP
#define BOOST_MPI_ALL_REDUCE_HPP
+#include <vector>
+
+#include <boost/mpi/inplace.hpp>
+
// All-reduce falls back to reduce() + broadcast() in some cases.
#include <boost/mpi/collectives/broadcast.hpp>
#include <boost/mpi/collectives/reduce.hpp>
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<T> 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<typename T, typename Op>
inline void
+all_reduce(const communicator& comm, inplace_t<T*> inout_values, int n, Op op)
+{
+ all_reduce(comm, static_cast<const T*>(MPI_IN_PLACE), n, inout_values.buffer, op);
+}
+
+template<typename T, typename Op>
+inline void
+all_reduce(const communicator& comm, inplace_t<T> inout_values, Op op)
+{
+ all_reduce(comm, static_cast<const T*>(MPI_IN_PLACE), 1, &(inout_values.buffer), op);
+}
+
+template<typename T, typename Op>
+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,
Added: trunk/boost/mpi/inplace.hpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/boost/mpi/inplace.hpp 2013-08-30 10:30:09 EDT (Fri, 30 Aug 2013) (r85527)
@@ -0,0 +1,63 @@
+// Copyright (C) 2005-2006 Alain Miniussi <alain.miniussi -at- oca.eu>.
+
+// 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 <boost/mpi/communicator.hpp>
+#include <vector>
+
+namespace boost { namespace mpi {
+
+/**
+ * @brief Wrapper type to explicitly indicate that a input data
+ * can be overriden with an output value.
+ */
+template <typename T>
+struct inplace_t {
+ inplace_t(T& inout) : buffer(inout) {}
+ T& buffer;
+};
+
+template <typename T>
+struct inplace_t<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<typename T>
+inplace_t<T>
+inplace(T& inout) {
+ return inplace_t<T>(inout);
+}
+/**
+ * \overload
+ */
+template<typename T>
+inplace_t<T*>
+inplace(T* inout) {
+ return inplace_t<T*>(inout);
+}
+} } // end namespace boost::mpi
+
+#endif // BOOST_MPI_INPLACE_HPP
+
Modified: trunk/libs/mpi/doc/Jamfile.v2
==============================================================================
--- trunk/libs/mpi/doc/Jamfile.v2 Fri Aug 30 09:18:37 2013 (r85526)
+++ trunk/libs/mpi/doc/Jamfile.v2 2013-08-30 10:30:09 EDT (Fri, 30 Aug 2013) (r85527)
@@ -31,6 +31,7 @@
../../../boost/mpi/status.hpp
../../../boost/mpi/request.hpp
../../../boost/mpi/timer.hpp
+ ../../../boost/mpi/inplace.hpp
../../../boost/mpi/python.hpp
]
: <doxygen:param>MACRO_EXPANSION=YES
Modified: trunk/libs/mpi/doc/mpi.qbk
==============================================================================
--- trunk/libs/mpi/doc/mpi.qbk Fri Aug 30 09:18:37 2013 (r85526)
+++ trunk/libs/mpi/doc/mpi.qbk 2013-08-30 10:30:09 EDT (Fri, 30 Aug 2013) (r85527)
@@ -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]
@@ -743,7 +743,7 @@
function object. For instance, we can randomly generate values in each
process and the compute the minimum value over all processes via a
call to [funcref boost::mpi::reduce `reduce`]
-(`random_min.cpp`)::
+(`random_min.cpp`):
#include <boost/mpi.hpp>
#include <iostream>
@@ -862,6 +862,64 @@
processes. This variant is useful, for instance, in establishing
global minimum or maximum values.
+The following code (`global_min.cpp`) shows a broadcasting version of
+the `random_min.cpp` example:
+
+ #include <boost/mpi.hpp>
+ #include <iostream>
+ #include <cstdlib>
+ namespace mpi = boost::mpi;
+
+ int main(int argc, char* argv[])
+ {
+ mpi::environment env(argc, argv);
+ mpi::communicator world;
+
+ std::srand(world.rank());
+ int my_number = std::rand();
+ int minimum;
+
+ all_reduce(world, my_number, minimum, mpi::minimum<int>());
+
+ if (world.rank() == 0) {
+ std::cout << "The minimum value is " << minimum << std::endl;
+ }
+
+ return 0;
+ }
+
+In that example we provide both input and output values, requiring
+twice as much space, which can be a problem depending on the size
+of the transmitted data.
+If there is no need to preserve the input value, the ouput value
+can be omitted. In that case the input value will be overriden with
+the output value and Boost.MPI is able, in some situation, to implement
+the operation with a more space efficient solution (using the `MPI_IN_PLACE`
+flag of the MPI C mapping), as in the following example (`in_place_global_min.cpp`):
+
+ #include <boost/mpi.hpp>
+ #include <iostream>
+ #include <cstdlib>
+ namespace mpi = boost::mpi;
+
+ int main(int argc, char* argv[])
+ {
+ mpi::environment env(argc, argv);
+ mpi::communicator world;
+
+ std::srand(world.rank());
+ int my_number = std::rand();
+
+ all_reduce(world, my_number, mpi::minimum<int>());
+
+ if (world.rank() == 0) {
+ std::cout << "The minimum value is " << my_number << std::endl;
+ }
+
+ return 0;
+ }
+
+
[endsect]
[endsect]
@@ -1413,6 +1471,10 @@
[[[@http://www.mpi-forum.org/docs/mpi-11-html/node71.html#Node71
`MPI_Scatterv`]] [most uses supported by [funcref boost::mpi::scatter `scatter`]]]
+
+ [[[@http://www.mpi-forum.org/docs/mpi-20-html/node145.htm#Node145
+`MPI_IN_PLACE`]] [supported implicitly by [funcref boost::mpi::all_reduce
+`all_reduce` by omiting the ouput value]]]
]
Boost.MPI uses function objects to specify how reductions should occur
Added: trunk/libs/mpi/example/global_min.cpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/libs/mpi/example/global_min.cpp 2013-08-30 10:30:09 EDT (Fri, 30 Aug 2013) (r85527)
@@ -0,0 +1,31 @@
+// Copyright (C) 2013 Alain Miniussi <alain.miniussi_at_oca.eu>
+
+// 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)
+
+// An example using Boost.MPI's all_reduce() that compute the minimum
+// of each process's value and broadcast the result to all the processes.
+
+#include <boost/mpi.hpp>
+#include <iostream>
+#include <cstdlib>
+namespace mpi = boost::mpi;
+
+int main(int argc, char* argv[])
+{
+ mpi::environment env(argc, argv);
+ mpi::communicator world;
+
+ std::srand(world.rank());
+ int my_number = std::rand();
+ int minimum;
+
+ all_reduce(world, my_number, minimum, mpi::minimum<int>());
+
+ if (world.rank() == 0) {
+ std::cout << "The minimum value is " << minimum << std::endl;
+ }
+
+ return 0;
+}
Added: trunk/libs/mpi/example/in_place_global_min.cpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/libs/mpi/example/in_place_global_min.cpp 2013-08-30 10:30:09 EDT (Fri, 30 Aug 2013) (r85527)
@@ -0,0 +1,29 @@
+// Copyright (C) 2013 Alain Miniussi <alain.miniussi_at_oca.eu>
+
+// 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)
+
+// An example using Boost.MPI's all_reduce() that compute the minimum
+// of each process's value and broadcast the result to all the processes.
+#include <boost/mpi.hpp>
+#include <iostream>
+#include <cstdlib>
+namespace mpi = boost::mpi;
+
+int main(int argc, char* argv[])
+{
+ mpi::environment env(argc, argv);
+ mpi::communicator world;
+
+ std::srand(world.rank());
+ int my_number = std::rand();
+
+ all_reduce(world, my_number, mpi::minimum<int>());
+
+ if (world.rank() == 0) {
+ std::cout << "The minimum value is " << my_number << std::endl;
+ }
+
+ return 0;
+}
Modified: trunk/libs/mpi/test/all_reduce_test.cpp
==============================================================================
--- trunk/libs/mpi/test/all_reduce_test.cpp Fri Aug 30 09:18:37 2013 (r85526)
+++ trunk/libs/mpi/test/all_reduce_test.cpp 2013-08-30 10:30:09 EDT (Fri, 30 Aug 2013) (r85527)
@@ -9,6 +9,7 @@
#include <boost/mpi/communicator.hpp>
#include <boost/mpi/environment.hpp>
#include <boost/test/minimal.hpp>
+#include <vector>
#include <algorithm>
#include <boost/serialization/string.hpp>
#include <boost/iterator/counting_iterator.hpp>
@@ -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<typename Generator, typename Op>
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<value_type> generated_values;
for (int p = 0; p < comm.size(); ++p)
@@ -97,6 +115,69 @@
(comm.barrier)();
}
+template<typename Generator, typename Op>
+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<value_type> 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<value_type> result;
+ if (in_place) {
+ all_reduce(comm, inplace(&(send[0])), send.size(), op);
+ result.swap(send);
+ } else {
+ std::vector<value_type> recv(10, value_type());
+ all_reduce(comm, &(send[0]), send.size(), &(recv[0]), op);
+ result.swap(recv);
+ }
+
+ // Compute expected result
+ std::vector<value_type> 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<typename Generator, typename Op>
+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<int>(), "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<wrapped_int>(), "sum", wrapped_int(0));
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk