• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2005, 2006 Douglas Gregor <doug.gregor -at- gmail.com>.
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 // A test of the reduce() collective.
8 #include <boost/mpi/collectives/reduce.hpp>
9 #include <boost/mpi/communicator.hpp>
10 #include <boost/mpi/environment.hpp>
11 #include <algorithm>
12 #include <boost/serialization/string.hpp>
13 #include <boost/iterator/counting_iterator.hpp>
14 #include <boost/lexical_cast.hpp>
15 #include <numeric>
16 
17 #define BOOST_TEST_MODULE mpi_reduce_test
18 #include <boost/test/included/unit_test.hpp>
19 
20 using boost::mpi::communicator;
21 
22 // A simple point class that we can build, add, compare, and
23 // serialize.
24 struct point
25 {
pointpoint26   point() : x(0), y(0), z(0) { }
pointpoint27   point(int x, int y, int z) : x(x), y(y), z(z) { }
28 
29   int x;
30   int y;
31   int z;
32 
33  private:
34   template<typename Archiver>
serializepoint35   void serialize(Archiver& ar, unsigned int /*version*/)
36   {
37     ar & x & y & z;
38   }
39 
40   friend class boost::serialization::access;
41 };
42 
operator <<(std::ostream & out,const point & p)43 std::ostream& operator<<(std::ostream& out, const point& p)
44 {
45   return out << p.x << ' ' << p.y << ' ' << p.z;
46 }
47 
operator ==(const point & p1,const point & p2)48 bool operator==(const point& p1, const point& p2)
49 {
50   return p1.x == p2.x && p1.y == p2.y && p1.z == p2.z;
51 }
52 
operator !=(const point & p1,const point & p2)53 bool operator!=(const point& p1, const point& p2)
54 {
55   return !(p1 == p2);
56 }
57 
operator +(const point & p1,const point & p2)58 point operator+(const point& p1, const point& p2)
59 {
60   return point(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z);
61 }
62 
63 namespace boost { namespace mpi {
64 
65   template <>
66   struct is_mpi_datatype<point> : public mpl::true_ { };
67 
68 } } // end namespace boost::mpi
69 
70 template<typename Generator, typename Op>
71 void
reduce_test(const communicator & comm,Generator generator,const char * type_kind,Op op,const char * op_kind,typename Generator::result_type init,int root=-1)72 reduce_test(const communicator& comm, Generator generator,
73             const char* type_kind, Op op, const char* op_kind,
74             typename Generator::result_type init,
75             int root = -1)
76 {
77   typedef typename Generator::result_type value_type;
78   value_type value = generator(comm.rank());
79 
80   if (root == -1) {
81     for (root = 0; root < comm.size(); ++root)
82       reduce_test(comm, generator, type_kind, op, op_kind, init, root);
83   } else {
84     using boost::mpi::reduce;
85 
86     if (comm.rank() == root) {
87       std::cout << "Reducing to " << op_kind << " of " << type_kind
88                 << " at root " << root << "...";
89       std::cout.flush();
90 
91       value_type result_value;
92       reduce(comm, value, result_value, op, root);
93 
94       // Compute expected result
95       std::vector<value_type> generated_values;
96       for (int p = 0; p < comm.size(); ++p)
97         generated_values.push_back(generator(p));
98       value_type expected_result = std::accumulate(generated_values.begin(),
99                                                    generated_values.end(),
100                                                    init, op);
101       BOOST_CHECK(result_value == expected_result);
102       if (result_value == expected_result)
103         std::cout << "OK." << std::endl;
104     } else {
105       reduce(comm, value, op, root);
106     }
107   }
108 
109   (comm.barrier)();
110 }
111 
112 // Generates integers to test with reduce()
113 struct int_generator
114 {
115   typedef int result_type;
116 
int_generatorint_generator117   int_generator(int base = 1) : base(base) { }
118 
operator ()int_generator119   int operator()(int p) const { return base + p; }
120 
121  private:
122   int base;
123 };
124 
125 // Generate points to test with reduce()
126 struct point_generator
127 {
128   typedef point result_type;
129 
point_generatorpoint_generator130   point_generator(point origin) : origin(origin) { }
131 
operator ()point_generator132   point operator()(int p) const
133   {
134     return point(origin.x + 1, origin.y + 1, origin.z + 1);
135   }
136 
137  private:
138   point origin;
139 };
140 
141 struct string_generator
142 {
143   typedef std::string result_type;
144 
operator ()string_generator145   std::string operator()(int p) const
146   {
147     std::string result = boost::lexical_cast<std::string>(p);
148     result += " rosebud";
149     if (p != 1) result += 's';
150     return result;
151   }
152 };
153 
154 struct secret_int_bit_and
155 {
operator ()secret_int_bit_and156   int operator()(int x, int y) const { return x & y; }
157 };
158 
159 struct wrapped_int
160 {
wrapped_intwrapped_int161   wrapped_int() : value(0) { }
wrapped_intwrapped_int162   explicit wrapped_int(int value) : value(value) { }
163 
164   template<typename Archive>
serializewrapped_int165   void serialize(Archive& ar, unsigned int /* version */)
166   {
167     ar & value;
168   }
169 
170   int value;
171 };
172 
operator +(const wrapped_int & x,const wrapped_int & y)173 wrapped_int operator+(const wrapped_int& x, const wrapped_int& y)
174 {
175   return wrapped_int(x.value + y.value);
176 }
177 
operator ==(const wrapped_int & x,const wrapped_int & y)178 bool operator==(const wrapped_int& x, const wrapped_int& y)
179 {
180   return x.value == y.value;
181 }
182 
183 // Generates wrapped_its to test with reduce()
184 struct wrapped_int_generator
185 {
186   typedef wrapped_int result_type;
187 
wrapped_int_generatorwrapped_int_generator188   wrapped_int_generator(int base = 1) : base(base) { }
189 
operator ()wrapped_int_generator190   wrapped_int operator()(int p) const { return wrapped_int(base + p); }
191 
192  private:
193   int base;
194 };
195 
196 namespace boost { namespace mpi {
197 
198 // Make std::plus<wrapped_int> commutative.
199 template<>
200 struct is_commutative<std::plus<wrapped_int>, wrapped_int>
201   : mpl::true_ { };
202 
203 } } // end namespace boost::mpi
204 
BOOST_AUTO_TEST_CASE(reduce_check)205 BOOST_AUTO_TEST_CASE(reduce_check)
206 {
207   using namespace boost::mpi;
208   environment env;
209 
210   communicator comm;
211 
212   // Built-in MPI datatypes with built-in MPI operations
213   reduce_test(comm, int_generator(), "integers", std::plus<int>(), "sum", 0);
214   reduce_test(comm, int_generator(), "integers", std::multiplies<int>(),
215               "product", 1);
216   reduce_test(comm, int_generator(), "integers", maximum<int>(),
217               "maximum", 0);
218   reduce_test(comm, int_generator(), "integers", minimum<int>(),
219               "minimum", 2);
220 
221   // User-defined MPI datatypes with operations that have the
222   // same name as built-in operations.
223   reduce_test(comm, point_generator(point(0,0,0)), "points",
224               std::plus<point>(), "sum", point());
225 
226   // Built-in MPI datatypes with user-defined operations
227   reduce_test(comm, int_generator(17), "integers", secret_int_bit_and(),
228               "bitwise and", -1);
229 
230   // Arbitrary types with user-defined, commutative operations.
231   reduce_test(comm, wrapped_int_generator(17), "wrapped integers",
232               std::plus<wrapped_int>(), "sum", wrapped_int(0));
233 
234   // Arbitrary types with (non-commutative) user-defined operations
235   reduce_test(comm, string_generator(), "strings",
236               std::plus<std::string>(), "concatenation", std::string());
237 }
238