From 959ebd4e870b2de25783dd6caa43de101ebd9af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-No=C3=ABl=20Grad?= Date: Fri, 4 Aug 2023 11:17:15 +0200 Subject: [PATCH] core: Remove MpiCallbacks tag dispatch --- src/core/MpiCallbacks.hpp | 371 +--------------------- src/core/communication.hpp | 32 -- src/core/unit_tests/MpiCallbacks_test.cpp | 80 +---- 3 files changed, 16 insertions(+), 467 deletions(-) diff --git a/src/core/MpiCallbacks.hpp b/src/core/MpiCallbacks.hpp index 03f3a014b79..5ea39c0e553 100644 --- a/src/core/MpiCallbacks.hpp +++ b/src/core/MpiCallbacks.hpp @@ -31,28 +31,20 @@ * e.g. to broadcast global variables or run an algorithm in parallel. * * Callbacks are registered on the head node as function pointers via - * the @ref callback_macros "callback macros". The visitor pattern - * allows using arbitrary function signatures. For non-void returning - * callbacks, several return value policies are available: ignore return - * value, return only one value (this is achieved using a boost optional - * that is empty on all but one node), return the value of the head node, - * or return a reduced value (by specifying the reduction operation). + * the @ref REGISTER_CALLBACK. The visitor pattern allows using arbitrary + * function signatures. */ #include #include #include -#include #include -#include #include -#include +#include #include #include -#include -#include #include #include #include @@ -63,26 +55,6 @@ namespace Communication { class MpiCallbacks; -/** - * This namespace contains tag types - * to indicate what to do with the return - * values of callbacks. - */ -namespace Result { -/** %Ignore result */ -struct Ignore {}; -constexpr auto ignore = Ignore{}; -/** Return value from one rank */ -struct OneRank {}; -constexpr auto one_rank = OneRank{}; -/** Return value from the head node */ -struct MainRank {}; -constexpr auto main_rank = MainRank{}; -/** Reduce return value over all ranks */ -struct Reduction {}; -constexpr auto reduction = Reduction{}; -} // namespace Result - namespace detail { /** * @brief Check if a type can be used as a callback argument. @@ -153,8 +125,7 @@ struct callback_concept_t { * @brief Callback without a return value. * * This is an implementation of a callback for a specific callable - * @p F and a set of arguments to call it with, where the there - * is no return value. + * @p F and a set of arguments to call it with. */ template struct callback_void_t final : public callback_concept_t { @@ -171,112 +142,6 @@ struct callback_void_t final : public callback_concept_t { } }; -/** - * @brief Callback where the return value is ignored. - * - * This is an implementation of a callback for a specific callable - * @p F and a set of arguments to call it with, where the valued from - * all ranks are ignored. - */ -template -struct callback_ignore_t final : public callback_concept_t { - F m_f; - - callback_ignore_t(callback_ignore_t const &) = delete; - callback_ignore_t(callback_ignore_t &&) = delete; - - template - explicit callback_ignore_t(FRef &&f) : m_f(std::forward(f)) {} - void operator()(boost::mpi::communicator const &, - boost::mpi::packed_iarchive &ia) const override { - detail::invoke(m_f, ia); - } -}; - -/** - * @brief Callback with a return value from one rank. - * - * This is an implementation of a callback for a specific callable - * @p F and a set of arguments to call it with, where the value from - * one rank is returned. Only one node is allowed to return a value. - */ -template -struct callback_one_rank_t final : public callback_concept_t { - F m_f; - - callback_one_rank_t(callback_one_rank_t const &) = delete; - callback_one_rank_t(callback_one_rank_t &&) = delete; - - template - explicit callback_one_rank_t(FRef &&f) : m_f(std::forward(f)) {} - void operator()(boost::mpi::communicator const &comm, - boost::mpi::packed_iarchive &ia) const override { - auto const result = detail::invoke(m_f, ia); - - assert(1 == boost::mpi::all_reduce(comm, static_cast(!!result), - std::plus<>()) && - "Incorrect number of return values"); - - /* If this rank returned a result, send it to the head node. */ - if (!!result) { - comm.send(0, 42, *result); - } - } -}; - -/** - * @brief Callback with a return value from the head node. - * - * This is an implementation of a callback for a specific callable - * @p F and a set of arguments to call it with, where the value from - * the head node is returned. - */ -template -struct callback_main_rank_t final : public callback_concept_t { - F m_f; - - callback_main_rank_t(callback_main_rank_t const &) = delete; - callback_main_rank_t(callback_main_rank_t &&) = delete; - - template - explicit callback_main_rank_t(FRef &&f) : m_f(std::forward(f)) {} - void operator()(boost::mpi::communicator const &, - boost::mpi::packed_iarchive &ia) const override { - std::ignore = detail::invoke(m_f, ia); - } -}; - -/** - * @brief Callback with return value reduction. - * - * This is an implementation of a callback for a specific callable - * @p F and a set of arguments to call it with, where the return - * value is reduced over the communicator. - */ -template -struct callback_reduce_t final : public callback_concept_t { - Op m_op; - F m_f; - - template - explicit callback_reduce_t(OpRef &&op, FRef &&f) - : m_op(std::forward(op)), m_f(std::forward(f)) {} - - /** - * @brief Execute the callback. - * - * Receive parameters for this callback, and then call it. - * - * @param comm The communicator to receive the parameters on. - */ - void operator()(boost::mpi::communicator const &comm, - boost::mpi::packed_iarchive &ia) const override { - /* Call the callback function, and reduce over the results with - * the stored reduction operation. */ - boost::mpi::reduce(comm, detail::invoke(m_f, ia), m_op, 0); - } -}; - template struct FunctorTypes { using functor_type = F; using return_type = R; @@ -293,7 +158,7 @@ using functor_types = decltype(functor_types_impl(&std::remove_reference_t::operator())); template -auto make_model_impl(Result::Ignore, CRef &&c, FunctorTypes) { +auto make_model_impl(CRef &&c, FunctorTypes) { return std::make_unique>(std::forward(c)); } @@ -304,43 +169,15 @@ auto make_model_impl(Result::Ignore, CRef &&c, FunctorTypes) { * to exist and can not be overloaded. */ template auto make_model(F &&f) { - return make_model_impl(Result::Ignore{}, std::forward(f), - functor_types{}); + return make_model_impl(std::forward(f), functor_types{}); } /** * @brief Make a @ref callback_model_t for a function pointer. - * - * This instantiates an implementation of a callback for a function - * pointer. The main task here is to transfer the signature from - * the pointer to the callback_model_t by template argument type - * deduction. */ template auto make_model(void (*f_ptr)(Args...)) { return std::make_unique>(f_ptr); } - -template -auto make_model(Result::Reduction, R (*f_ptr)(Args...), Op &&op) { - return std::make_unique< - callback_reduce_t, R (*)(Args...), Args...>>( - std::forward(op), f_ptr); -} - -template -auto make_model(Result::Ignore, R (*f_ptr)(Args...)) { - return std::make_unique>(f_ptr); -} - -template -auto make_model(Result::OneRank, R (*f_ptr)(Args...)) { - return std::make_unique>(f_ptr); -} - -template -auto make_model(Result::MainRank, R (*f_ptr)(Args...)) { - return std::make_unique>(f_ptr); -} } // namespace detail /** @@ -484,25 +321,6 @@ class MpiCallbacks { detail::make_model(fp)); } - /** - * @brief Add a new callback with a return value. - * - * Add a new callback to the system. This is a collective - * function that must be run on all nodes. - * @p tag is one of the tag types from @ref Communication::Result, - * which indicates what to do with the return values. - * - * @param tag Tag type indicating return operation - * @param tag_args Argument for the return operation, if any. - * @param fp Pointer to the static callback function to add. - */ - template - static void add_static(Tag tag, R (*fp)(Args...), TagArgs &&...tag_args) { - static_callbacks().emplace_back( - reinterpret_cast(fp), - detail::make_model(tag, fp, std::forward(tag_args)...)); - } - private: /** * @brief Remove callback. @@ -566,8 +384,7 @@ class MpiCallbacks { */ template auto call(void (*fp)(Args...), ArgRef &&...args) const -> - /* Enable only if fp can be called with the provided arguments, - * e.g. if fp(args...) is well-formed. */ + /* enable only if fp can be called with the provided arguments */ std::enable_if_t> { const int id = m_func_ptr_to_id.at(reinterpret_cast(fp)); @@ -585,104 +402,13 @@ class MpiCallbacks { * @param args Arguments for the callback. */ template - void call_all(void (*fp)(Args...), ArgRef &&...args) const { + auto call_all(void (*fp)(Args...), ArgRef &&...args) const -> + /* enable only if fp can be called with the provided arguments */ + std::enable_if_t> { call(fp, args...); fp(args...); } - /** - * @brief Call a callback and reduce the result over all nodes. - * - * This calls a callback on all nodes, including the head node, - * and does a mpi reduction with the registered operation. - * - * This method can only be called on the head node. - */ - template - auto call(Result::Reduction, Op op, R (*fp)(Args...), Args... args) const - -> std::remove_reference_t(), - std::declval()))> { - const int id = m_func_ptr_to_id.at(reinterpret_cast(fp)); - - call(id, args...); - - std::remove_cv_t(), - std::declval()))>> - result{}; - boost::mpi::reduce(m_comm, fp(args...), result, op, 0); - - return result; - } - - /** - * @brief Call a callback and ignore the result over all nodes. - * - * This calls a callback on all nodes, including the head node, - * and ignore all return values. - * - * This method can only be called on the head node. - */ - template - auto call(Result::Ignore, R (*fp)(Args...), ArgRef... args) const - -> std::remove_reference_t { - - const int id = m_func_ptr_to_id.at(reinterpret_cast(fp)); - call(id, args...); - - fp(std::forward(args)...); - - return {}; - } - - /** - * @brief Call a callback and reduce the result over all nodes. - * - * This calls a callback on all nodes, including the head node, - * and returns the value from the node which returned an engaged - * optional. Only one node is allowed to return a value. - * - * This method can only be called on the head node. - */ - template - auto call(Result::OneRank, boost::optional (*fp)(Args...), - ArgRef... args) const -> std::remove_reference_t { - - const int id = m_func_ptr_to_id.at(reinterpret_cast(fp)); - call(id, args...); - - auto const local_result = fp(std::forward(args)...); - - assert(1 == boost::mpi::all_reduce(m_comm, static_cast(!!local_result), - std::plus<>()) && - "Incorrect number of return values"); - - if (!!local_result) { - return *local_result; - } - - std::remove_cv_t> result; - m_comm.recv(boost::mpi::any_source, boost::mpi::any_tag, result); - return result; - } - - /** - * @brief Call a callback and return the result of the head node. - * - * This calls a callback on all nodes, including the head node, - * and returns the value from the head node. - * - * This method can only be called on the head node. - */ - template - auto call(Result::MainRank, R (*fp)(Args...), ArgRef... args) const - -> std::remove_reference_t { - - const int id = m_func_ptr_to_id.at(reinterpret_cast(fp)); - call(id, args...); - - return fp(std::forward(args)...); - } - /** * @brief Start the MPI loop. * @@ -771,20 +497,9 @@ class RegisterCallback { template explicit RegisterCallback(void (*cb)(Args...)) { MpiCallbacks::add_static(cb); } - - template - explicit RegisterCallback(Tag tag, R (*cb)(Args...), TagArgs &&...tag_args) { - MpiCallbacks::add_static(tag, cb, std::forward(tag_args)...); - } }; } /* namespace Communication */ -/** - * @name Callback macros - * @anchor callback_macros - */ -/**@{*/ - /** * @brief Register a static callback without return value. * @@ -798,70 +513,4 @@ class RegisterCallback { static ::Communication::RegisterCallback register_##cb(&(cb)); \ } -/** - * @brief Register a static callback whose return value is reduced. - * - * This registers a function as an mpi callback with - * reduction of the return values from the nodes. - * The macro should be used at global scope. - * - * @param cb A function - * @param op Reduction operation - */ -#define REGISTER_CALLBACK_REDUCTION(cb, op) \ - namespace Communication { \ - static ::Communication::RegisterCallback \ - register_reduction_##cb(::Communication::Result::Reduction{}, &(cb), \ - (op)); \ - } - -/** - * @brief Register a static callback whose return value is to be ignored. - * - * This registers a function as an mpi callback with - * ignored return values. - * The macro should be used at global scope. - * - * @param cb A function - */ -#define REGISTER_CALLBACK_IGNORE(cb) \ - namespace Communication { \ - static ::Communication::RegisterCallback \ - register_ignore_##cb(::Communication::Result::Ignore{}, &(cb)); \ - } - -/** - * @brief Register a static callback which returns a value on only one node. - * - * This registers a function as an mpi callback with - * reduction of the return values from one node - * where the value of the optional is set. - * The macro should be used at global scope. - * - * @param cb A function - */ -#define REGISTER_CALLBACK_ONE_RANK(cb) \ - namespace Communication { \ - static ::Communication::RegisterCallback \ - register_one_rank_##cb(::Communication::Result::OneRank{}, &(cb)); \ - } - -/** - * @brief Register a static callback whose return value is ignored except on - * the head node. - * - * This registers a function as an mpi callback with - * reduction of the return values from the head node. - * The macro should be used at global scope. - * - * @param cb A function - */ -#define REGISTER_CALLBACK_MAIN_RANK(cb) \ - namespace Communication { \ - static ::Communication::RegisterCallback \ - register_main_rank_##cb(::Communication::Result::MainRank{}, &(cb)); \ - } - -/**@}*/ - #endif diff --git a/src/core/communication.hpp b/src/core/communication.hpp index 9acdef0c561..a7dbcf42800 100644 --- a/src/core/communication.hpp +++ b/src/core/communication.hpp @@ -98,38 +98,6 @@ void mpi_call_all(void (*fp)(Args...), ArgRef &&...args) { Communication::mpiCallbacks().call_all(fp, std::forward(args)...); } -/** @brief Call a local function. - * @tparam Tag Any tag type defined in @ref Communication::Result - * @tparam R Return type of the local function - * @tparam Args Local function argument types - * @tparam ArgRef Local function argument types - * @param tag Reduction strategy - * @param fp Local function - * @param args Local function arguments - */ -template -auto mpi_call(Tag tag, R (*fp)(Args...), ArgRef &&...args) { - return Communication::mpiCallbacks().call(tag, fp, - std::forward(args)...); -} - -/** @brief Call a local function. - * @tparam Tag Any tag type defined in @ref Communication::Result - * @tparam TagArg Types of arguments to @p Tag - * @tparam R Return type of the local function - * @tparam Args Local function argument types - * @tparam ArgRef Local function argument types - * @param tag Reduction strategy - * @param tag_arg Arguments to the reduction strategy - * @param fp Local function - * @param args Local function arguments - */ -template -auto mpi_call(Tag tag, TagArg &&tag_arg, R (*fp)(Args...), ArgRef &&...args) { - return Communication::mpiCallbacks().call(tag, std::forward(tag_arg), - fp, std::forward(args)...); -} - /** Process requests from head node. Worker nodes main loop. */ void mpi_loop(); diff --git a/src/core/unit_tests/MpiCallbacks_test.cpp b/src/core/unit_tests/MpiCallbacks_test.cpp index 3afbe3f2a63..27884057ebf 100644 --- a/src/core/unit_tests/MpiCallbacks_test.cpp +++ b/src/core/unit_tests/MpiCallbacks_test.cpp @@ -175,38 +175,20 @@ BOOST_AUTO_TEST_CASE(CallbackHandle) { } } -BOOST_AUTO_TEST_CASE(reduce_callback) { - auto cb = []() -> int { return boost::mpi::communicator().rank(); }; - Communication::MpiCallbacks::add_static(Communication::Result::Reduction{}, - static_cast(cb), - std::plus()); - - boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); - - if (0 == world.rank()) { - auto const ret = cbs.call(Communication::Result::reduction, - std::plus(), static_cast(cb)); - auto const n = world.size(); - BOOST_CHECK_EQUAL(ret, (n * (n - 1)) / 2); - } else { - cbs.loop(); - } -} - -BOOST_AUTO_TEST_CASE(ignore_callback) { +BOOST_AUTO_TEST_CASE(call) { called = false; - auto cb = []() -> int { return called = true, -1; }; + auto cb = []() { called = true; }; - auto const fp = static_cast(cb); + auto const fp = static_cast(cb); - Communication::MpiCallbacks::add_static(Communication::Result::ignore, fp); + Communication::MpiCallbacks::add_static(fp); boost::mpi::communicator world; Communication::MpiCallbacks cbs(world); if (0 == world.rank()) { - cbs.call(Communication::Result::ignore, fp); + cbs.call(fp); + fp(); } else { cbs.loop(); } @@ -214,56 +196,6 @@ BOOST_AUTO_TEST_CASE(ignore_callback) { BOOST_CHECK(called); } -BOOST_AUTO_TEST_CASE(one_rank_callback) { - auto cb = []() -> boost::optional { - boost::mpi::communicator world; - if (world.rank() == (world.size() - 1)) { - return world.rank(); - } - - return {}; - }; - - auto const fp = static_cast (*)()>(cb); - - Communication::MpiCallbacks::add_static(Communication::Result::one_rank, fp); - - boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); - - if (0 == world.rank()) { - BOOST_CHECK_EQUAL(cbs.call(Communication::Result::one_rank, fp), - world.size() - 1); - } else { - cbs.loop(); - } -} - -BOOST_AUTO_TEST_CASE(main_rank_callback) { - auto cb = []() -> int { - boost::mpi::communicator world; - if (world.rank() == 0) { - return world.size(); - } - - return -1; - }; - - auto const fp = static_cast(cb); - - Communication::MpiCallbacks::add_static(Communication::Result::main_rank, fp); - - boost::mpi::communicator world; - Communication::MpiCallbacks cbs(world); - - if (0 == world.rank()) { - BOOST_CHECK_EQUAL(cbs.call(Communication::Result::main_rank, fp), - world.size()); - } else { - cbs.loop(); - } -} - BOOST_AUTO_TEST_CASE(call_all) { called = false; auto cb = []() { called = true; };