| // Copyright (C) 2006 Trustees of Indiana University |
| // |
| // Authors: Douglas Gregor |
| // Andrew Lumsdaine |
| |
| // 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) |
| |
| // Performance test of the reduce() collective |
| #include <boost/mpi.hpp> |
| #include <boost/lexical_cast.hpp> |
| |
| namespace mpi = boost::mpi; |
| |
| struct add_int { |
| int operator()(int x, int y) const { return x + y; } |
| }; |
| |
| struct wrapped_int |
| { |
| wrapped_int() : value(0) { } |
| wrapped_int(int value) : value(value) { } |
| |
| template<typename Archiver> |
| void serialize(Archiver& ar, const unsigned int /*version*/) { |
| ar & value; |
| } |
| |
| int value; |
| }; |
| |
| inline wrapped_int operator+(wrapped_int x, wrapped_int y) |
| { |
| return wrapped_int(x.value + y.value); |
| } |
| |
| namespace boost { namespace mpi { |
| template<> struct is_mpi_datatype<wrapped_int> : mpl::true_ { }; |
| } } |
| |
| struct serialized_int |
| { |
| serialized_int() : value(0) { } |
| serialized_int(int value) : value(value) { } |
| |
| template<typename Archiver> |
| void serialize(Archiver& ar, const unsigned int /*version*/) { |
| ar & value; |
| } |
| |
| int value; |
| }; |
| |
| inline serialized_int operator+(serialized_int x, serialized_int y) |
| { |
| return serialized_int(x.value + y.value); |
| } |
| |
| int main(int argc, char* argv[]) |
| { |
| mpi::environment env(argc, argv); |
| mpi::communicator world; |
| |
| int repeat_count = 100; |
| int outer_repeat_count = 2; |
| |
| if (argc > 1) repeat_count = boost::lexical_cast<int>(argv[1]); |
| if (argc > 2) outer_repeat_count = boost::lexical_cast<int>(argv[2]); |
| |
| if (world.rank() == 0) |
| std::cout << "# of processors: " << world.size() << std::endl |
| << "# of iterations: " << repeat_count << std::endl; |
| |
| int value = world.rank(); |
| int result; |
| wrapped_int wi_value = world.rank(); |
| wrapped_int wi_result; |
| serialized_int si_value = world.rank(); |
| serialized_int si_result; |
| |
| // Spin for a while... |
| for (int i = 0; i < repeat_count/10; ++i) { |
| reduce(world, value, result, std::plus<int>(), 0); |
| reduce(world, value, result, add_int(), 0); |
| reduce(world, wi_value, wi_result, std::plus<wrapped_int>(), 0); |
| reduce(world, si_value, si_result, std::plus<serialized_int>(), 0); |
| } |
| |
| for (int outer = 0; outer < outer_repeat_count; ++outer) { |
| // Raw MPI |
| mpi::timer time; |
| for (int i = 0; i < repeat_count; ++i) { |
| MPI_Reduce(&value, &result, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); |
| } |
| double reduce_raw_mpi_total_time = time.elapsed(); |
| |
| // MPI_INT/MPI_SUM case |
| time.restart(); |
| for (int i = 0; i < repeat_count; ++i) { |
| reduce(world, value, result, std::plus<int>(), 0); |
| } |
| double reduce_int_sum_total_time = time.elapsed(); |
| |
| // MPI_INT/MPI_Op case |
| time.restart(); |
| for (int i = 0; i < repeat_count; ++i) { |
| reduce(world, value, result, add_int(), 0); |
| } |
| double reduce_int_op_total_time = time.elapsed(); |
| |
| // MPI_Datatype/MPI_Op case |
| time.restart(); |
| for (int i = 0; i < repeat_count; ++i) { |
| reduce(world, wi_value, wi_result, std::plus<wrapped_int>(), 0); |
| } |
| double reduce_type_op_total_time = time.elapsed(); |
| |
| // Serialized/MPI_Op case |
| time.restart(); |
| for (int i = 0; i < repeat_count; ++i) { |
| reduce(world, si_value, si_result, std::plus<serialized_int>(), 0); |
| } |
| double reduce_ser_op_total_time = time.elapsed(); |
| |
| |
| if (world.rank() == 0) |
| std::cout << "\nInvocation\tElapsed Time (seconds)" |
| << "\nRaw MPI\t\t\t" << reduce_raw_mpi_total_time |
| << "\nMPI_INT/MPI_SUM\t\t" << reduce_int_sum_total_time |
| << "\nMPI_INT/MPI_Op\t\t" << reduce_int_op_total_time |
| << "\nMPI_Datatype/MPI_Op\t" << reduce_type_op_total_time |
| << "\nSerialized/MPI_Op\t" << reduce_ser_op_total_time |
| << std::endl; |
| } |
| |
| return 0; |
| } |