diff --git a/include/boost/mpi/collectives/broadcast.hpp b/include/boost/mpi/collectives/broadcast.hpp index f8b27f0b..5d55f1e7 100644 --- a/include/boost/mpi/collectives/broadcast.hpp +++ b/include/boost/mpi/collectives/broadcast.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace boost { namespace mpi { @@ -133,6 +134,25 @@ namespace detail { ia >> values[i]; } } + +#if BOOST_MPI_VERSION >= 3 + // We're sending a type that has an associated MPI datatype, so + // we'll use MPI_Ibcast to do all of the work. + template + request + ibroadcast_impl(const communicator& comm, T* values, int n, int root, + mpl::true_) + { + MPI_Request* c_ptr; + request req = request::make_trivial(c_ptr); + BOOST_MPI_CHECK_RESULT(MPI_Ibcast, + (values, n, + boost::mpi::get_mpi_datatype(*values), + root, MPI_Comm(comm), c_ptr)); + return req; + } +#endif + } // end namespace detail template @@ -147,6 +167,19 @@ void broadcast(const communicator& comm, T* values, int n, int root) detail::broadcast_impl(comm, values, n, root, is_mpi_datatype()); } +#if BOOST_MPI_VERSION >= 3 +template +request ibroadcast(const communicator& comm, T& value, int root) +{ + return detail::ibroadcast_impl(comm, &value, 1, root, is_mpi_datatype()); +} + +template +request ibroadcast(const communicator& comm, T* values, int n, int root) +{ + return detail::ibroadcast_impl(comm, values, n, root, is_mpi_datatype()); +} +#endif } } // end namespace boost::mpi // If the user has already included skeleton_and_content.hpp, include diff --git a/include/boost/mpi/communicator.hpp b/include/boost/mpi/communicator.hpp index 8663ba3f..46349c57 100644 --- a/include/boost/mpi/communicator.hpp +++ b/include/boost/mpi/communicator.hpp @@ -775,6 +775,15 @@ class BOOST_MPI_DECL communicator void barrier() const; #endif +#if BOOST_MPI_VERSION >= 3 + /** + * @brief Non blocking version of barrier. + * + * This version will return immediatly. The request completes + * once all participants have reached the barrier. + */ + request ibarrier() const; +#endif /** @brief Determine if this communicator is valid for * communication. * diff --git a/include/boost/mpi/config.hpp b/include/boost/mpi/config.hpp index 1fd093bf..91ea093d 100644 --- a/include/boost/mpi/config.hpp +++ b/include/boost/mpi/config.hpp @@ -45,7 +45,7 @@ #endif #if defined MPI_SUBVERSION -/** @brief Major version of the underlying MPI implementation supproted standard. +/** @brief Major version of the underlying MPI implementation supported standard. * * If, for some reason, MPI_SUBVERSION is not supported, you should probably set that * according to your MPI documentation diff --git a/include/boost/mpi/detail/request_handlers.hpp b/include/boost/mpi/detail/request_handlers.hpp index 50a22ec3..2f14a24f 100644 --- a/include/boost/mpi/detail/request_handlers.hpp +++ b/include/boost/mpi/detail/request_handlers.hpp @@ -509,6 +509,9 @@ class BOOST_MPI_DECL request::trivial_handler : public request::handler { private: friend class request; +#if BOOST_MPI_VERSION >= 3 + friend class communicator; +#endif MPI_Request m_request; }; diff --git a/include/boost/mpi/request.hpp b/include/boost/mpi/request.hpp index 6edccc9b..6818b613 100644 --- a/include/boost/mpi/request.hpp +++ b/include/boost/mpi/request.hpp @@ -38,6 +38,11 @@ class BOOST_MPI_DECL request */ request(); + /** + * Just make a request from a C API request and provide its address + * for future assignement. + */ + static request make_trivial(MPI_Request*& r); /** * Send a known number of primitive objects in one MPI request. */ @@ -150,7 +155,9 @@ class BOOST_MPI_DECL request template class legacy_dynamic_primitive_array_handler; #if BOOST_MPI_VERSION >= 3 template class probe_handler; + friend class communicator; #endif + private: shared_ptr m_handler; shared_ptr m_preserved; diff --git a/src/communicator.cpp b/src/communicator.cpp index 016c6af4..07853857 100644 --- a/src/communicator.cpp +++ b/src/communicator.cpp @@ -131,7 +131,6 @@ void (communicator::barrier)() const BOOST_MPI_CHECK_RESULT(MPI_Barrier, (MPI_Comm(*this))); } - communicator::operator MPI_Comm() const { if (comm_ptr) return *comm_ptr; @@ -325,4 +324,14 @@ bool operator==(const communicator& comm1, const communicator& comm2) return result == MPI_IDENT; } +// Non blocking common +#if BOOST_MPI_VERSION >= 3 +request communicator::ibarrier() const +{ + request::trivial_handler* handler = new request::trivial_handler; + BOOST_MPI_CHECK_RESULT(MPI_Ibarrier, (*this, &handler->m_request)); + return request(handler); +} +#endif + } } // end namespace boost::mpi diff --git a/src/request.cpp b/src/request.cpp index 3ba5695a..b78951f8 100644 --- a/src/request.cpp +++ b/src/request.cpp @@ -26,6 +26,13 @@ request::preserve(boost::shared_ptr d) { } request request::make_dynamic() { return request(new dynamic_handler()); } +request request::make_trivial(MPI_Request*& r) { + trivial_handler *handler = new trivial_handler; + r = &(handler->m_request); + return request(handler); +} + + request request::make_bottom_send(communicator const& comm, int dest, int tag, MPI_Datatype tp) { trivial_handler* handler = new trivial_handler; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index ac9ac253..31883867 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -49,7 +49,9 @@ test-suite mpi [ mpi-test groups_test ] # tests that require -std=c++11 [ mpi-test sendrecv_vector : : : 2 ] - # Intel MPI 2018 and older are axtected to fail: + # Intel MPI 2018 and older are expected to fail: [ mpi-test non_blocking_any_source : : : 2 17 ] + # Non Blockin common (MPI 3 only) + [ mpi-test ibarrier_test : : : 1 3 ] ; } diff --git a/test/broadcast_test.cpp b/test/broadcast_test.cpp index 50081aad..2d9d2b8a 100644 --- a/test/broadcast_test.cpp +++ b/test/broadcast_test.cpp @@ -19,22 +19,42 @@ #define BOOST_TEST_MODULE mpi_broadcast #include -using boost::mpi::communicator; - -using boost::mpi::packed_skeleton_iarchive; -using boost::mpi::packed_skeleton_oarchive; +namespace mpi = boost::mpi; template void -broadcast_test(const communicator& comm, const T& bc_value, - const char* kind, int root = -1) +broadcast_test(mpi::communicator const& comm, T const& bc_value, + char const* kind, int root = -1) { if (root == -1) { for (root = 0; root < comm.size(); ++root) broadcast_test(comm, bc_value, kind, root); } else { - using boost::mpi::broadcast; + T value; + if (comm.rank() == root) { + value = bc_value; + std::cout << "Broadcasting " << kind << " from root " << root << "..."; + std::cout.flush(); + } + + mpi::broadcast(comm, value, root); + BOOST_CHECK(value == bc_value); + if (comm.rank() == root && value == bc_value) + std::cout << "OK." << std::endl; + } + comm.barrier(); +} + +template +void +ibroadcast_test(mpi::communicator const& comm, T const& bc_value, + char const* kind, int root = -1) +{ + if (root == -1) { + for (root = 0; root < comm.size(); ++root) + ibroadcast_test(comm, bc_value, kind, root); + } else { T value; if (comm.rank() == root) { value = bc_value; @@ -42,22 +62,27 @@ broadcast_test(const communicator& comm, const T& bc_value, std::cout.flush(); } - broadcast(comm, value, root); + mpi::request req = mpi::ibroadcast(comm, value, root); + std::ostringstream buf; + buf << "rk" << comm.rank() << ": Broadcasting " << value << " from " << root << "..."; + if (!req.test()) { + buf << ".. not finished here. So we wait..."; + req.wait(); + buf << "done.\n"; + } else { + buf << ".. which is already finished.\n"; + } + std::cout << buf.str(); BOOST_CHECK(value == bc_value); if (comm.rank() == root && value == bc_value) std::cout << "OK." << std::endl; } - - (comm.barrier)(); } void -test_skeleton_and_content(const communicator& comm, int root = 0) +test_skeleton_and_content(mpi::communicator const& comm, int root = 0) { - using boost::mpi::content; - using boost::mpi::get_content; using boost::make_counting_iterator; - using boost::mpi::broadcast; int list_size = comm.size() + 7; if (comm.rank() == root) { @@ -67,21 +92,21 @@ test_skeleton_and_content(const communicator& comm, int root = 0) original_list.push_back(i); // Build up the skeleton - packed_skeleton_oarchive oa(comm); + mpi::packed_skeleton_oarchive oa(comm); oa << original_list; // Broadcast the skeleton std::cout << "Broadcasting integer list skeleton from root " << root << "..." << std::flush; - broadcast(comm, oa, root); + mpi::broadcast(comm, oa, root); std::cout << "OK." << std::endl; // Broadcast the content std::cout << "Broadcasting integer list content from root " << root << "..." << std::flush; { - content c = get_content(original_list); - broadcast(comm, c, root); + mpi::content c = mpi::get_content(original_list); + mpi::broadcast(comm, c, root); } std::cout << "OK." << std::endl; @@ -90,8 +115,8 @@ test_skeleton_and_content(const communicator& comm, int root = 0) std::cout << "Broadcasting reversed integer list content from root " << root << "..." << std::flush; { - content c = get_content(original_list); - broadcast(comm, c, root); + mpi::content c = mpi::get_content(original_list); + mpi::broadcast(comm, c, root); } std::cout << "OK." << std::endl; @@ -101,24 +126,24 @@ test_skeleton_and_content(const communicator& comm, int root = 0) std::list junk_list(comm.rank() * 3 + 1, 17); // Receive the skeleton - packed_skeleton_iarchive ia(comm); - broadcast(comm, ia, root); + mpi::packed_skeleton_iarchive ia(comm); + mpi::broadcast(comm, ia, root); // Build up a list to match the skeleton, and make sure it has the // right structure (we have no idea what the data will be). std::list transferred_list; ia >> transferred_list; - BOOST_CHECK((int)transferred_list.size() == list_size); + BOOST_CHECK(int(transferred_list.size()) == list_size); // Receive the content and check it - broadcast(comm, get_content(transferred_list), root); + mpi::broadcast(comm, mpi::get_content(transferred_list), root); bool list_content_ok = std::equal(make_counting_iterator(0), make_counting_iterator(list_size), transferred_list.begin()); BOOST_CHECK(list_content_ok); // Receive the reversed content and check it - broadcast(comm, get_content(transferred_list), root); + mpi::broadcast(comm, mpi::get_content(transferred_list), root); bool rlist_content_ok = std::equal(make_counting_iterator(0), make_counting_iterator(list_size), transferred_list.rbegin()); @@ -132,18 +157,19 @@ test_skeleton_and_content(const communicator& comm, int root = 0) } } - (comm.barrier)(); + comm.barrier(); } BOOST_AUTO_TEST_CASE(broadcast_check) { boost::mpi::environment env; - communicator comm; + mpi::communicator comm; BOOST_TEST_REQUIRE(comm.size() > 1); // Check transfer of individual objects broadcast_test(comm, 17, "integers"); + ibroadcast_test(comm, 17, "integers"); broadcast_test(comm, gps_position(39,16,20.2799), "GPS positions"); broadcast_test(comm, gps_position(26,25,30.0), "GPS positions"); broadcast_test(comm, std::string("Rosie"), "string"); diff --git a/test/ibarrier_test.cpp b/test/ibarrier_test.cpp new file mode 100644 index 00000000..ac7a1923 --- /dev/null +++ b/test/ibarrier_test.cpp @@ -0,0 +1,42 @@ +// Copyright Alain Miniussi 2014. +// Distributed under 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) + +// A test of the non blocking barrier operation. + +#include +#include + + +#define BOOST_TEST_MODULE mpi_ibarrier +#include + +namespace mpi = boost::mpi; + +BOOST_AUTO_TEST_CASE(ibarrier_check) +{ + mpi::environment env; + mpi::communicator world; + + std::ostringstream buf; + int rk = world.rank(); + buf << "rk" << rk << ": calling ibarrier.\n"; + std::cout << buf.str(); + mpi::request r = world.ibarrier(); + if (rk == 0) { + while (!r.test()) { + buf << "rk" << rk << ": not completed yet.\n"; + std::cout << buf.str(); + } + buf << "rk" << rk << ": completed.\n"; + std::cout << buf.str(); + } else { + buf << "rk" << rk << ": waiting..."; + std::cout << buf.str() << std::flush; + r.wait(); + buf << "rk" << rk << ": done.\n"; + std::cout << buf.str(); + } + BOOST_TEST(true); +} diff --git a/test/sendrecv_test.cpp b/test/sendrecv_test.cpp index 801b2629..a7fe3a3e 100644 --- a/test/sendrecv_test.cpp +++ b/test/sendrecv_test.cpp @@ -1,4 +1,4 @@ -// Copyright Alain Miniussi 20014. +// Copyright Alain Miniussi 2014. // Distributed under 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)