Boost logo

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