Ticket #6704: mpi_all_red-r85156-6704.patch

File mpi_all_red-r85156-6704.patch, 13.0 KB (added by alain.miniussi@…, 9 years ago)

patch with tests and documentation

  • boost/mpi/inplace.hpp

     
     1// Copyright (C) 2005-2006 Alain Miniussi <alain.miniussi -at- oca.eu>.
     2
     3// Use, modification and distribution is subject to the Boost Software
     4// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
     5// http://www.boost.org/LICENSE_1_0.txt)
     6
     7// Message Passing Interface 1.1 -- Section 4. MPI Collectives
     8
     9/** @file inplace.hpp
     10 *
     11 *  This header provides helpers to indicate to MPI collective operation
     12 *  that a buffer can be use both as an input and output.
     13 */
     14#ifndef BOOST_MPI_INPLACE_HPP
     15#define BOOST_MPI_INPLACE_HPP
     16
     17#include <boost/mpi/communicator.hpp>
     18#include <vector>
     19
     20namespace boost { namespace mpi {
     21
     22/**
     23 *  @brief Wrapper type to explicitly indicate that a input data
     24 * can be overriden with an output value.
     25 */
     26template <typename T>
     27struct inplace_t {
     28  inplace_t(T& inout) : buffer(inout) {}
     29  T& buffer;
     30};
     31
     32template <typename T>
     33struct inplace_t<T*> {
     34  inplace_t(T* inout) : buffer(inout) {}
     35  T* buffer;
     36};
     37
     38
     39/**
     40 *  @brief Wrapp a input data to indicate that it can be overriden
     41 *  with an ouput value.
     42 *  @param inout the contributing input value, it will be overriden
     43 *  with the output value where one is expected. If it is a pointer,
     44 *  the number of elements will be provided separatly.
     45 *  @returns The wrapped value or pointer.
     46 */
     47template<typename T>
     48inplace_t<T>
     49inplace(T& inout) {
     50  return inplace_t<T>(inout);
     51}
     52/**
     53 * \overload
     54 */
     55template<typename T>
     56inplace_t<T*>
     57inplace(T* inout) {
     58  return inplace_t<T*>(inout);
     59}
     60} }  // end namespace boost::mpi
     61
     62#endif // BOOST_MPI_INPLACE_HPP
     63
  • boost/mpi/collectives.hpp

     
    1919#define BOOST_MPI_COLLECTIVES_HPP
    2020
    2121#include <boost/mpi/communicator.hpp>
     22#include <boost/mpi/inplace.hpp>
    2223#include <vector>
    2324
    2425namespace boost { namespace mpi {
    25 
    2626/**
    2727 *  @brief Gather the values stored at every process into vectors of
    2828 *  values from each process.
     
    9494 *
    9595 *    @param comm The communicator over which the reduction will
    9696 *    occur.
    97  *
    98  *    @param in_value The local value to be combined with the local
     97 *    @param value The local value to be combined with the local
    9998 *    values of every other process. For reducing arrays, @c in_values
    10099 *    is a pointer to the local values to be reduced and @c n is the
    101100 *    number of values to reduce. See @c reduce for more information.
    102101 *
     102 *    If wrapped in a @c inplace_t object, combine the usage of both
     103 *    input and $c out_value and the local value will be overwritten
     104 *    (a convenience function @c inplace is provided for the wrapping).
     105 *
    103106 *    @param out_value Will receive the result of the reduction
    104107 *    operation. If this parameter is omitted, the outgoing value will
    105108 *    instead be returned.
     
    116119 *    gives the implementation additional lattitude to optimize the
    117120 *    reduction operation.
    118121 *
     122 *    @param n Indicated the size of the buffers of array type.
    119123 *    @returns If no @p out_value parameter is supplied, returns the
    120124 *    result of the reduction operation.
    121125 */
    122126template<typename T, typename Op>
    123127void
    124 all_reduce(const communicator& comm, const T& in_value, T& out_value, Op op);
    125 
     128all_reduce(const communicator& comm, const T* value, int n, T* out_value,
     129           Op op);
    126130/**
    127131 * \overload
    128132 */
    129133template<typename T, typename Op>
    130 T all_reduce(const communicator& comm, const T& in_value, Op op);
     134void
     135all_reduce(const communicator& comm, const T& value, T& out_value, Op op);
     136/**
     137 * \overload
     138 */
     139template<typename T, typename Op>
     140T all_reduce(const communicator& comm, const T& value, Op op);
    131141
    132142/**
    133143 * \overload
    134144 */
    135145template<typename T, typename Op>
    136146void
    137 all_reduce(const communicator& comm, const T* in_values, int n, T* out_values,
     147all_reduce(const communicator& comm, inplace_t<T*> value, int n,
    138148           Op op);
     149/**
     150 * \overload
     151 */
     152template<typename T, typename Op>
     153void
     154all_reduce(const communicator& comm, inplace_t<T> value, Op op);
    139155
    140156/**
    141157 *  @brief Send data from every process to every other process.
  • boost/mpi/collectives/all_reduce.hpp

     
    1212#ifndef BOOST_MPI_ALL_REDUCE_HPP
    1313#define BOOST_MPI_ALL_REDUCE_HPP
    1414
     15#include <vector>
     16
     17#include <boost/mpi/inplace.hpp>
     18
    1519// All-reduce falls back to reduce() + broadcast() in some cases.
    1620#include <boost/mpi/collectives/broadcast.hpp>
    1721#include <boost/mpi/collectives/reduce.hpp>
    1822
    1923namespace boost { namespace mpi {
    20 
    2124namespace detail {
    2225  /**********************************************************************
    2326   * Simple reduction with MPI_Allreduce                                *
     
    6770                  T* out_values, Op op, mpl::false_ /*is_mpi_op*/,
    6871                  mpl::false_ /*is_mpi_datatype*/)
    6972  {
    70     reduce(comm, in_values, n, out_values, op, 0);
     73    if (in_values == MPI_IN_PLACE) {
     74      // if in_values matches the in place tag, then the output
     75      // buffer actually contains the input data.
     76      // But we can just go back to the out of place
     77      // implementation in this case.
     78      // it's not clear how/if we can avoid the copy.
     79      std::vector<T> tmp_in( out_values, out_values + n);
     80      reduce(comm, &(tmp_in[0]), n, out_values, op, 0);
     81    } else {
     82      reduce(comm, in_values, n, out_values, op, 0);
     83    }
    7184    broadcast(comm, out_values, n, 0);
    7285  }
    7386} // end namespace detail
     
    8396
    8497template<typename T, typename Op>
    8598inline void
     99all_reduce(const communicator& comm, inplace_t<T*> inout_values, int n, Op op)
     100{
     101  all_reduce(comm, static_cast<const T*>(MPI_IN_PLACE), n, inout_values.buffer, op);
     102}
     103
     104template<typename T, typename Op>
     105inline void
     106all_reduce(const communicator& comm, inplace_t<T> inout_values, Op op)
     107{
     108  all_reduce(comm, static_cast<const T*>(MPI_IN_PLACE), 1, &(inout_values.buffer), op);
     109}
     110
     111template<typename T, typename Op>
     112inline void
    86113all_reduce(const communicator& comm, const T& in_value, T& out_value, Op op)
    87114{
    88115  detail::all_reduce_impl(comm, &in_value, 1, &out_value, op,
  • libs/mpi/test/all_reduce_test.cpp

     
    99#include <boost/mpi/communicator.hpp>
    1010#include <boost/mpi/environment.hpp>
    1111#include <boost/test/minimal.hpp>
     12#include <vector>
    1213#include <algorithm>
    1314#include <boost/serialization/string.hpp>
    1415#include <boost/iterator/counting_iterator.hpp>
     
    5859  return point(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z);
    5960}
    6061
     62// test lexical order
     63bool operator<(const point& p1, const point& p2)
     64{
     65  return (p1.x < p2.x
     66          ? true
     67          : (p1.x > p2.x
     68             ? false
     69             : p1.y < p2.y ));
     70}
     71
    6172namespace boost { namespace mpi {
    6273
    6374  template <>
     
    6778
    6879template<typename Generator, typename Op>
    6980void
    70 all_reduce_test(const communicator& comm, Generator generator,
    71                 const char* type_kind, Op op, const char* op_kind,
    72                 typename Generator::result_type init)
     81all_reduce_one_test(const communicator& comm, Generator generator,
     82                    const char* type_kind, Op op, const char* op_kind,
     83                    typename Generator::result_type init, bool in_place)
    7384{
    7485  typedef typename Generator::result_type value_type;
    7586  value_type value = generator(comm.rank());
    7687
    7788  using boost::mpi::all_reduce;
     89  using boost::mpi::inplace;
    7890
    7991  if (comm.rank() == 0) {
    8092    std::cout << "Reducing to " << op_kind << " of " << type_kind << "...";
    8193    std::cout.flush();
    8294  }
    8395
    84   value_type result_value = all_reduce(comm, value, op);
    85 
     96  value_type result_value;
     97  if (in_place) {
     98    all_reduce(comm, inplace(value), op);
     99    result_value = value;
     100  } else {
     101    result_value = all_reduce(comm, value, op);
     102  }
     103 
    86104  // Compute expected result
    87105  std::vector<value_type> generated_values;
    88106  for (int p = 0; p < comm.size(); ++p)
     
    97115  (comm.barrier)();
    98116}
    99117
     118template<typename Generator, typename Op>
     119void
     120all_reduce_array_test(const communicator& comm, Generator generator,
     121                      const char* type_kind, Op op, const char* op_kind,
     122                      typename Generator::result_type init, bool in_place)
     123{
     124  typedef typename Generator::result_type value_type;
     125  value_type value = generator(comm.rank());
     126  std::vector<value_type> send(10, value);
     127
     128  using boost::mpi::all_reduce;
     129  using boost::mpi::inplace;
     130
     131  if (comm.rank() == 0) {
     132      char const* place = in_place ? "in place" : "out of place";
     133      std::cout << "Reducing (" << place << ") array to " << op_kind << " of " << type_kind << "...";
     134      std::cout.flush();
     135  }
     136  std::vector<value_type> result;
     137  if (in_place) {
     138    all_reduce(comm, inplace(&(send[0])), send.size(), op);
     139    result.swap(send);
     140  } else {
     141    std::vector<value_type> recv(10, value_type());
     142    all_reduce(comm, &(send[0]), send.size(), &(recv[0]), op);
     143    result.swap(recv);
     144  }
     145
     146  // Compute expected result
     147  std::vector<value_type> generated_values;
     148  for (int p = 0; p < comm.size(); ++p)
     149    generated_values.push_back(generator(p));
     150  value_type expected_result = std::accumulate(generated_values.begin(),
     151                                               generated_values.end(),
     152                                               init, op);
     153 
     154  bool got_expected_result = (std::equal_range(result.begin(), result.end(),
     155                                               expected_result)
     156                              == std::make_pair(result.begin(), result.end()));
     157  BOOST_CHECK(got_expected_result);
     158  if (got_expected_result && comm.rank() == 0)
     159      std::cout << "OK." << std::endl;
     160
     161  (comm.barrier)();
     162}
     163
     164// Test the 4 families of all reduce: (value, array) X (in place, out of place)
     165template<typename Generator, typename Op>
     166void
     167all_reduce_test(const communicator& comm, Generator generator,
     168                const char* type_kind, Op op, const char* op_kind,
     169                typename Generator::result_type init)
     170{
     171  const bool in_place = true;
     172  const bool out_of_place = false;
     173  all_reduce_one_test(comm, generator, type_kind, op, op_kind,  init, in_place);
     174  all_reduce_one_test(comm, generator, type_kind, op, op_kind,  init, out_of_place);
     175  all_reduce_array_test(comm, generator, type_kind, op, op_kind,
     176                        init, in_place);
     177  all_reduce_array_test(comm, generator, type_kind, op, op_kind,
     178                        init, out_of_place);
     179}
     180
    100181// Generates integers to test with all_reduce()
    101182struct int_generator
    102183{
     
    168249  return x.value == y.value;
    169250}
    170251
     252bool operator<(const wrapped_int& x, const wrapped_int& y)
     253{
     254  return x.value < y.value;
     255}
     256
    171257// Generates wrapped_its to test with all_reduce()
    172258struct wrapped_int_generator
    173259{
     
    196282  environment env(argc, argv);
    197283
    198284  communicator comm;
     285  const bool in_place = true;
     286  const bool out_of_place = false;
    199287
    200288  // Built-in MPI datatypes with built-in MPI operations
    201289  all_reduce_test(comm, int_generator(), "integers", std::plus<int>(), "sum",
     
    215303  // Built-in MPI datatypes with user-defined operations
    216304  all_reduce_test(comm, int_generator(17), "integers", secret_int_bit_and(),
    217305                  "bitwise and", -1);
    218 
    219     // Arbitrary types with user-defined, commutative operations.
     306 
     307  // Arbitrary types with user-defined, commutative operations.
    220308  all_reduce_test(comm, wrapped_int_generator(17), "wrapped integers",
    221309                  std::plus<wrapped_int>(), "sum", wrapped_int(0));
    222310
  • libs/mpi/doc/mpi.qbk

     
    22    [authors [Gregor, Douglas], [Troyer, Matthias] ]
    33    [copyright 2005 2006 2007 Douglas Gregor, Matthias Troyer, Trustees of Indiana University]
    44    [purpose
    5         An generic, user-friendly interface to MPI, the Message
     5        A generic, user-friendly interface to MPI, the Message
    66        Passing Interface.
    77    ]
    88    [id mpi]
  • libs/mpi/doc/Jamfile.v2

     
    3131    ../../../boost/mpi/status.hpp
    3232    ../../../boost/mpi/request.hpp
    3333    ../../../boost/mpi/timer.hpp
     34    ../../../boost/mpi/inplace.hpp
    3435    ../../../boost/mpi/python.hpp
    3536    ]
    3637  : <doxygen:param>MACRO_EXPANSION=YES