diff --git a/.circleci/tests.unit1.algorithms b/.circleci/tests.unit1.algorithms index d35e591525da..2cfaa138ed4f 100644 --- a/.circleci/tests.unit1.algorithms +++ b/.circleci/tests.unit1.algorithms @@ -5,7 +5,6 @@ # file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) tests.unit.modules.algorithms.algorithms.adjacentdifference -tests.unit.modules.algorithms.algorithms.adjacentdifference_sender tests.unit.modules.algorithms.algorithms.adjacentfind tests.unit.modules.algorithms.algorithms.adjacentfind_binary tests.unit.modules.algorithms.algorithms.all_of @@ -31,7 +30,6 @@ tests.unit.modules.algorithms.algorithms.exclusive_scan_validate tests.unit.modules.algorithms.algorithms.fill tests.unit.modules.algorithms.algorithms.filln tests.unit.modules.algorithms.algorithms.find -tests.unit.modules.algorithms.algorithms.find_sender tests.unit.modules.algorithms.algorithms.findend tests.unit.modules.algorithms.algorithms.findfirstof tests.unit.modules.algorithms.algorithms.findfirstof_binary @@ -40,7 +38,6 @@ tests.unit.modules.algorithms.algorithms.findifnot tests.unit.modules.algorithms.algorithms.foreach tests.unit.modules.algorithms.algorithms.foreach_executors tests.unit.modules.algorithms.algorithms.foreach_prefetching -tests.unit.modules.algorithms.algorithms.foreach_sender tests.unit.modules.algorithms.algorithms.foreach_scheduler tests.unit.modules.algorithms.algorithms.foreachn tests.unit.modules.algorithms.algorithms.foreachn_exception @@ -53,7 +50,6 @@ tests.unit.modules.algorithms.algorithms.for_loop_n tests.unit.modules.algorithms.algorithms.for_loop_n_strided tests.unit.modules.algorithms.algorithms.for_loop_reduction tests.unit.modules.algorithms.algorithms.for_loop_reduction_async -tests.unit.modules.algorithms.algorithms.for_loop_sender tests.unit.modules.algorithms.algorithms.for_loop_strided tests.unit.modules.algorithms.algorithms.generate tests.unit.modules.algorithms.algorithms.generaten @@ -74,3 +70,5 @@ tests.unit.modules.algorithms.algorithms.min_element tests.unit.modules.algorithms.algorithms.minmax_element tests.unit.modules.algorithms.algorithms.mismatch tests.unit.modules.algorithms.algorithms.mismatch_binary +tests.unit.modules.algorithms.algorithms.move +tests.unit.modules.algorithms.algorithms.nth_element diff --git a/.circleci/tests.unit2.algorithms b/.circleci/tests.unit2.algorithms index 37f5d6020d6d..639ac1daec09 100644 --- a/.circleci/tests.unit2.algorithms +++ b/.circleci/tests.unit2.algorithms @@ -4,8 +4,6 @@ # 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) -tests.unit.modules.algorithms.algorithms.move -tests.unit.modules.algorithms.algorithms.nth_element tests.unit.modules.algorithms.algorithms.none_of tests.unit.modules.algorithms.algorithms.parallel_sort tests.unit.modules.algorithms.algorithms.partial_sort @@ -15,7 +13,6 @@ tests.unit.modules.algorithms.algorithms.partition_copy tests.unit.modules.algorithms.algorithms.reduce_ tests.unit.modules.algorithms.algorithms.reduce_by_key tests.unit.modules.algorithms.algorithms.remove -tests.unit.modules.algorithms.algorithms.remove tests.unit.modules.algorithms.algorithms.remove1 tests.unit.modules.algorithms.algorithms.remove2 tests.unit.modules.algorithms.algorithms.remove_if @@ -28,10 +25,8 @@ tests.unit.modules.algorithms.algorithms.replace_copy tests.unit.modules.algorithms.algorithms.replace_copy_if tests.unit.modules.algorithms.algorithms.reverse tests.unit.modules.algorithms.algorithms.reverse_copy -tests.unit.modules.algorithms.algorithms.reverse_sender tests.unit.modules.algorithms.algorithms.rotate tests.unit.modules.algorithms.algorithms.rotate_copy -tests.unit.modules.algorithms.algorithms.rotate_sender tests.unit.modules.algorithms.algorithms.search tests.unit.modules.algorithms.algorithms.searchn tests.unit.modules.algorithms.algorithms.set_difference diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp index 27d7c543a529..60fd4fa36a23 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp @@ -276,27 +276,34 @@ namespace hpx::parallel { util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; - - constexpr bool scheduler_policy = + constexpr bool has_scheduler_policy = hpx::execution_policy_has_scheduler_executor_v; - if constexpr (!scheduler_policy) + FwdIter1 prev = first; + difference_type count; + + if (first == last) { - if (first == last) + if constexpr (!has_scheduler_policy) { return result::get(HPX_MOVE(dest)); } + else + { + count = static_cast(0); + } } + else + { + count = detail::distance(first, last) - 1; - difference_type count = detail::distance(first, last) - 1; - - FwdIter1 prev = first; - hpx::traits::proxy_value_t< - typename std::iterator_traits::value_type> - tmp = *first++; - *dest++ = HPX_MOVE(tmp); + hpx::traits::proxy_value_t< + typename std::iterator_traits::value_type> + tmp = *first++; + *dest++ = HPX_MOVE(tmp); + } - if constexpr (!scheduler_policy) + if constexpr (!has_scheduler_policy) { if (count == 0) { diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_find.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_find.hpp index 9da7bdc02507..b145beed6d76 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_find.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_find.hpp @@ -173,18 +173,24 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, FwdIter first, Sent_ last, Pred&& pred, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& orgpolicy, FwdIter first, + Sent_ last, Pred&& pred, Proj&& proj) { using zip_iterator = hpx::util::zip_iterator; using difference_type = typename std::iterator_traits::difference_type; + using result = + util::detail::algorithm_result; - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(HPX_MOVE(last)); + if (first == last) + { + return result::get(HPX_MOVE(last)); + } } decltype(auto) policy = parallel::util::adapt_placement_mode( @@ -209,10 +215,15 @@ namespace hpx::parallel { }; auto f2 = [tok, count, first, last]( - auto&& data) mutable -> FwdIter { - // make sure iterators embedded in function object that is - // attached to futures are invalidated - util::detail::clear_container(data); + auto&&... data) mutable -> FwdIter { + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + // make sure iterators embedded in function object that + // is attached to futures are invalidated + util::detail::clear_container(data...); + } + difference_type adj_find_res = tok.get_data(); if (adj_find_res != count) { @@ -222,9 +233,16 @@ namespace hpx::parallel { { first = last; } - return HPX_MOVE(first); + return first; }; + if constexpr (has_scheduler_executor) + { + // underflow prevention for the upcoming call + if (count == 0) + ++count; + } + using partitioner_type = util::partitioner; return partitioner_type::call_with_index( @@ -269,10 +287,8 @@ namespace hpx { hpx::traits::is_forward_iterator_v )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::adjacent_find_t, ExPolicy&& policy, - FwdIter first, FwdIter last, Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke(hpx::adjacent_find_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred = Pred()) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least a forward iterator"); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/all_any_none.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/all_any_none.hpp index 2b6af6095c44..c9a5ae0bd0bb 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/all_any_none.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/all_any_none.hpp @@ -373,23 +373,31 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIter first, Sent last, F&& op, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter first, + Sent last, F&& op, Proj&& proj) { - if (first == last) + using result = util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - true); + if (first == last) + { + return result::get(true); + } } using policy_type = std::decay_t; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; auto f1 = [op = HPX_FORWARD(F, op), tok, proj = HPX_FORWARD(Proj, proj)]( FwdIter part_begin, - std::size_t part_count) mutable -> bool { + std::size_t part_count) mutable + -> intermediate_result_t { detail::sequential_find_if(part_begin, part_count, tok, HPX_FORWARD(F, op), HPX_FORWARD(Proj, proj)); @@ -397,18 +405,20 @@ namespace hpx::parallel { return !tok.was_cancelled(); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), first, - detail::distance(first, last), HPX_MOVE(f1), - [](auto&& results) { - return detail::sequential_find_if_not< - hpx::execution::sequenced_policy>( - hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) { - return val.get(); - }) == hpx::util::end(results); - }); + auto f2 = [](auto&& results) { + return detail::sequential_find_if_not< + hpx::execution::sequenced_policy>( + hpx::util::begin(results), + hpx::util::end(results), + hpx::functional::unwrap{}) == + hpx::util::end(results); + }; + + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), + first, detail::distance(first, last), HPX_MOVE(f1), + HPX_MOVE(f2)); } }; /// \endcond @@ -438,23 +448,31 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIter first, Sent last, F&& op, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter first, + Sent last, F&& op, Proj&& proj) { - if (first == last) + using result = util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - false); + if (first == last) + { + return result::get(true); + } } using policy_type = std::decay_t; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; auto f1 = [op = HPX_FORWARD(F, op), tok, proj = HPX_FORWARD(Proj, proj)]( FwdIter part_begin, - std::size_t part_count) mutable -> bool { + std::size_t part_count) mutable + -> intermediate_result_t { detail::sequential_find_if(part_begin, part_count, tok, HPX_FORWARD(F, op), HPX_FORWARD(Proj, proj)); @@ -462,18 +480,20 @@ namespace hpx::parallel { return tok.was_cancelled(); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), first, - detail::distance(first, last), HPX_MOVE(f1), - [](auto&& results) { - return detail::sequential_find_if< - hpx::execution::sequenced_policy>( - hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) { - return val.get(); - }) != hpx::util::end(results); - }); + auto f2 = [](auto&& results) { + return detail::sequential_find_if< + hpx::execution::sequenced_policy>( + hpx::util::begin(results), + hpx::util::end(results), + hpx::functional::unwrap{}) != + hpx::util::end(results); + }; + + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), + first, detail::distance(first, last), HPX_MOVE(f1), + HPX_MOVE(f2)); } }; /// \endcond @@ -502,23 +522,31 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIter first, Sent last, F&& op, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter first, + Sent last, F&& op, Proj&& proj) { - if (first == last) + using result = util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - true); + if (first == last) + { + return result::get(true); + } } using policy_type = std::decay_t; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; auto f1 = [op = HPX_FORWARD(F, op), tok, proj = HPX_FORWARD(Proj, proj)]( FwdIter part_begin, - std::size_t part_count) mutable -> bool { + std::size_t part_count) mutable + -> intermediate_result_t { detail::sequential_find_if_not(part_begin, part_count, tok, HPX_FORWARD(F, op), HPX_FORWARD(Proj, proj)); @@ -526,18 +554,20 @@ namespace hpx::parallel { return !tok.was_cancelled(); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), first, - detail::distance(first, last), HPX_MOVE(f1), - [](auto&& results) { - return detail::sequential_find_if_not< - hpx::execution::sequenced_policy>( - hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) { - return val.get(); - }) == hpx::util::end(results); - }); + auto f2 = [](auto&& results) { + return detail::sequential_find_if_not< + hpx::execution::sequenced_policy>( + hpx::util::begin(results), + hpx::util::end(results), + hpx::functional::unwrap{}) == + hpx::util::end(results); + }; + + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), + first, detail::distance(first, last), HPX_MOVE(f1), + HPX_MOVE(f2)); } }; /// \endcond @@ -559,8 +589,7 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( none_of_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, @@ -601,8 +630,7 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( any_of_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, @@ -643,8 +671,7 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( all_of_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp index 22b9bf65200a..c1eca482f7b8 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/copy.hpp @@ -404,9 +404,7 @@ namespace hpx::parallel { template - static typename util::detail::algorithm_result>::type - parallel([[maybe_unused]] ExPolicy&& policy, + static decltype(auto) parallel([[maybe_unused]] ExPolicy&& policy, [[maybe_unused]] FwdIter1 first, [[maybe_unused]] Sent1 last, [[maybe_unused]] FwdIter2 dest) { @@ -492,10 +490,8 @@ namespace hpx::parallel { } template - static typename util::detail::algorithm_result>::type - parallel(ExPolicy&& policy, FwdIter1 first, std::size_t count, - FwdIter2 dest) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter1 first, + std::size_t count, FwdIter2 dest) { using zip_iterator = hpx::util::zip_iterator; @@ -674,10 +670,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::copy_t, ExPolicy&& policy, FwdIter1 first, - FwdIter1 last, FwdIter2 dest) + friend decltype(auto) tag_fallback_invoke(hpx::copy_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest) { return parallel::util::get_second_element( parallel::detail::transfer< @@ -717,10 +711,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::copy_n_t, ExPolicy&& policy, FwdIter1 first, - Size count, FwdIter2 dest) + friend decltype(auto) tag_fallback_invoke(hpx::copy_n_t, + ExPolicy&& policy, FwdIter1 first, Size count, FwdIter2 dest) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -729,11 +721,21 @@ namespace hpx { hpx::traits::is_output_iterator_v), "Requires at least forward iterator or sequential execution."); - // if count is representing a negative value, we do nothing + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + if (hpx::parallel::detail::is_negative(count)) { - return hpx::parallel::util::detail::algorithm_result::get(HPX_MOVE(dest)); + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + // if count is representing a negative value, we do nothing + return hpx::parallel::util::detail::algorithm_result< + ExPolicy, FwdIter2>::get(HPX_MOVE(dest)); + } } return hpx::parallel::util::get_second_element( diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/count.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/count.hpp index 7c1c24116764..47213702ac67 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/count.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/count.hpp @@ -322,14 +322,20 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t - parallel(ExPolicy&& policy, IterB first, IterE last, T const& value, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, IterB first, + IterE last, T const& value, Proj&& proj) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(0); + if (first == last) + { + return util::detail:: + algorithm_result::get( + static_cast(0)); + } } auto f1 = @@ -382,14 +388,20 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t - parallel(ExPolicy&& policy, IterB first, IterE last, Pred&& op, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, IterB first, + IterE last, Pred&& op, Proj&& proj) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(0); + if (first == last) + { + return util::detail:: + algorithm_result::get( + static_cast(0)); + } } auto f1 = count_iteration( @@ -425,10 +437,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::difference_type>::type - tag_fallback_invoke(count_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T const& value) + friend decltype(auto) tag_fallback_invoke(count_t, ExPolicy&& policy, + FwdIter first, FwdIter last, T const& value) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -478,9 +488,7 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::difference_type>::type - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( count_if_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/destroy.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/destroy.hpp index 6dee2424370a..1e4049fef36b 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/destroy.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/destroy.hpp @@ -190,14 +190,19 @@ namespace hpx::parallel { /////////////////////////////////////////////////////////////////////// template - util::detail::algorithm_result_t - parallel_sequential_destroy_n( + decltype(auto) parallel_sequential_destroy_n( ExPolicy&& policy, Iter first, std::size_t count) { - if (count == 0) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - HPX_MOVE(first)); + if (count == 0) + { + return util::detail::algorithm_result::get( + HPX_MOVE(first)); + } } return util::foreach_partitioner::call( @@ -227,7 +232,7 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, Iter first, Sent last) { return parallel_sequential_destroy_n( @@ -270,7 +275,7 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, Iter first, std::size_t count) { return parallel_sequential_destroy_n( @@ -296,9 +301,7 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result< - ExPolicy>::type - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( destroy_t, ExPolicy&& policy, FwdIter first, FwdIter last) { static_assert(hpx::traits::is_forward_iterator_v, @@ -338,19 +341,26 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( destroy_n_t, ExPolicy&& policy, FwdIter first, Size count) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - // if count is representing a negative value, we do nothing if (hpx::parallel::detail::is_negative(count)) { - return hpx::parallel::util::detail::algorithm_result::get(HPX_MOVE(first)); + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + // if count is representing a negative value, we do nothing + return hpx::parallel::util::detail::algorithm_result< + ExPolicy, FwdIter>::get(HPX_MOVE(first)); + } } return hpx::parallel::detail::destroy_n().call( diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/detail/search.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/search.hpp index 6cd29d41c2b3..0172494cbd1a 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/detail/search.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/search.hpp @@ -65,52 +65,65 @@ namespace hpx::parallel::detail { template - static hpx::parallel::util::detail::algorithm_result_t - parallel(ExPolicy&& orgpolicy, FwdIter first, Sent last, - FwdIter2 s_first, Sent2 s_last, Pred&& op, Proj1&& proj1, + static decltype(auto) parallel(ExPolicy&& orgpolicy, FwdIter first, + Sent last, FwdIter2 s_first, Sent2 s_last, Pred&& op, Proj1&& proj1, Proj2&& proj2) { using reference = typename std::iterator_traits::reference; - using difference_type = typename std::iterator_traits::difference_type; - using s_difference_type = typename std::iterator_traits::difference_type; - using result = hpx::parallel::util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; // Use of hpx::distance instead of std::distance to support // sentinels s_difference_type diff = hpx::parallel::detail::distance(s_first, s_last); - if (diff <= 0) - return result::get(HPX_MOVE(first)); + + if constexpr (!has_scheduler_executor) + { + if (diff <= 0) + return result::get(HPX_MOVE(first)); + } difference_type count = hpx::parallel::detail::distance(first, last); - if (diff > count) + + if constexpr (!has_scheduler_executor) { - std::advance( - first, hpx::parallel::detail::distance(first, last) - 1); - return result::get(HPX_MOVE(first)); + if (diff > count) + { + std::advance( + first, hpx::parallel::detail::distance(first, last)); + return result::get(HPX_MOVE(first)); + } + } + + hpx::parallel::util::cancellation_token tok(count); + auto partitioner_count = count - (diff - 1); + + if constexpr (has_scheduler_executor) + { + if (diff <= 0 || diff > count) + partitioner_count = 0; + + if (diff <= 0) + tok.cancel(0); } decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), hpx::threads::thread_placement_hint::breadth_first); - using policy_type = std::decay_t; - using partitioner = hpx::parallel::util::partitioner; - hpx::parallel::util::cancellation_token tok(count); - auto f1 = [diff, count, tok, s_first, op = HPX_FORWARD(Pred, op), proj1 = HPX_FORWARD(Proj1, proj1), proj2 = HPX_FORWARD(Proj2, proj2)](FwdIter it, @@ -148,10 +161,15 @@ namespace hpx::parallel::detail { }); }; - auto f2 = [=](auto&& data) mutable -> FwdIter { - // make sure iterators embedded in function object that is - // attached to futures are invalidated - util::detail::clear_container(data); + auto f2 = [=](auto&&... data) mutable -> FwdIter { + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + // make sure iterators embedded in function object that is + // attached to futures are invalidated + util::detail::clear_container(data...); + } + difference_type search_res = tok.get_data(); if (search_res != count) { @@ -159,16 +177,16 @@ namespace hpx::parallel::detail { } else { - std::advance(first, - hpx::parallel::detail::distance(first, last) - 1); + std::advance( + first, hpx::parallel::detail::distance(first, last)); } return HPX_MOVE(first); }; return partitioner::call_with_index( - HPX_FORWARD(decltype(policy), policy), first, - count - (diff - 1), 1, HPX_MOVE(f1), HPX_MOVE(f2)); + HPX_FORWARD(decltype(policy), policy), first, partitioner_count, + 1, HPX_MOVE(f1), HPX_MOVE(f2)); } }; diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/detail/transfer.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/transfer.hpp index 052abd9ffc12..fb1dd8b098c7 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/detail/transfer.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/transfer.hpp @@ -48,10 +48,8 @@ namespace hpx::parallel { // parallel version template - typename util::detail::algorithm_result>::type - transfer_(ExPolicy&& policy, FwdIter1 first, Sent1 last, FwdIter2 dest, - std::false_type) + decltype(auto) transfer_(ExPolicy&& policy, FwdIter1 first, Sent1 last, + FwdIter2 dest, std::false_type) { return Algo().call( HPX_FORWARD(ExPolicy, policy), first, last, dest); @@ -115,9 +113,8 @@ namespace hpx::parallel { hpx::traits::is_iterator_v )> // clang-format on - typename util::detail::algorithm_result>::type - transfer(ExPolicy&& policy, FwdIter1 first, Sent1 last, FwdIter2 dest) + decltype(auto) transfer( + ExPolicy&& policy, FwdIter1 first, Sent1 last, FwdIter2 dest) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/ends_with.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/ends_with.hpp index 79bf1356a071..8234f5c3ca39 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/ends_with.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/ends_with.hpp @@ -147,41 +147,54 @@ namespace hpx::parallel { Sent1 last1, Iter2 first2, Sent2 last2, Pred&& pred, Proj1&& proj1, Proj2&& proj2) { - auto const drop = detail::distance(first1, last1) - - detail::distance(first2, last2); + auto distance1 = detail::distance(first1, last1); + auto distance2 = detail::distance(first2, last2); - if (drop < 0) + if (distance1 < distance2) return false; + std::advance(first1, distance1 - distance2); + return hpx::parallel::detail::equal_binary().call( - hpx::execution::seq, std::next(HPX_MOVE(first1), drop), - HPX_MOVE(last1), HPX_MOVE(first2), HPX_MOVE(last2), - HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj1, proj1), - HPX_FORWARD(Proj2, proj2)); + hpx::execution::seq, HPX_MOVE(first1), HPX_MOVE(last1), + HPX_MOVE(first2), HPX_MOVE(last2), HPX_FORWARD(Pred, pred), + HPX_FORWARD(Proj1, proj1), HPX_FORWARD(Proj2, proj2)); } template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIter1 first1, Sent1 last1, - FwdIter2 first2, Sent2 last2, Pred&& pred, Proj1&& proj1, - Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter1 first1, + Sent1 last1, FwdIter2 first2, Sent2 last2, Pred&& pred, + Proj1&& proj1, Proj2&& proj2) { - auto const drop = detail::distance(first1, last1) - - detail::distance(first2, last2); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - if (drop < 0) + auto distance1 = detail::distance(first1, last1); + auto distance2 = detail::distance(first2, last2); + auto diff = distance1 - distance2; + + if (distance1 < distance2) { - return util::detail::algorithm_result::get( - false); + if constexpr (has_scheduler_executor) + { + diff = 0; + } + else + { + return util::detail::algorithm_result::get(false); + } } + std::advance(first1, diff); + return hpx::parallel::detail::equal_binary().call( - HPX_FORWARD(ExPolicy, policy), - std::next(HPX_MOVE(first1), drop), HPX_MOVE(last1), - HPX_MOVE(first2), HPX_MOVE(last2), HPX_FORWARD(Pred, pred), - HPX_FORWARD(Proj1, proj1), HPX_FORWARD(Proj2, proj2)); + HPX_FORWARD(ExPolicy, policy), HPX_MOVE(first1), + HPX_MOVE(last1), HPX_MOVE(first2), HPX_MOVE(last2), + HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj1, proj1), + HPX_FORWARD(Proj2, proj2)); } }; /// \endcond @@ -193,7 +206,7 @@ namespace hpx { /////////////////////////////////////////////////////////////////////////// // CPO for hpx::ends_with inline constexpr struct ends_with_t final - : hpx::functional::detail::tag_fallback + : hpx::detail::tag_parallel_algorithm { private: // clang-format off @@ -235,11 +248,9 @@ namespace hpx { > )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::ends_with_t, ExPolicy&& policy, - FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, - Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke(hpx::ends_with_t, + ExPolicy&& policy, FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, + FwdIter2 last2, Pred pred = Pred()) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/equal.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/equal.hpp index fd951af7974f..af6e00f81031 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/equal.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/equal.hpp @@ -500,29 +500,32 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, Iter1 first1, Sent1 last1, Iter2 first2, - Sent2 last2, F&& f, Proj1&& proj1, Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& orgpolicy, Iter1 first1, + Sent1 last1, Iter2 first2, Sent2 last2, F&& f, Proj1&& proj1, + Proj2&& proj2) { using difference_type1 = typename std::iterator_traits::difference_type; using difference_type2 = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + execution_policy_has_scheduler_executor_v; - if (first1 == last1) + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - first2 == last2); + if (first1 == last1) + { + return util::detail::algorithm_result::get(first2 == last2); + } + + if (first2 == last2) + { + return util::detail::algorithm_result::get(false); + } } - if (first2 == last2) - { - return util::detail::algorithm_result::get( - false); - } - - difference_type1 count1 = detail::distance(first1, last1); - // The specification of std::equal(_binary) states that if // FwdIter1 and FwdIter2 meet the requirements of // RandomAccessIterator and last1 - first1 != last2 - first2 @@ -530,11 +533,18 @@ namespace hpx::parallel { // // We perform this check for any iterator type better than input // iterators. This could turn into a QoI issue. + + difference_type1 count1 = detail::distance(first1, last1); difference_type2 count2 = detail::distance(first2, last2); - if (count1 != count2) + bool different_lengths = (count1 != count2); + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - false); + if (different_lengths) + { + return util::detail::algorithm_result::get(false); + } } auto policy = parallel::util::adapt_placement_mode( @@ -543,27 +553,41 @@ namespace hpx::parallel { using policy_type = decltype(policy); using zip_iterator = hpx::util::zip_iterator; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; auto f1 = [tok, f = HPX_FORWARD(F, f), proj1 = HPX_FORWARD(Proj1, proj1), proj2 = HPX_FORWARD(Proj2, proj2)]( - zip_iterator it, - std::size_t part_count) mutable -> bool { + zip_iterator it, std::size_t part_count) mutable + -> intermediate_result_t { sequential_equal_binary(it, part_count, tok, HPX_FORWARD(F, f), HPX_FORWARD(Proj1, proj1), HPX_FORWARD(Proj2, proj2)); return !tok.was_cancelled(); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), zip_iterator(first1, first2), count1, HPX_MOVE(f1), - [](auto&& results) -> bool { + [different_lengths](auto&& results) -> bool { + if constexpr (has_scheduler_executor) + { + if (different_lengths) + { + return false; + } + } + else + { + HPX_UNUSED(different_lengths); + } + return std::all_of(hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) { return val.get(); }); + hpx::util::end(results), hpx::functional::unwrap{}); }); } }; @@ -592,14 +616,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, FwdIter1 first1, FwdIter1 last1, - FwdIter2 first2, F&& f) + static decltype(auto) parallel(ExPolicy&& orgpolicy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, F&& f) { - if (first1 == last1) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - true); + if (first1 == last1) + { + return util::detail::algorithm_result::get(true); + } } using difference_type = @@ -613,22 +642,25 @@ namespace hpx::parallel { using policy_type = std::decay_t; using zip_iterator = hpx::util::zip_iterator; + using intermediate_result_t = + std::conditional_t; util::cancellation_token<> tok; - auto f1 = [f, tok](zip_iterator it, - std::size_t part_count) mutable -> bool { + auto f1 = [f, tok]( + zip_iterator it, std::size_t part_count) mutable + -> intermediate_result_t { sequential_equal( it, part_count, tok, HPX_FORWARD(F, f)); return !tok.was_cancelled(); }; - return util::partitioner::call( - HPX_FORWARD(decltype(policy), policy), + return util::partitioner::call(HPX_FORWARD(decltype(policy), + policy), zip_iterator(first1, first2), count, HPX_MOVE(f1), [](auto&& results) { return std::all_of(hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) { return val.get(); }); + hpx::util::end(results), hpx::functional::unwrap{}); }); } }; @@ -657,9 +689,9 @@ namespace hpx { > )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(equal_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, Pred op) + friend decltype(auto) tag_fallback_invoke(equal_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, + Pred op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -679,9 +711,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(equal_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, FwdIter2 last2) + friend decltype(auto) tag_fallback_invoke(equal_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -707,9 +738,8 @@ namespace hpx { > )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(equal_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, Pred op) + friend decltype(auto) tag_fallback_invoke(equal_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, Pred op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -729,9 +759,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(equal_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2) + friend decltype(auto) tag_fallback_invoke(equal_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/fill.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/fill.hpp index 97c5302b2957..ea7887487d83 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/fill.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/fill.hpp @@ -221,13 +221,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, Sent last, T const& val) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(HPX_MOVE(first)); + if (first == last) + { + return util::detail::algorithm_result::get(HPX_MOVE(first)); + } } return for_each_n().call(HPX_FORWARD(ExPolicy, policy), @@ -260,9 +266,8 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIter first, std::size_t count, - T const& val) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter first, + std::size_t count, T const& val) { return for_each_n().call( HPX_FORWARD(ExPolicy, policy), first, count, @@ -289,10 +294,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result< - ExPolicy>::type - tag_fallback_invoke(fill_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T const& value) + friend decltype(auto) tag_fallback_invoke(fill_t, ExPolicy&& policy, + FwdIter first, FwdIter last, T const& value) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -338,19 +341,27 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(fill_n_t, ExPolicy&& policy, FwdIter first, - Size count, T const& value) + friend decltype(auto) tag_fallback_invoke(fill_n_t, ExPolicy&& policy, + FwdIter first, Size count, T const& value) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + // if count is representing a negative value, we do nothing if (hpx::parallel::detail::is_negative(count)) { - return hpx::parallel::util::detail::algorithm_result::get(HPX_MOVE(first)); + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + return hpx::parallel::util::detail::algorithm_result< + ExPolicy, FwdIter>::get(HPX_MOVE(first)); + } } return hpx::parallel::detail::fill_n().call( diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/find.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/find.hpp index effbc144dfc7..edf55211eeb9 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/find.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/find.hpp @@ -831,17 +831,19 @@ namespace hpx::parallel { using result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - difference_type count = detail::distance(first, last); - if constexpr (!hpx::execution_policy_has_scheduler_executor_v< - ExPolicy>) + if constexpr (!has_scheduler_executor) { - if (count <= 0) + if (first == last) { return result::get(HPX_MOVE(last)); } } + difference_type count = detail::distance(first, last); + decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), hpx::threads::thread_placement_hint::breadth_first); @@ -878,7 +880,7 @@ namespace hpx::parallel { { first = detail::advance_to_sentinel(first, last); } - return HPX_MOVE(first); + return first; }; using partitioner_type = @@ -914,17 +916,24 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, Iter first, Sent last, F&& f, - Proj&& proj = Proj()) + static decltype(auto) parallel(ExPolicy&& orgpolicy, Iter first, + Sent last, F&& f, Proj&& proj = Proj()) { using result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) + { + if (first == last) + { + return result::get(HPX_MOVE(last)); + } + } difference_type count = detail::distance(first, last); - if (count <= 0) - return result::get(HPX_MOVE(last)); decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), @@ -943,10 +952,14 @@ namespace hpx::parallel { }; auto f2 = [tok, count, first, last]( - auto&& data) mutable -> Iter { + auto&&... data) mutable -> Iter { // make sure iterators embedded in the function objects that // are attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } auto find_res = static_cast(tok.get_data()); @@ -959,7 +972,7 @@ namespace hpx::parallel { { first = detail::advance_to_sentinel(first, last); } - return HPX_MOVE(first); + return first; }; using partitioner_type = @@ -994,17 +1007,24 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, Iter first, Sent last, F&& f, - Proj&& proj = Proj()) + static decltype(auto) parallel(ExPolicy&& orgpolicy, Iter first, + Sent last, F&& f, Proj&& proj = Proj()) { using result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_policy = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_policy) + { + if (first == last) + { + return result::get(HPX_MOVE(last)); + } + } difference_type count = detail::distance(first, last); - if (count <= 0) - return result::get(HPX_MOVE(last)); decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), @@ -1023,10 +1043,14 @@ namespace hpx::parallel { }; auto f2 = [tok, count, first, last]( - auto&& data) mutable -> Iter { + auto&&... data) mutable -> Iter { // make sure iterators embedded in the function objects that // are attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } auto find_res = static_cast(tok.get_data()); @@ -1039,7 +1063,7 @@ namespace hpx::parallel { { first = detail::advance_to_sentinel(first, last); } - return HPX_MOVE(first); + return first; }; using partitioner_type = @@ -1078,26 +1102,41 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, Iter1 first1, Sent1 last1, Iter2 first2, - Sent2 last2, Pred&& op, Proj1&& proj1, Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& orgpolicy, Iter1 first1, + Sent1 last1, Iter2 first2, Sent2 last2, Pred&& op, + Proj1&& proj1, Proj2&& proj2) { using result_type = util::detail::algorithm_result; - using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - difference_type diff = detail::distance(first2, last2); - if (diff <= 0) + if constexpr (!has_scheduler_executor) { - return result_type::get(HPX_MOVE(last1)); + if (first2 == last2) + { + return result_type::get(HPX_MOVE(last1)); + } } difference_type count = detail::distance(first1, last1); - if (diff > count) + difference_type diff = detail::distance(first2, last2); + + if constexpr (!has_scheduler_executor) { - return result_type::get(HPX_MOVE(last1)); + if (diff > count) + { + return result_type::get(HPX_MOVE(last1)); + } + } + + difference_type partitioner_count = count - diff + 1; + if constexpr (has_scheduler_executor) + { + if (diff == 0 || diff > count) + partitioner_count = static_cast(0); } decltype(auto) policy = parallel::util::adapt_placement_mode( @@ -1121,10 +1160,14 @@ namespace hpx::parallel { }; auto f2 = [tok, count, first1, last1]( - auto&& data) mutable -> Iter1 { + auto&&... data) mutable -> Iter1 { // make sure iterators embedded in the function objects that // are attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } difference_type find_end_res = tok.get_data(); @@ -1136,14 +1179,14 @@ namespace hpx::parallel { { first1 = last1; } - return HPX_MOVE(first1); + return first1; }; using partitioner_type = util::partitioner; return partitioner_type::call_with_index( HPX_FORWARD(decltype(policy), policy), first1, - count - diff + 1, 1, HPX_MOVE(f1), HPX_MOVE(f2)); + partitioner_count, 1, HPX_MOVE(f1), HPX_MOVE(f2)); } }; } // namespace detail @@ -1173,25 +1216,36 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, FwdIter first, FwdIter last, - FwdIter2 s_first, FwdIter2 s_last, Pred&& op, Proj1&& proj1, - Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& orgpolicy, FwdIter first, + FwdIter last, FwdIter2 s_first, FwdIter2 s_last, Pred&& op, + Proj1&& proj1, Proj2&& proj2) { using result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; - using s_difference_type = - typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - s_difference_type diff = std::distance(s_first, s_last); - if (diff <= 0) - return result::get(HPX_MOVE(last)); + if constexpr (!has_scheduler_executor) + { + if (first == last) + return result::get(HPX_MOVE(last)); + } difference_type count = std::distance(first, last); - if (diff > count) - return result::get(HPX_MOVE(last)); + + if (s_first == s_last) + { + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + return result::get(HPX_MOVE(last)); + } + } decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), @@ -1212,10 +1266,14 @@ namespace hpx::parallel { }; auto f2 = [tok, count, first, last]( - auto&& data) mutable -> FwdIter { + auto&&... data) mutable -> FwdIter { // make sure iterators embedded in the function objects that // are attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } difference_type find_first_of_res = tok.get_data(); @@ -1228,7 +1286,7 @@ namespace hpx::parallel { first = last; } - return HPX_MOVE(first); + return first; }; using partitioner_type = @@ -1302,9 +1360,7 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( find_if_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, @@ -1351,9 +1407,7 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( find_if_not_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, @@ -1403,10 +1457,9 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(find_end_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, Pred op) + friend decltype(auto) tag_fallback_invoke(find_end_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, + Pred op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -1426,10 +1479,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(find_end_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, FwdIter2 last2) + friend decltype(auto) tag_fallback_invoke(find_end_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -1508,10 +1559,9 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(find_first_of_t, ExPolicy&& policy, FwdIter1 first, - FwdIter1 last, FwdIter2 s_first, FwdIter2 s_last, Pred op) + friend decltype(auto) tag_fallback_invoke(find_first_of_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 s_first, + FwdIter2 s_last, Pred op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -1531,10 +1581,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(find_first_of_t, ExPolicy&& policy, FwdIter1 first, - FwdIter1 last, FwdIter2 s_first, FwdIter2 s_last) + friend decltype(auto) tag_fallback_invoke(find_first_of_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 s_first, + FwdIter2 s_last) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/for_each.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/for_each.hpp index 83688e3bd469..8f90c42cbb6a 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/for_each.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/for_each.hpp @@ -625,19 +625,29 @@ namespace hpx { std::is_integral_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::for_each_n_t, ExPolicy&& policy, FwdIter first, - Size count, F f) + friend decltype(auto) tag_fallback_invoke(hpx::for_each_n_t, + ExPolicy&& policy, FwdIter first, Size count, F f) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + // if count is representing a negative value, we do nothing if (parallel::detail::is_negative(count)) { - using result = - parallel::util::detail::algorithm_result; - return result::get(HPX_MOVE(first)); + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + using result = + parallel::util::detail::algorithm_result; + return result::get(HPX_MOVE(first)); + } } return hpx::parallel::detail::for_each_n().call( diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/generate.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/generate.hpp index eb3cfdf5075a..fdc3dc4ecac8 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/generate.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/generate.hpp @@ -245,7 +245,7 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, Iter first, Sent last, F&& f) { auto f1 = [policy, f = HPX_FORWARD(F, f)]( @@ -287,7 +287,7 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, std::size_t count, F&& f) { auto f1 = [policy, f = HPX_FORWARD(F, f)](FwdIter part_begin, @@ -322,9 +322,7 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( generate_t, ExPolicy&& policy, FwdIter first, FwdIter last, F f) { static_assert(hpx::traits::is_forward_iterator_v, @@ -365,18 +363,26 @@ namespace hpx { std::is_integral_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke( + friend decltype(auto) tag_fallback_invoke( generate_n_t, ExPolicy&& policy, FwdIter first, Size count, F f) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); + constexpr bool has_scheduler_executor = + execution_policy_has_scheduler_executor_v; + if (hpx::parallel::detail::is_negative(count)) { - return hpx::parallel::util::detail::algorithm_result::get(HPX_MOVE(first)); + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + } + else + { + return hpx::parallel::util::detail::algorithm_result< + ExPolicy, FwdIter>::get(HPX_MOVE(first)); + } } return hpx::parallel::detail::generate_n().call( diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/includes.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/includes.hpp index 660e33b53c48..71c03d0585d8 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/includes.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/includes.hpp @@ -271,28 +271,38 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, Iter1 first1, Sent1 last1, Iter2 first2, - Sent2 last2, F&& f, Proj1&& proj1, Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& policy, Iter1 first1, + Sent1 last1, Iter2 first2, Sent2 last2, F&& f, Proj1&& proj1, + Proj2&& proj2) { - if (first1 == last1) - { - return util::detail::algorithm_result::get( - false); - } - if (first2 == last2) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - true); + if (first2 == last2) + { + return util::detail::algorithm_result::get(true); + } + if (first1 == last1) + { + return util::detail::algorithm_result::get(false); + } } + using intermediate_result_t = + std::conditional_t; + util::cancellation_token<> tok; - auto f1 = - [first1, last1, first2, last2, tok, f = HPX_FORWARD(F, f), - proj1 = HPX_FORWARD(Proj1, proj1), - proj2 = HPX_FORWARD(Proj2, proj2)](Iter2 part_begin, - std::size_t part_count) mutable -> bool { + auto f1 = [first1, last1, first2, last2, tok, + f = HPX_FORWARD(F, f), + proj1 = HPX_FORWARD(Proj1, proj1), + proj2 = HPX_FORWARD(Proj2, proj2)]( + Iter2 part_begin, std::size_t part_count) mutable + -> intermediate_result_t { Iter2 part_end = detail::next(part_begin, part_count); auto value = HPX_INVOKE(proj2, *part_begin); @@ -351,13 +361,12 @@ namespace hpx::parallel { auto f2 = [](auto&& results) { return std::all_of(hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) { return val.get(); }); + hpx::util::end(results), hpx::functional::unwrap{}); }; - return util::partitioner::call( - HPX_FORWARD(ExPolicy, policy), first2, - detail::distance(first2, last2), HPX_MOVE(f1), + return util::partitioner::call(HPX_FORWARD(ExPolicy, policy), + first2, detail::distance(first2, last2), HPX_MOVE(f1), HPX_MOVE(f2)); } }; @@ -385,9 +394,9 @@ namespace hpx { > )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(includes_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, Pred op = Pred()) + friend decltype(auto) tag_fallback_invoke(includes_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, + Pred op = Pred()) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/is_heap.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/is_heap.hpp index 843c1972bf1c..52ed9f5d3b5e 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/is_heap.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/is_heap.hpp @@ -263,19 +263,32 @@ namespace hpx::parallel { { template - util::detail::algorithm_result_t operator()( - ExPolicy&& orgpolicy, Iter first, Sent last, Comp&& comp, - Proj&& proj) + decltype(auto) operator()(ExPolicy&& orgpolicy, Iter first, + Sent last, Comp&& comp, Proj&& proj) { using result = util::detail::algorithm_result; using type = typename std::iterator_traits::value_type; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = detail::distance(first, last); - if (count <= 1) + + if constexpr (has_scheduler_executor) { - return result::get(true); + if (count == 0) + { + // underflow prevention + ++count; + } + } + else + { + if (count <= 1) + { + return result::get(true); + } } Iter second = first + 1; @@ -307,15 +320,19 @@ namespace hpx::parallel { }); }; - auto f2 = [tok](auto&& data) mutable -> bool { - // make sure iterators embedded in function object that is - // attached to futures are invalidated - util::detail::clear_container(data); + auto f2 = [tok, count](auto&&... data) mutable -> bool { + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + // make sure iterators embedded in function object that + // is attached to futures are invalidated + util::detail::clear_container(data...); + } difference_type find_res = static_cast(tok.get_data()); - return find_res != 0; + return find_res == count; }; using partitioner_type = @@ -345,9 +362,8 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, Iter first, Sent last, Comp&& comp, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, Iter first, + Sent last, Comp&& comp, Proj&& proj) { return is_heap_helper()(HPX_FORWARD(ExPolicy, policy), first, last, HPX_FORWARD(Comp, comp), HPX_FORWARD(Proj, proj)); @@ -382,23 +398,36 @@ namespace hpx::parallel { { template - util::detail::algorithm_result_t operator()( - ExPolicy&& orgpolicy, Iter first, Sent last, Comp comp, - Proj proj) + decltype(auto) operator()(ExPolicy&& orgpolicy, Iter first, + Sent last, Comp comp, Proj proj) { using result = util::detail::algorithm_result; using type = typename std::iterator_traits::value_type; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = detail::distance(first, last); + + Iter second; if (count <= 1) { - return result::get(HPX_MOVE(last)); + if constexpr (has_scheduler_executor) + { + count = static_cast(0); + second = last; + } + else + { + return result::get(HPX_MOVE(last)); + } + } + else + { + second = first + 1; + --count; } - - Iter second = first + 1; - --count; decltype(auto) policy = parallel::util::adapt_placement_mode( HPX_FORWARD(ExPolicy, orgpolicy), @@ -426,17 +455,21 @@ namespace hpx::parallel { }); }; - auto f2 = [tok, second](auto&& data) mutable -> Iter { - // make sure iterators embedded in function object that is - // attached to futures are invalidated - util::detail::clear_container(data); + auto f2 = [tok, second](auto&&... data) mutable -> Iter { + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + // make sure iterators embedded in function object that is + // attached to futures are invalidated + util::detail::clear_container(data...); + } difference_type find_res = static_cast(tok.get_data()); std::advance(second, find_res); - return HPX_MOVE(second); + return second; }; using partitioner_type = @@ -467,9 +500,8 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, Iter first, Sent last, Comp&& comp, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, Iter first, + Sent last, Comp&& comp, Proj&& proj) { return is_heap_until_helper()(HPX_FORWARD(ExPolicy, policy), first, last, HPX_FORWARD(Comp, comp), @@ -499,10 +531,8 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(is_heap_t, ExPolicy&& policy, RandIter first, - RandIter last, Comp comp = Comp()) + friend decltype(auto) tag_fallback_invoke(is_heap_t, ExPolicy&& policy, + RandIter first, RandIter last, Comp comp = Comp()) { static_assert(hpx::traits::is_random_access_iterator_v, "Requires a random access iterator."); @@ -553,10 +583,9 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(is_heap_until_t, ExPolicy&& policy, RandIter first, - RandIter last, Comp comp = Comp()) + friend decltype(auto) tag_fallback_invoke(is_heap_until_t, + ExPolicy&& policy, RandIter first, RandIter last, + Comp comp = Comp()) { static_assert(hpx::traits::is_random_access_iterator_v, "Requires a random access iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/is_partitioned.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/is_partitioned.hpp index dc62cfe105e3..1037e92c515c 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/is_partitioned.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/is_partitioned.hpp @@ -136,12 +136,12 @@ namespace hpx::parallel { namespace detail { /// \cond NOINTERNAL - inline bool sequential_is_partitioned( - std::vector>&& res) + template + inline bool sequential_is_partitioned(std::vector&& res) { auto first = res.begin(); auto const last = res.end(); - while (first != last && first->get()) + while (first != last && hpx::unwrap(*first)) { ++first; } @@ -150,7 +150,7 @@ namespace hpx::parallel { ++first; while (first != last) { - if (first->get()) + if (hpx::unwrap(*first)) return false; ++first; } @@ -178,28 +178,34 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, Iter first, Sent last, Pred&& pred, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, Iter first, + Sent last, Pred&& pred, Proj&& proj) { using difference_type = typename std::iterator_traits::difference_type; using result = util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = std::distance(first, last); - if (count <= 1) - return result::get(true); + + if constexpr (!has_scheduler_executor) + { + if (count <= 1) + return result::get(true); + } util::invoke_projected pred_projected( HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj)); - util::cancellation_token<> tok; + using intermediate_result_t = + std::conditional_t; // Note: replacing the invoke() with HPX_INVOKE() // below makes gcc generate errors auto f1 = [tok, pred_projected = HPX_MOVE(pred_projected)]( - Iter part_begin, - std::size_t part_count) mutable -> bool { + Iter part_begin, std::size_t part_count) mutable + -> intermediate_result_t { bool fst_bool = HPX_INVOKE(pred_projected, *part_begin); if (part_count == 1) return fst_bool; @@ -226,9 +232,9 @@ namespace hpx::parallel { return sequential_is_partitioned(HPX_MOVE(results)); }; - return util::partitioner::call( - HPX_FORWARD(ExPolicy, policy), first, count, HPX_MOVE(f1), - HPX_MOVE(f2)); + return util::partitioner::call(HPX_FORWARD(ExPolicy, policy), + first, count, HPX_MOVE(f1), HPX_MOVE(f2)); } }; /// \endcond @@ -263,10 +269,8 @@ namespace hpx { hpx::traits::is_forward_iterator_v )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::is_partitioned_t, ExPolicy&& policy, - FwdIter first, FwdIter last, Pred pred) + friend decltype(auto) tag_fallback_invoke(hpx::is_partitioned_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred) { return hpx::parallel::detail::is_partitioned() .call(HPX_FORWARD(ExPolicy, policy), first, last, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/is_sorted.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/is_sorted.hpp index a7613d7a8974..ca68e2c32a8e 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/is_sorted.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/is_sorted.hpp @@ -272,30 +272,36 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIter first, Sent last, Pred&& pred, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter first, + Sent last, Pred&& pred, Proj&& proj) { using difference_type = typename std::iterator_traits::difference_type; using result = typename util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = std::distance(first, last); - if (count <= 1) - return result::get(true); + + if constexpr (!has_scheduler_executor) + { + if (count <= 1) + return result::get(true); + } util::invoke_projected pred_projected{ HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj)}; - hpx::parallel::util::cancellation_token<> tok; + using intermediate_result_t = + std::conditional_t; // Note: replacing the invoke() with HPX_INVOKE() // below makes gcc generate errors auto f1 = [tok, last, pred_projected = HPX_MOVE(pred_projected)]( - FwdIter part_begin, - std::size_t part_size) mutable -> bool { + FwdIter part_begin, std::size_t part_size) mutable + -> intermediate_result_t { FwdIter trail = part_begin++; util::loop_n>(part_begin, part_size - 1, @@ -322,15 +328,12 @@ namespace hpx::parallel { auto f2 = [](auto&& results) { return std::all_of(hpx::util::begin(results), - hpx::util::end(results), - [](hpx::future& val) -> bool { - return val.get(); - }); + hpx::util::end(results), hpx::functional::unwrap{}); }; - return util::partitioner::call( - HPX_FORWARD(ExPolicy, policy), first, count, HPX_MOVE(f1), - HPX_MOVE(f2)); + return util::partitioner::call(HPX_FORWARD(ExPolicy, policy), + first, count, HPX_MOVE(f1), HPX_MOVE(f2)); } }; /// \endcond @@ -358,9 +361,8 @@ namespace hpx::parallel { } template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, FwdIter first, Sent last, Pred&& pred, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& orgpolicy, FwdIter first, + Sent last, Pred&& pred, Proj&& proj) { using reference = typename std::iterator_traits::reference; @@ -368,10 +370,15 @@ namespace hpx::parallel { typename std::iterator_traits::difference_type; using result = typename util::detail::algorithm_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = std::distance(first, last); - if (count <= 1) - return result::get(HPX_MOVE(last)); + if constexpr (!has_scheduler_executor) + { + if (count <= 1) + return result::get(HPX_MOVE(last)); + } util::invoke_projected pred_projected{ HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj, proj)}; @@ -417,14 +424,18 @@ namespace hpx::parallel { } }; - auto f2 = [first, tok](auto&& data) mutable -> FwdIter { + auto f2 = [first, tok](auto&&... data) mutable -> FwdIter { // make sure iterators embedded in function object that is // attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } difference_type loc = tok.get_data(); std::advance(first, loc); - return HPX_MOVE(first); + return first; }; using partitioner_type = @@ -474,10 +485,8 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::is_sorted_t, ExPolicy&& policy, FwdIter first, - FwdIter last, Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke(hpx::is_sorted_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred = Pred()) { return hpx::parallel::detail::is_sorted().call( HPX_FORWARD(ExPolicy, policy), first, last, HPX_MOVE(pred), @@ -519,10 +528,8 @@ namespace hpx { > )> // clang-format on - friend typename hpx::parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::is_sorted_until_t, ExPolicy&& policy, - FwdIter first, FwdIter last, Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke(hpx::is_sorted_until_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred = Pred()) { return hpx::parallel::detail::is_sorted_until() .call(HPX_FORWARD(ExPolicy, policy), first, last, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/lexicographical_compare.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/lexicographical_compare.hpp index 11650877977f..5ef38f97ac6a 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/lexicographical_compare.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/lexicographical_compare.hpp @@ -224,30 +224,34 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& orgpolicy, FwdIter1 first1, Sent1 last1, - FwdIter2 first2, Sent2 last2, Pred&& pred, Proj1&& proj1, - Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& orgpolicy, + FwdIter1 first1, Sent1 last1, FwdIter2 first2, Sent2 last2, + Pred&& pred, Proj1&& proj1, Proj2&& proj2) { using zip_iterator = hpx::util::zip_iterator; using reference = typename zip_iterator::reference; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; std::size_t const count1 = detail::distance(first1, last1); std::size_t const count2 = detail::distance(first2, last2); // An empty range is lexicographically less than any non-empty // range - if (count1 == 0 && count2 != 0) + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - true); - } + if (count1 == 0 && count2 != 0) + { + return util::detail::algorithm_result::get(true); + } - if (count2 == 0 && count1 != 0) - { - return util::detail::algorithm_result::get( - false); + if (count2 == 0 && count1 != 0) + { + return util::detail::algorithm_result::get(false); + } } decltype(auto) policy = parallel::util::adapt_placement_mode( @@ -279,10 +283,14 @@ namespace hpx::parallel { }; auto f2 = [tok, first1, first2, last1, last2, pred, proj1, - proj2](auto&& data) mutable -> bool { + proj2](auto&&... data) mutable -> bool { // make sure iterators embedded in function object that is // attached to futures are invalidated - util::detail::clear_container(data); + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + util::detail::clear_container(data...); + } std::size_t mismatched = tok.get_data(); @@ -357,10 +365,9 @@ namespace hpx { > )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::lexicographical_compare_t, ExPolicy&& policy, - FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, - Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke( + hpx::lexicographical_compare_t, ExPolicy&& policy, FwdIter1 first1, + FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, Pred pred = Pred()) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp index 1e76ca7cc5a9..04b73961902c 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/minmax.hpp @@ -493,13 +493,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, Sent last, F&& f, Proj&& proj) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(HPX_MOVE(first)); + if (first == last) + { + return util::detail::algorithm_result::get(HPX_MOVE(first)); + } } auto f1 = [f, proj, policy]( @@ -508,9 +514,21 @@ namespace hpx::parallel { policy, it, part_count, f, proj); }; - auto f2 = [policy, f = HPX_FORWARD(F, f), + auto f2 = [policy, first, f = HPX_FORWARD(F, f), proj = HPX_FORWARD(Proj, proj)]( auto&& positions) -> FwdIter { + if constexpr (has_scheduler_executor) + { + if (positions.size() == 0) + { + return first; + } + } + else + { + HPX_UNUSED(first); + } + return min_element::sequential_minmax_element_ind( policy, positions.begin(), positions.size(), f, proj); }; @@ -628,13 +646,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, Sent last, F&& f, Proj&& proj) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(HPX_MOVE(first)); + if (first == last) + { + return util::detail::algorithm_result::get(HPX_MOVE(first)); + } } auto f1 = [f, proj, policy]( @@ -643,9 +667,21 @@ namespace hpx::parallel { policy, it, part_count, f, proj); }; - auto f2 = [policy, f = HPX_FORWARD(F, f), + auto f2 = [policy, first, f = HPX_FORWARD(F, f), proj = HPX_FORWARD(Proj, proj)]( auto&& positions) -> FwdIter { + if constexpr (has_scheduler_executor) + { + if (positions.size() == 0) + { + return first; + } + } + else + { + HPX_UNUSED(first); + } + return max_element::sequential_minmax_element_ind( policy, positions.begin(), positions.size(), f, proj); }; @@ -789,18 +825,22 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t> - parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter first, Sent last, F&& f, Proj&& proj) { using result_type = minmax_element_result; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; result_type result = {first, first}; - if (first == last || ++first == last) + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(HPX_MOVE(result)); + if (first == last || ++first == last) + { + return util::detail::algorithm_result::get(HPX_MOVE(result)); + } } auto f1 = [f, proj, policy](FwdIter it, std::size_t part_count) @@ -809,9 +849,21 @@ namespace hpx::parallel { policy, it, part_count, f, proj); }; - auto f2 = [policy, f = HPX_FORWARD(F, f), + auto f2 = [policy, first, f = HPX_FORWARD(F, f), proj = HPX_FORWARD(Proj, proj)]( auto&& positions) -> result_type { + if constexpr (has_scheduler_executor) + { + if (positions.size() == 0) + { + return result_type{first, first}; + } + } + else + { + HPX_UNUSED(first); + } + return minmax_element::sequential_minmax_element_ind( policy, positions.begin(), positions.size(), f, proj); }; @@ -862,10 +914,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::min_element_t, ExPolicy&& policy, - FwdIter first, FwdIter last, F f = F()) + friend decltype(auto) tag_fallback_invoke(hpx::min_element_t, + ExPolicy&& policy, FwdIter first, FwdIter last, F f = F()) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -906,10 +956,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::max_element_t, ExPolicy&& policy, - FwdIter first, FwdIter last, F f = F()) + friend decltype(auto) tag_fallback_invoke(hpx::max_element_t, + ExPolicy&& policy, FwdIter first, FwdIter last, F f = F()) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -950,10 +998,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t> - tag_fallback_invoke(hpx::minmax_element_t, ExPolicy&& policy, - FwdIter first, FwdIter last, F f = F()) + friend decltype(auto) tag_fallback_invoke(hpx::minmax_element_t, + ExPolicy&& policy, FwdIter first, FwdIter last, F f = F()) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/mismatch.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/mismatch.hpp index 2ad6ecc019c7..80097d6e561a 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/mismatch.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/mismatch.hpp @@ -593,39 +593,27 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t> - parallel(ExPolicy&& orgpolicy, Iter1 first1, Sent1 last1, - Iter2 first2, Sent2 last2, F&& f, Proj1&& proj1, Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& orgpolicy, Iter1 first1, + Sent1 last1, Iter2 first2, Sent2 last2, F&& f, Proj1&& proj1, + Proj2&& proj2) { - if (first1 == last1 || first2 == last2) - { - return util::detail::algorithm_result>:: - get(util::in_in_result{first1, first2}); - } + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - using difference_type1 = - typename std::iterator_traits::difference_type; - difference_type1 count1 = detail::distance(first1, last1); - - // The specification of std::mismatch(_binary) states that if FwdIter1 - // and FwdIter2 meet the requirements of RandomAccessIterator and - // last1 - first1 != last2 - first2 then no applications of the - // predicate p are made. - // - // We perform this check for any iterator type better than input - // iterators. This could turn into a QoI issue. - using difference_type2 = - typename std::iterator_traits::difference_type; - difference_type2 count2 = detail::distance(first2, last2); - if (count1 != count2) + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result>:: - get(util::in_in_result{first1, first2}); + if (first1 == last1 || first2 == last2) + { + return util::detail::algorithm_result>::get(util:: + in_in_result{first1, first2}); + } } + auto count1 = detail::distance(first1, last1); + auto count2 = detail::distance(first2, last2); + auto count = (std::min)(count1, count2); + using zip_iterator = hpx::util::zip_iterator; decltype(auto) policy = parallel::util::adapt_placement_mode( @@ -634,8 +622,7 @@ namespace hpx::parallel { using policy_type = std::decay_t; - hpx::parallel::util::cancellation_token tok( - count1); + hpx::parallel::util::cancellation_token tok(count); auto f1 = [tok, f = HPX_FORWARD(F, f), proj1 = HPX_FORWARD(Proj1, proj1), @@ -647,23 +634,20 @@ namespace hpx::parallel { HPX_FORWARD(Proj1, proj1), HPX_FORWARD(Proj2, proj2)); }; - auto f2 = [=](auto&& data) mutable + auto f2 = [=](auto&&... data) mutable -> util::in_in_result { - // make sure iterators embedded in function object that is - // attached to futures are invalidated - util::detail::clear_container(data); - difference_type1 mismatched = - static_cast(tok.get_data()); - if (mismatched != count1) + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) { - std::advance(first1, mismatched); - std::advance(first2, mismatched); - } - else - { - first1 = detail::advance_to_sentinel(first1, last1); - first2 = detail::advance_to_sentinel(first2, last2); + // make sure iterators embedded in function object that is + // attached to futures are invalidated + util::detail::clear_container(data...); } + + auto mismatched = tok.get_data(); + std::advance(first1, mismatched); + std::advance(first2, mismatched); + return {first1, first2}; }; @@ -672,7 +656,7 @@ namespace hpx::parallel { return partitioner_type::call_with_index( HPX_FORWARD(decltype(policy), policy), - zip_iterator(first1, first2), count1, 1, HPX_MOVE(f1), + zip_iterator(first1, first2), count, 1, HPX_MOVE(f1), HPX_MOVE(f2)); } }; @@ -685,6 +669,21 @@ namespace hpx::parallel { return {HPX_MOVE(p.in1), HPX_MOVE(p.in2)}; } + // clang-format off + template + )> + // clang-format on + decltype(auto) get_pair(InInSender&& in_in_sender) noexcept + { + return hpx::execution::experimental::then( + HPX_FORWARD(InInSender, in_in_sender), [](auto&& in_in_result) { + return get_pair( + HPX_FORWARD(decltype(in_in_result), in_in_result)); + }); + } + template hpx::future> get_pair( hpx::future>&& f) noexcept @@ -719,14 +718,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t - parallel(ExPolicy&& orgpolicy, FwdIter1 first1, Sent last1, - FwdIter2 first2, F&& f) + static decltype(auto) parallel(ExPolicy&& orgpolicy, + FwdIter1 first1, Sent last1, FwdIter2 first2, F&& f) { - if (first1 == last1) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get(std::make_pair(first1, first2)); + if (first1 == last1) + { + return util::detail::algorithm_result::get(std::make_pair(first1, first2)); + } } using difference_type = @@ -751,11 +755,16 @@ namespace hpx::parallel { base_idx, it, part_count, tok, HPX_FORWARD(F, f)); }; - auto f2 = - [=](auto&& data) mutable -> std::pair { - // make sure iterators embedded in function object that is - // attached to futures are invalidated - util::detail::clear_container(data); + auto f2 = [=](auto&&... data) mutable + -> std::pair { + static_assert(sizeof...(data) < 2); + if constexpr (sizeof...(data) == 1) + { + // make sure iterators embedded in function object that is + // attached to futures are invalidated + util::detail::clear_container(data...); + } + difference_type mismatched = static_cast(tok.get_data()); if (mismatched != count) @@ -799,10 +808,9 @@ namespace hpx { > )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t> - tag_fallback_invoke(mismatch_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, Pred op) + friend decltype(auto) tag_fallback_invoke(mismatch_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, + Pred op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -824,10 +832,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t> - tag_fallback_invoke(mismatch_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, FwdIter2 last2) + friend decltype(auto) tag_fallback_invoke(mismatch_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -855,10 +861,8 @@ namespace hpx { > )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t> - tag_fallback_invoke(mismatch_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2, Pred op) + friend decltype(auto) tag_fallback_invoke(mismatch_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, Pred op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -879,10 +883,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t> - tag_fallback_invoke(mismatch_t, ExPolicy&& policy, FwdIter1 first1, - FwdIter1 last1, FwdIter2 first2) + friend decltype(auto) tag_fallback_invoke(mismatch_t, ExPolicy&& policy, + FwdIter1 first1, FwdIter1 last1, FwdIter2 first2) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/move.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/move.hpp index e6c32181f828..ebf03fd56c24 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/move.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/move.hpp @@ -158,8 +158,7 @@ namespace hpx::parallel { } template - static typename util::detail::algorithm_result>::type + static decltype(auto) parallel( ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest) { @@ -225,10 +224,8 @@ namespace hpx { hpx::traits::is_iterator_v && hpx::traits::is_iterator_v)> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(move_t, ExPolicy&& policy, FwdIter1 first, - FwdIter1 last, FwdIter2 dest) + friend decltype(auto) tag_fallback_invoke(move_t, ExPolicy&& policy, + FwdIter1 first, FwdIter1 last, FwdIter2 dest) { return hpx::parallel::util::get_second_element( hpx::parallel::detail::transfer< diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/reduce.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/reduce.hpp index d4c7b7ddb663..e42a79de4558 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/reduce.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/reduce.hpp @@ -402,14 +402,19 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIterB first, FwdIterE last, T_&& init, - Reduce&& r) + static decltype(auto) parallel(ExPolicy&& policy, FwdIterB first, + FwdIterE last, T_&& init, Reduce&& r) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - return util::detail::algorithm_result::get( - HPX_FORWARD(T_, init)); + if (first == last) + { + return util::detail::algorithm_result::get( + HPX_FORWARD(T_, init)); + } } auto f1 = [r](FwdIterB part_begin, std::size_t part_size) -> T { @@ -450,9 +455,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::reduce_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T init, F f) + friend auto tag_fallback_invoke(hpx::reduce_t, ExPolicy&& policy, + FwdIter first, FwdIter last, T init, F f) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -470,9 +474,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::reduce_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T init) + friend auto tag_fallback_invoke(hpx::reduce_t, ExPolicy&& policy, + FwdIter first, FwdIter last, T init) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -489,9 +492,7 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t::value_type> - tag_fallback_invoke( + friend auto tag_fallback_invoke( hpx::reduce_t, ExPolicy&& policy, FwdIter first, FwdIter last) { static_assert(hpx::traits::is_forward_iterator_v, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/remove.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/remove.hpp index d3ad22b2b6fc..5bd439c66897 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/remove.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/remove.hpp @@ -286,20 +286,24 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, Iter first, Sent last, Pred&& pred, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, Iter first, + Sent last, Pred&& pred, Proj&& proj) { using zip_iterator = hpx::util::zip_iterator; using algorithm_result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = detail::distance(first, last); - if (count == 0) - return algorithm_result::get(HPX_MOVE(first)); + if constexpr (!has_scheduler_executor) + { + if (count == 0) + return algorithm_result::get(HPX_MOVE(first)); + } #if defined(HPX_HAVE_CXX17_SHARED_PTR_ARRAY) std::shared_ptr flags(new bool[count]); @@ -325,10 +329,7 @@ namespace hpx::parallel { }); }; - auto f2 = [flags, first, count]( - auto&& results) mutable -> Iter { - HPX_UNUSED(results); - + auto f2 = [flags, first, count](auto&&...) mutable -> Iter { auto part_begin = zip_iterator(first, flags.get()); auto dest = first; auto part_size = count; @@ -407,9 +408,8 @@ namespace hpx { > )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::remove_if_t, ExPolicy&& policy, FwdIter first, - FwdIter last, Pred pred) + friend decltype(auto) tag_fallback_invoke(hpx::remove_if_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -450,9 +450,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::remove_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T const& value) + friend decltype(auto) tag_fallback_invoke(hpx::remove_t, + ExPolicy&& policy, FwdIter first, FwdIter last, T const& value) { using Type = typename std::iterator_traits::value_type; diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp index b07d8c8595cc..15517937af19 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/replace.hpp @@ -553,9 +553,9 @@ namespace hpx::parallel { template - static constexpr util::detail::algorithm_result_t - parallel(ExPolicy&& policy, FwdIter first, Sent last, F&& f, - T const& new_value, Proj&& proj) + static constexpr decltype(auto) parallel(ExPolicy&& policy, + FwdIter first, Sent last, F&& f, T const& new_value, + Proj&& proj) { return sequential_replace_if( HPX_FORWARD(ExPolicy, policy), first, last, @@ -632,10 +632,9 @@ namespace hpx::parallel { template - static constexpr util::detail::algorithm_result_t> - parallel(ExPolicy&& policy, FwdIter1 first, Sent sent, - FwdIter2 dest, F&& f, T const& new_value, Proj&& proj) + static constexpr decltype(auto) parallel(ExPolicy&& policy, + FwdIter1 first, Sent sent, FwdIter2 dest, F&& f, + T const& new_value, Proj&& proj) { return sequential_replace_copy_if( HPX_FORWARD(ExPolicy, policy), first, sent, dest, @@ -688,10 +687,9 @@ namespace hpx { > )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::replace_if_t, ExPolicy&& policy, FwdIter first, - FwdIter last, Pred pred, T const& new_value) + friend decltype(auto) tag_fallback_invoke(hpx::replace_if_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred, + T const& new_value) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -738,10 +736,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::replace_t, ExPolicy&& policy, FwdIter first, - FwdIter last, T const& old_value, T const& new_value) + friend decltype(auto) tag_fallback_invoke(hpx::replace_t, + ExPolicy&& policy, FwdIter first, FwdIter last, T const& old_value, + T const& new_value) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -800,11 +797,9 @@ namespace hpx { > )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::replace_copy_if_t, ExPolicy&& policy, - FwdIter1 first, FwdIter1 last, FwdIter2 dest, Pred pred, - T const& new_value) + friend decltype(auto) tag_fallback_invoke(hpx::replace_copy_if_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest, + Pred pred, T const& new_value) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); @@ -860,11 +855,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::replace_copy_t, ExPolicy&& policy, - FwdIter1 first, FwdIter1 last, FwdIter2 dest, T const& old_value, - T const& new_value) + friend decltype(auto) tag_fallback_invoke(hpx::replace_copy_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest, + T const& old_value, T const& new_value) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/reverse.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/reverse.hpp index 3e8d8d18137b..e36111482488 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/reverse.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/reverse.hpp @@ -299,10 +299,8 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t> - parallel(ExPolicy&& policy, BidirIter first, Sent last, - FwdIter dest_first) + static decltype(auto) parallel(ExPolicy&& policy, BidirIter first, + Sent last, FwdIter dest_first) { auto last2{hpx::ranges::next(first, last)}; typedef std::reverse_iterator iterator; @@ -402,9 +400,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::reverse_copy_t, ExPolicy&& policy, - BidirIter first, BidirIter last, FwdIter dest) + friend decltype(auto) tag_fallback_invoke(hpx::reverse_copy_t, + ExPolicy&& policy, BidirIter first, BidirIter last, FwdIter dest) { static_assert(hpx::traits::is_bidirectional_iterator_v, "Requires at least bidirectional iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/rotate.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/rotate.hpp index ed23caa20f37..6e3965057191 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/rotate.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/rotate.hpp @@ -390,28 +390,43 @@ namespace hpx::parallel { template - hpx::future> rotate_copy_helper( - ExPolicy policy, FwdIter1 first, FwdIter1 new_first, Sent last, - FwdIter2 dest_first) + decltype(auto) rotate_copy_helper(ExPolicy policy, FwdIter1 first, + FwdIter1 new_first, Sent last, FwdIter2 dest_first) { - using non_seq = std::false_type; - - auto p = hpx::execution::parallel_task_policy() - .on(policy.executor()) - .with(policy.parameters()); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + using non_seq = std::false_type; using copy_return_type = util::in_out_result; - hpx::future f = - detail::copy().call2( - p, non_seq(), new_first, last, dest_first); - - return f.then([=](hpx::future&& result) - -> hpx::future { - copy_return_type p1 = result.get(); - return detail::copy().call2( - p, non_seq(), first, new_first, p1.out); - }); + if constexpr (!has_scheduler_executor) + { + auto p = hpx::execution::parallel_task_policy() + .on(policy.executor()) + .with(policy.parameters()); + + hpx::future f = + detail::copy().call2( + p, non_seq(), new_first, last, dest_first); + + return f.then([p, first, new_first]( + hpx::future&& result) + -> hpx::future { + copy_return_type p1 = result.get(); + return detail::copy().call2( + p, non_seq(), first, new_first, p1.out); + }); + } + else + { + return detail::copy().call( + policy, new_first, last, dest_first) | + hpx::execution::experimental::let_value( + [policy, first, new_first](copy_return_type& result) { + return detail::copy().call( + policy, first, new_first, result.out); + }); + } } template @@ -434,10 +449,8 @@ namespace hpx::parallel { template - static typename util::detail::algorithm_result>::type - parallel(ExPolicy&& policy, FwdIter1 first, FwdIter1 new_first, - Sent last, FwdIter2 dest_first) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter1 first, + FwdIter1 new_first, Sent last, FwdIter2 dest_first) { return util::detail::algorithm_result::get( rotate_copy_helper(HPX_FORWARD(ExPolicy, policy), first, @@ -563,10 +576,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::rotate_copy_t, ExPolicy&& policy, - FwdIter1 first, FwdIter1 new_first, FwdIter1 last, - FwdIter2 dest_first) + friend decltype(auto) tag_fallback_invoke(hpx::rotate_copy_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 new_first, + FwdIter1 last, FwdIter2 dest_first) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/search.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/search.hpp index fd01f2957eb6..4814226fe0eb 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/search.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/search.hpp @@ -341,10 +341,9 @@ namespace hpx { > )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::search_t, ExPolicy&& policy, FwdIter first, - FwdIter last, FwdIter2 s_first, FwdIter2 s_last, Pred op = Pred()) + friend decltype(auto) tag_fallback_invoke(hpx::search_t, + ExPolicy&& policy, FwdIter first, FwdIter last, FwdIter2 s_first, + FwdIter2 s_last, Pred op = Pred()) { return hpx::parallel::detail::search().call( HPX_FORWARD(ExPolicy, policy), first, last, s_first, s_last, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/starts_with.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/starts_with.hpp index 2f8d970f7351..dea00aea6f41 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/starts_with.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/starts_with.hpp @@ -154,7 +154,7 @@ namespace hpx { #include #include #include -#include +#include #include #include #include @@ -212,43 +212,46 @@ namespace hpx::parallel { } auto end_first = std::next(first1, dist2); - return detail::get_starts_with_result( - hpx::parallel::detail::mismatch_binary< - util::in_in_result>() - .call(hpx::execution::seq, HPX_MOVE(first1), - HPX_MOVE(end_first), HPX_MOVE(first2), last2, - HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj1, proj1), - HPX_FORWARD(Proj2, proj2)), - last2); + + return detail::equal_binary{}.call(hpx::execution::seq, first1, + end_first, first2, last2, HPX_FORWARD(Pred, pred), + HPX_FORWARD(Proj1, proj1), HPX_FORWARD(Proj2, proj2)); } template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIter1 first1, Sent1 last1, - FwdIter2 first2, Sent2 last2, Pred&& pred, Proj1&& proj1, - Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter1 first1, + Sent1 last1, FwdIter2 first2, Sent2 last2, Pred&& pred, + Proj1&& proj1, Proj2&& proj2) { auto dist1 = detail::distance(first1, last1); auto dist2 = detail::distance(first2, last2); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + if (dist1 < dist2) { - return util::detail::algorithm_result::get( - false); + if constexpr (has_scheduler_executor) + { + // make sure that the two ranges passed to equal_binary + // have different length to get `false` as return value + dist2 = 0; + } + else + { + return util::detail::algorithm_result::get(false); + } } auto end_first = std::next(first1, dist2); - return detail::get_starts_with_result( - detail::mismatch_binary< - util::in_in_result>() - .call(HPX_FORWARD(ExPolicy, policy), first1, end_first, - first2, last2, HPX_FORWARD(Pred, pred), - HPX_FORWARD(Proj1, proj1), - HPX_FORWARD(Proj2, proj2)), - last2); + + return detail::equal_binary{}.call( + HPX_FORWARD(ExPolicy, policy), first1, end_first, first2, + last2, HPX_FORWARD(Pred, pred), HPX_FORWARD(Proj1, proj1), + HPX_FORWARD(Proj2, proj2)); } }; /// \endcond @@ -260,7 +263,7 @@ namespace hpx { /////////////////////////////////////////////////////////////////////////// // CPO for hpx::starts_with inline constexpr struct starts_with_t final - : hpx::functional::detail::tag_fallback + : hpx::detail::tag_parallel_algorithm { private: // clang-format off @@ -302,10 +305,9 @@ namespace hpx { > )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::starts_with_t, ExPolicy&& policy, - FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter2 last2, - Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke(hpx::starts_with_t, + ExPolicy&& policy, FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, + FwdIter2 last2, Pred pred = Pred()) { static_assert(hpx::traits::is_forward_iterator_v, "Required at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/swap_ranges.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/swap_ranges.hpp index 166da02c7d0b..8e3d1eea6eac 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/swap_ranges.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/swap_ranges.hpp @@ -136,9 +136,7 @@ namespace hpx::parallel { template - util::detail::algorithm_result_t> - parallel_swap_ranges( + decltype(auto) parallel_swap_ranges( ExPolicy&& policy, FwdIter1 first1, FwdIter2 first2, Size n) { using zip_iterator = hpx::util::zip_iterator; @@ -196,8 +194,7 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t - parallel( + static decltype(auto) parallel( ExPolicy&& policy, FwdIter1 first1, Sent last1, FwdIter2 first2) { return util::get_in2_element( @@ -207,15 +204,14 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t> - parallel(ExPolicy&& policy, FwdIter1 first1, Sent1 last1, - FwdIter2 first2, Sent2 last2) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter1 first1, + Sent1 last1, FwdIter2 first2, Sent2 last2) { auto dist1 = detail::distance(first1, last1); auto dist2 = detail::distance(first2, last2); + return parallel_swap_ranges(HPX_FORWARD(ExPolicy, policy), - first1, first2, dist1 < dist2 ? dist1 : dist2); + first1, first2, (std::min)(dist1, dist2)); } }; /// \endcond @@ -223,8 +219,8 @@ namespace hpx::parallel { template HPX_DEPRECATED_V(1, 8, - "hpx::parallel::transform_exclusive_scan is deprecated, use " - "hpx::transform_exclusive_scan instead") + "hpx::parallel::swap_ranges is deprecated, use " + "hpx::swap_ranges instead") std::enable_if_t, util::detail::algorithm_result_t> swap_ranges(ExPolicy&& policy, FwdIter1 first1, @@ -281,9 +277,8 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::swap_ranges_t, ExPolicy&& policy, - FwdIter1 first1, FwdIter1 last1, FwdIter2 first2) + friend decltype(auto) tag_fallback_invoke(hpx::swap_ranges_t, + ExPolicy&& policy, FwdIter1 first1, FwdIter1 last1, FwdIter2 first2) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/transform.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/transform.hpp index 427772ceb2e8..9e61ad4264d8 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/transform.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/transform.hpp @@ -490,29 +490,34 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t> - parallel(ExPolicy&& policy, FwdIter1B first, FwdIter1E last, - FwdIter2 dest, F&& f, Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter1B first, + FwdIter1E last, FwdIter2 dest, F&& f, Proj&& proj) { - if (first != last) - { - auto f1 = transform_iteration( - HPX_FORWARD(F, f), HPX_FORWARD(Proj, proj)); + using result_type = util::in_out_result; + using algorithm_result = + util::detail::algorithm_result; - return util::detail::get_in_out_result( - util::foreach_partitioner::call( - HPX_FORWARD(ExPolicy, policy), - hpx::util::zip_iterator(first, dest), - detail::distance(first, last), HPX_MOVE(f1), - hpx::identity_v)); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) + { + if (first == last) + { + return algorithm_result::get( + result_type{HPX_MOVE(first), HPX_MOVE(dest)}); + } } - using result_type = util::in_out_result; + auto f1 = transform_iteration( + HPX_FORWARD(F, f), HPX_FORWARD(Proj, proj)); - return util::detail::algorithm_result::get(result_type{ - HPX_MOVE(first), HPX_MOVE(dest)}); + return util::detail::get_in_out_result( + util::foreach_partitioner::call( + HPX_FORWARD(ExPolicy, policy), + hpx::util::zip_iterator(first, dest), + detail::distance(first, last), HPX_MOVE(f1), + hpx::identity_v)); } }; /// \endcond @@ -712,33 +717,38 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t> - parallel(ExPolicy&& policy, FwdIter1B first1, FwdIter1E last1, - FwdIter2 first2, FwdIter3 dest, F&& f, Proj1&& proj1, - Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter1B first1, + FwdIter1E last1, FwdIter2 first2, FwdIter3 dest, F&& f, + Proj1&& proj1, Proj2&& proj2) { - if (first1 != last1) - { - auto f1 = - transform_binary_iteration( - HPX_FORWARD(F, f), HPX_FORWARD(Proj1, proj1), - HPX_FORWARD(Proj2, proj2)); + using result_type = + util::in_in_out_result; + using algorithm_result = + util::detail::algorithm_result; - return util::detail::get_in_in_out_result( - util::foreach_partitioner::call( - HPX_FORWARD(ExPolicy, policy), - hpx::util::zip_iterator(first1, first2, dest), - detail::distance(first1, last1), HPX_MOVE(f1), - hpx::identity_v)); + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) + { + if (first1 == last1) + { + return algorithm_result::get( + result_type{HPX_MOVE(first1), HPX_MOVE(first2), + HPX_MOVE(dest)}); + } } - using result_type = - util::in_in_out_result; + auto f1 = transform_binary_iteration( + HPX_FORWARD(F, f), HPX_FORWARD(Proj1, proj1), + HPX_FORWARD(Proj2, proj2)); - return util::detail::algorithm_result::get(result_type{ - HPX_MOVE(first1), HPX_MOVE(first2), HPX_MOVE(dest)}); + return util::detail::get_in_in_out_result( + util::foreach_partitioner::call( + HPX_FORWARD(ExPolicy, policy), + hpx::util::zip_iterator(first1, first2, dest), + detail::distance(first1, last1), HPX_MOVE(f1), + hpx::identity_v)); } }; /// \endcond @@ -787,21 +797,34 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t> - parallel(ExPolicy&& policy, FwdIter1B first1, FwdIter1E last1, - FwdIter2B first2, FwdIter2E last2, FwdIter3 dest, F&& f, - Proj1&& proj1, Proj2&& proj2) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter1B first1, + FwdIter1E last1, FwdIter2B first2, FwdIter2E last2, + FwdIter3 dest, F&& f, Proj1&& proj1, Proj2&& proj2) { - if (first1 != last1 && first2 != last2) + using result_type = + util::in_in_out_result; + using algorithm_result = + util::detail::algorithm_result; + + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - auto f1 = - transform_binary_iteration( - HPX_FORWARD(F, f), HPX_FORWARD(Proj1, proj1), - HPX_FORWARD(Proj2, proj2)); + if (first1 == last1 || first2 == last2) + { + return algorithm_result::get( + result_type{HPX_MOVE(first1), HPX_MOVE(first2), + HPX_MOVE(dest)}); + } + } + + auto f1 = transform_binary_iteration( + HPX_FORWARD(F, f), HPX_FORWARD(Proj1, proj1), + HPX_FORWARD(Proj2, proj2)); - // different versions of clang-format do different things - // clang-format off + // different versions of clang-format do different things + // clang-format off return util::detail::get_in_in_out_result( util::foreach_partitioner::call( HPX_FORWARD(ExPolicy, policy), @@ -809,15 +832,7 @@ namespace hpx::parallel { (std::min) (detail::distance(first1, last1), detail::distance(first2, last2)), HPX_MOVE(f1), hpx::identity_v)); - // clang-format on - } - - using result_type = - util::in_in_out_result; - - return util::detail::algorithm_result::get(result_type{ - HPX_MOVE(first1), HPX_MOVE(first2), HPX_MOVE(dest)}); + // clang-format on } }; /// \endcond @@ -942,9 +957,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::transform_t, ExPolicy&& policy, FwdIter1 first, - FwdIter1 last, FwdIter2 dest, F f) + friend decltype(auto) tag_fallback_invoke(hpx::transform_t, + ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest, + F f) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -993,10 +1008,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t - tag_fallback_invoke(hpx::transform_t, ExPolicy&& policy, - FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, FwdIter3 dest, - F f) + friend decltype(auto) tag_fallback_invoke(hpx::transform_t, + ExPolicy&& policy, FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, + FwdIter3 dest, F f) { static_assert(hpx::traits::is_input_iterator_v && hpx::traits::is_input_iterator_v, diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/transform_reduce.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/transform_reduce.hpp index a0494593927e..ed6ba8511b2c 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/transform_reduce.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/transform_reduce.hpp @@ -470,15 +470,20 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, Iter first, Sent last, T_&& init, Reduce&& r, - Convert&& conv) + static decltype(auto) parallel(ExPolicy&& policy, Iter first, + Sent last, T_&& init, Reduce&& r, Convert&& conv) { - if (first == last) + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; + + if constexpr (!has_scheduler_executor) { - T init_ = init; - return util::detail::algorithm_result::get( - HPX_MOVE(init_)); + if (first == last) + { + T init_ = init; + return util::detail::algorithm_result::get( + HPX_MOVE(init_)); + } } auto f1 = [r, conv]( @@ -527,18 +532,23 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, Iter first1, Sent last1, Iter2 first2, - T_&& init, Op1&& op1, Op2&& op2) + static decltype(auto) parallel(ExPolicy&& policy, Iter first1, + Sent last1, Iter2 first2, T_&& init, Op1&& op1, Op2&& op2) { using result = util::detail::algorithm_result; using zip_iterator = hpx::util::zip_iterator; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; - if (first1 == last1) + if constexpr (!has_scheduler_executor) { - return result::get(HPX_FORWARD(T_, init)); + if (first1 == last1) + { + T init_ = init; + return result::get(HPX_MOVE(init_)); + } } difference_type count = detail::distance(first1, last1); @@ -568,9 +578,10 @@ namespace hpx::parallel { [init = HPX_FORWARD(T_, init), op1 = HPX_FORWARD(Op1, op1)]( auto&& results) mutable -> T { T ret = HPX_MOVE(init); - for (auto&& fut : results) + for (auto&& result : results) { - ret = HPX_INVOKE(op1, HPX_MOVE(ret), fut.get()); + ret = HPX_INVOKE( + op1, HPX_MOVE(ret), hpx::unwrap(result)); } return ret; }); @@ -605,9 +616,9 @@ namespace hpx { > )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(transform_reduce_t, ExPolicy&& policy, - FwdIter first, FwdIter last, T init, Reduce red_op, Convert conv_op) + friend decltype(auto) tag_fallback_invoke(transform_reduce_t, + ExPolicy&& policy, FwdIter first, FwdIter last, T init, + Reduce red_op, Convert conv_op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -654,9 +665,9 @@ namespace hpx { hpx::traits::is_iterator_v )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(transform_reduce_t, ExPolicy&& policy, - FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, T init) + friend decltype(auto) tag_fallback_invoke(transform_reduce_t, + ExPolicy&& policy, FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, + T init) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -713,10 +724,9 @@ namespace hpx { > )> // clang-format on - friend hpx::parallel::util::detail::algorithm_result_t - tag_fallback_invoke(transform_reduce_t, ExPolicy&& policy, - FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, T init, - Reduce red_op, Convert conv_op) + friend decltype(auto) tag_fallback_invoke(transform_reduce_t, + ExPolicy&& policy, FwdIter1 first1, FwdIter1 last1, FwdIter2 first2, + T init, Reduce red_op, Convert conv_op) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/unique.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/unique.hpp index 4ede8cc5c462..a5b47764b630 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/unique.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/unique.hpp @@ -382,22 +382,26 @@ namespace hpx::parallel { template - static util::detail::algorithm_result_t parallel( - ExPolicy&& policy, FwdIter first, Sent last, Pred&& pred, - Proj&& proj) + static decltype(auto) parallel(ExPolicy&& policy, FwdIter first, + Sent last, Pred&& pred, Proj&& proj) { using zip_iterator = hpx::util::zip_iterator; using algorithm_result = util::detail::algorithm_result; using difference_type = typename std::iterator_traits::difference_type; + constexpr bool has_scheduler_executor = + hpx::execution_policy_has_scheduler_executor_v; difference_type count = detail::distance(first, last); - if (count < 2) + if constexpr (!has_scheduler_executor) { - std::advance(first, count); - return algorithm_result::get(HPX_MOVE(first)); + if (count < 2) + { + std::advance(first, count); + return algorithm_result::get(HPX_MOVE(first)); + } } #if defined(HPX_HAVE_CXX17_SHARED_PTR_ARRAY) @@ -430,11 +434,7 @@ namespace hpx::parallel { }); }; - auto f2 = [flags, first, count]( - auto&& results) mutable -> FwdIter { - // HPX_UNUSED(flags); - HPX_UNUSED(results); - + auto f2 = [flags, first, count](auto&&...) mutable -> FwdIter { auto part_begin = zip_iterator(first, flags.get()); auto dest = first; auto part_size = count; @@ -467,6 +467,13 @@ namespace hpx::parallel { return dest; }; + if constexpr (has_scheduler_executor) + { + // underflow prevention + if (count == 0) + ++count; + } + return util::partitioner::call( HPX_FORWARD(ExPolicy, policy), zip_iterator(first, flags.get()), count - 1, HPX_MOVE(f1), @@ -794,10 +801,8 @@ namespace hpx { > )> // clang-format on - friend typename parallel::util::detail::algorithm_result::type - tag_fallback_invoke(hpx::unique_t, ExPolicy&& policy, FwdIter first, - FwdIter last, Pred pred = Pred()) + friend decltype(auto) tag_fallback_invoke(hpx::unique_t, + ExPolicy&& policy, FwdIter first, FwdIter last, Pred pred = Pred()) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); diff --git a/libs/core/algorithms/include/hpx/parallel/container_algorithms/transform.hpp b/libs/core/algorithms/include/hpx/parallel/container_algorithms/transform.hpp index 3078d96715b2..a24a93f4d192 100644 --- a/libs/core/algorithms/include/hpx/parallel/container_algorithms/transform.hpp +++ b/libs/core/algorithms/include/hpx/parallel/container_algorithms/transform.hpp @@ -723,10 +723,9 @@ namespace hpx::ranges { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t> - tag_fallback_invoke(hpx::ranges::transform_t, ExPolicy&& policy, - FwdIter1 first, Sent1 last, FwdIter2 dest, F f, Proj proj = Proj()) + friend decltype(auto) tag_fallback_invoke(hpx::ranges::transform_t, + ExPolicy&& policy, FwdIter1 first, Sent1 last, FwdIter2 dest, F f, + Proj proj = Proj()) { static_assert(hpx::traits::is_forward_iterator_v, "Requires at least forward iterator."); @@ -746,11 +745,8 @@ namespace hpx::ranges { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t, - FwdIter>> - tag_fallback_invoke(hpx::ranges::transform_t, ExPolicy&& policy, - Rng&& rng, FwdIter dest, F f, Proj proj = Proj()) + friend decltype(auto) tag_fallback_invoke(hpx::ranges::transform_t, + ExPolicy&& policy, Rng&& rng, FwdIter dest, F f, Proj proj = Proj()) { using iterator_type = typename hpx::traits::range_traits::iterator_type; @@ -778,11 +774,10 @@ namespace hpx::ranges { hpx::traits::is_iterator_v )> // clang-format on - friend typename parallel::util::detail::algorithm_result>::type - tag_fallback_invoke(hpx::ranges::transform_t, ExPolicy&& policy, - FwdIter1 first1, Sent1 last1, FwdIter2 first2, Sent2 last2, - FwdIter3 dest, F f, Proj1 proj1 = Proj1(), Proj2 proj2 = Proj2()) + friend decltype(auto) tag_fallback_invoke(hpx::ranges::transform_t, + ExPolicy&& policy, FwdIter1 first1, Sent1 last1, FwdIter2 first2, + Sent2 last2, FwdIter3 dest, F f, Proj1 proj1 = Proj1(), + Proj2 proj2 = Proj2()) { static_assert(hpx::traits::is_forward_iterator_v && hpx::traits::is_forward_iterator_v, @@ -805,12 +800,9 @@ namespace hpx::ranges { hpx::traits::is_iterator_v )> // clang-format on - friend parallel::util::detail::algorithm_result_t, - hpx::traits::range_iterator_t, FwdIter>> - tag_fallback_invoke(hpx::ranges::transform_t, ExPolicy&& policy, - Rng1&& rng1, Rng2&& rng2, FwdIter dest, F f, Proj1 proj1 = Proj1(), - Proj2 proj2 = Proj2()) + friend decltype(auto) tag_fallback_invoke(hpx::ranges::transform_t, + ExPolicy&& policy, Rng1&& rng1, Rng2&& rng2, FwdIter dest, F f, + Proj1 proj1 = Proj1(), Proj2 proj2 = Proj2()) { using iterator_type1 = typename hpx::traits::range_traits::iterator_type; diff --git a/libs/core/algorithms/include/hpx/parallel/util/detail/algorithm_result.hpp b/libs/core/algorithms/include/hpx/parallel/util/detail/algorithm_result.hpp index 9821caf33814..ae04cb021c5f 100644 --- a/libs/core/algorithms/include/hpx/parallel/util/detail/algorithm_result.hpp +++ b/libs/core/algorithms/include/hpx/parallel/util/detail/algorithm_result.hpp @@ -19,7 +19,7 @@ #include #if defined(HPX_HAVE_STDEXEC) -// For is_sender +// for is_sender #include #endif @@ -202,8 +202,9 @@ namespace hpx::parallel::util::detail { std::enable_if_t && hpx::execution_policy_has_scheduler_executor_v>> { - // The return type of the initiating function. - using type = T; + // Due to the nature of senders, this only serves as a dummy. + using type = + decltype(hpx::execution::experimental::just(std::declval())); template static constexpr auto get(T_&& t) @@ -225,8 +226,8 @@ namespace hpx::parallel::util::detail { std::enable_if_t && hpx::execution_policy_has_scheduler_executor_v>> { - // The return type of the initiating function. - using type = void; + // Due to the nature of senders, this only serves as a dummy. + using type = decltype(hpx::execution::experimental::just()); template static constexpr auto get(T_&& t) diff --git a/libs/core/algorithms/include/hpx/parallel/util/foreach_partitioner.hpp b/libs/core/algorithms/include/hpx/parallel/util/foreach_partitioner.hpp index a47090890fc9..28a8a800542d 100644 --- a/libs/core/algorithms/include/hpx/parallel/util/foreach_partitioner.hpp +++ b/libs/core/algorithms/include/hpx/parallel/util/foreach_partitioner.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/core/algorithms/include/hpx/parallel/util/partitioner.hpp b/libs/core/algorithms/include/hpx/parallel/util/partitioner.hpp index d458e422a6e5..ac04f6e6995e 100644 --- a/libs/core/algorithms/include/hpx/parallel/util/partitioner.hpp +++ b/libs/core/algorithms/include/hpx/parallel/util/partitioner.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/libs/core/algorithms/include/hpx/parallel/util/result_types.hpp b/libs/core/algorithms/include/hpx/parallel/util/result_types.hpp index c4a8c0e1374a..5b0517b48deb 100644 --- a/libs/core/algorithms/include/hpx/parallel/util/result_types.hpp +++ b/libs/core/algorithms/include/hpx/parallel/util/result_types.hpp @@ -68,6 +68,21 @@ namespace hpx::parallel::util { HPX_MOVE(f), [](util::in_in_result&& p) { return p.in2; }); } + // clang-format off + template + )> + // clang-format on + decltype(auto) get_in2_element(InInResultSender&& result_sender) + { + return hpx::execution::experimental::then( + HPX_FORWARD(InInResultSender, result_sender), [](auto&& result) { + return util::get_in2_element( + HPX_FORWARD(decltype(result), result)); + }); + } + /////////////////////////////////////////////////////////////////////////// template struct in_out_result @@ -139,8 +154,7 @@ namespace hpx::parallel::util { { // clang-format off template - auto operator()(T&& val) const -> decltype( - hpx::parallel::util::get_second_element(HPX_FORWARD(T, val))) + decltype(auto) operator()(T&& val) const { return hpx::parallel::util::get_second_element( HPX_FORWARD(T, val)); @@ -155,9 +169,7 @@ namespace hpx::parallel::util { hpx::execution::experimental::is_sender_v )> // clang-format on - auto get_second_element(Sender&& sender) - -> decltype(hpx::execution::experimental::then( - HPX_FORWARD(Sender, sender), functional::get_second_element{})) + decltype(auto) get_second_element(Sender&& sender) { return hpx::execution::experimental::then( HPX_FORWARD(Sender, sender), functional::get_second_element{}); @@ -261,6 +273,21 @@ namespace hpx::parallel::util { HPX_MOVE(f), [](in_in_out_result&& p) { return p.out; }); } + // clang-format off + template + )> + // clang-format on + decltype(auto) get_third_element(InInOutResultSender&& result_sender) + { + return hpx::execution::experimental::then( + HPX_FORWARD(InInOutResultSender, result_sender), [](auto&& result) { + return util::get_third_element( + HPX_FORWARD(decltype(result), result)); + }); + } + /////////////////////////////////////////////////////////////////////////// template struct in_out_out_result @@ -394,6 +421,21 @@ namespace hpx::parallel::util { return result_type{hpx::get<0>(t), hpx::get<1>(t)}; } + // clang-format off + template + )> + // clang-format on + decltype(auto) get_in_out_result(ZipIterSender&& zipiter_sender) + { + return hpx::execution::experimental::then( + HPX_FORWARD(ZipIterSender, zipiter_sender), [](auto&& zipiter) { + return get_in_out_result( + HPX_FORWARD(decltype(zipiter), zipiter)); + }); + } + template hpx::future< in_out_result(t), hpx::get<1>(t), hpx::get<2>(t)}; } + // clang-format off + template + )> + // clang-format on + decltype(auto) get_in_in_out_result(ZipIterSender&& zipiter_sender) + { + return hpx::execution::experimental::then( + HPX_FORWARD(ZipIterSender, zipiter_sender), [](auto&& zipiter) { + return get_in_in_out_result( + HPX_FORWARD(decltype(zipiter), zipiter)); + }); + } + template hpx::future< in_in_out_result(t), hpx::get<1>(t)}; } + // clang-format off + template + )> + // clang-format on + constexpr decltype(auto) get_iter_in_in_result( + ZipIterSender&& zipiter_sender) + { + return hpx::execution::experimental::then( + HPX_FORWARD(ZipIterSender, zipiter_sender), [](auto&& zipiter) { + return get_iter_in_in_result( + HPX_FORWARD(decltype(zipiter), zipiter)); + }); + } + template hpx::future< util::in_in_result +#include +#include +#include + +#include +#include +#include + +void find_first_of_failing_test() +{ + std::vector a{1, 2, 3, 4, 5}; + std::vector b{10, 11, 12, 2, 13, 14, 15}; + + auto result = hpx::find_first_of( + hpx::execution::par, a.begin(), a.end(), b.begin(), b.end()); + auto expected = ++a.begin(); + + HPX_TEST(result == expected); +} + +int hpx_main() +{ + find_first_of_failing_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + std::vector const cfg = {"hpx.os_threads=all"}; + + hpx::local::init_params init_args; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/regressions/includes_empty_ranges.cpp b/libs/core/algorithms/tests/regressions/includes_empty_ranges.cpp new file mode 100644 index 000000000000..63a81223e11c --- /dev/null +++ b/libs/core/algorithms/tests/regressions/includes_empty_ranges.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +// An empty subsequence is a subsequence of every sequency, thus if the second +// range is empty, includes should always return true, even if the first range +// is empty. + +#include +#include +#include +#include + +#include +#include +#include +#include + +void includes_two_empty_ranges_test() +{ + std::vector a{}; + std::vector b{}; + + auto result = hpx::includes( + hpx::execution::par, a.begin(), a.end(), b.begin(), b.end()); + + auto expected = std::includes(a.begin(), a.end(), b.begin(), b.end()); + + HPX_TEST(result == expected); +} + +int hpx_main() +{ + includes_two_empty_ranges_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + std::vector const cfg = {"hpx.os_threads=all"}; + + hpx::local::init_params init_args; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/regressions/minimal_findend.cpp b/libs/core/algorithms/tests/regressions/minimal_findend.cpp index 06842f5f9847..3d2ee1b1cef5 100644 --- a/libs/core/algorithms/tests/regressions/minimal_findend.cpp +++ b/libs/core/algorithms/tests/regressions/minimal_findend.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace test { @@ -69,7 +70,7 @@ void find_end_failing_test() bool caught_exception = false; try { - [[maybe_unused]] auto ret = + std::ignore = std::find_end(decorated_iterator(std::begin(c), []() { throw std::runtime_error("error"); }), decorated_iterator( diff --git a/libs/core/algorithms/tests/regressions/mismatch_differently_sized_ranges.cpp b/libs/core/algorithms/tests/regressions/mismatch_differently_sized_ranges.cpp new file mode 100644 index 000000000000..594a92c46795 --- /dev/null +++ b/libs/core/algorithms/tests/regressions/mismatch_differently_sized_ranges.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +// Concerns the mismatch overload taking two ranges: +// If the two ranges are not of the same length the algorithm should still +// proceed as usual and not just return the first iterators of both ranges. + +#include +#include +#include +#include + +#include +#include +#include +#include + +void mismatch_differently_size_ranges_test() +{ + std::vector a{1, 2, 7, 4}; + std::vector b{1, 2, 8, 4, 5, 6}; + + auto result = hpx::mismatch( + hpx::execution::par, a.begin(), a.end(), b.begin(), b.end()); + + auto expected = std::mismatch(a.begin(), a.end(), b.begin(), b.end()); + + HPX_TEST((a.begin() + 2) == result.first); + HPX_TEST((b.begin() + 2) == result.second); + HPX_TEST(result == expected); +} + +int hpx_main() +{ + mismatch_differently_size_ranges_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + std::vector const cfg = {"hpx.os_threads=all"}; + + hpx::local::init_params init_args; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/regressions/search_larger_2nd_range.cpp b/libs/core/algorithms/tests/regressions/search_larger_2nd_range.cpp new file mode 100644 index 000000000000..29a8b8f29bdb --- /dev/null +++ b/libs/core/algorithms/tests/regressions/search_larger_2nd_range.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +// If the second range is larger than the first range, search should return the +// past-the-end iterator of the second range. + +#include +#include +#include +#include + +#include +#include +#include +#include + +void search_larger_2nd_range_test() +{ + std::vector a{1, 2, 3}; + std::vector b{1, 2, 3, 4}; + + auto result = hpx::search( + hpx::execution::par, a.begin(), a.end(), b.begin(), b.end()); + + auto expected = std::search(a.begin(), a.end(), b.begin(), b.end()); + + HPX_TEST(result == a.end()); + HPX_TEST(result == expected); +} + +int hpx_main() +{ + search_larger_2nd_range_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + std::vector const cfg = {"hpx.os_threads=all"}; + + hpx::local::init_params init_args; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt index 8b5426b9b544..7d3a9204530c 100644 --- a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt @@ -18,7 +18,6 @@ endforeach() # add tests set(tests adjacentdifference - adjacentdifference_sender adjacentfind adjacentfind_binary all_of @@ -45,7 +44,6 @@ set(tests fill filln find - find_sender findend findfirstof findfirstof_binary @@ -54,7 +52,6 @@ set(tests foreach foreach_executors foreach_prefetching - foreach_sender foreach_scheduler foreachn foreachn_exception @@ -67,7 +64,6 @@ set(tests for_loop_n_strided for_loop_reduction for_loop_reduction_async - for_loop_sender for_loop_strided generate generaten @@ -111,10 +107,8 @@ set(tests replace_copy_if reverse reverse_copy - reverse_sender rotate rotate_copy - rotate_sender search searchn set_difference @@ -161,6 +155,70 @@ if(HPX_WITH_CXX17_STD_EXECUTION_POLICES) set(tests ${tests} foreach_std_policies) endif() +if(HPX_WITH_STDEXEC) + set(tests + ${tests} + adjacentdifference_sender + adjacentfind_sender + all_of_sender + any_of_sender + copy_sender + copyn_sender + count_sender + countif_sender + destroy_sender + destroyn_sender + ends_with_sender + equal_sender + equal_binary_sender + fill_sender + filln_sender + find_sender + findend_sender + findfirstof_sender + findif_sender + findifnot_sender + foreach_sender + foreachn_sender + generate_sender + generaten_sender + is_heap_sender + is_heap_until_sender + includes_sender + is_partitioned_sender + is_sorted_sender + is_sorted_until_sender + lexicographical_compare_sender + max_element_sender + min_element_sender + minmax_element_sender + mismatch_sender + mismatch_binary_sender + move_sender + none_of_sender + reduce_sender + remove_sender + remove_if_sender + replace_sender + replace_if_sender + replace_copy_sender + replace_copy_if_sender + reverse_sender + reverse_copy_sender + rotate_sender + rotate_copy_sender + search_sender + starts_with_sender + swapranges_sender + transform_sender + transform_binary_sender + transform_binary2_sender + transform_reduce_sender + transform_reduce_binary_sender + unique_sender + ) +endif() + foreach(test ${tests}) set(sources ${test}.cpp) diff --git a/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp index 932c94567355..0bd32e56d915 100644 --- a/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2015 Daniel Bourgeois // Copyright (c) 2022 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,7 @@ #pragma once +#include #include #include #include @@ -69,6 +71,7 @@ void test_adjacent_difference_direct(Policy l, ExPolicy policy) HPX_TEST(std::end(d) == it); } +#if defined(HPX_HAVE_STDEXEC) template void test_adjacent_difference_sender(Policy l, ExPolicy&& policy) { @@ -85,22 +88,51 @@ void test_adjacent_difference_sender(Policy l, ExPolicy&& policy) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) - auto result = - tt::sync_wait(ex::just(std::begin(c), std::end(c), std::begin(d)) | + + { + auto snd_result = + tt::sync_wait(ex::just(std::begin(c), std::end(c), std::begin(d)) | + hpx::adjacent_difference(policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + std::adjacent_difference(std::begin(c), std::end(c), std::begin(d_ans)); + + HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), + [](auto lhs, auto rhs) { return lhs == rhs; })); + HPX_TEST(std::end(d) == result); + } + + { + // edge case: empty range + auto snd_result = tt::sync_wait( + ex::just(std::begin(c), std::begin(c), std::begin(d)) | hpx::adjacent_difference(policy.on(exec))); -#else - auto result = ex::just(std::begin(c), std::end(c), std::begin(d)) | - hpx::adjacent_difference(policy.on(exec)) | tt::sync_wait(); -#endif + auto result = hpx::get<0>(*snd_result); - std::adjacent_difference(std::begin(c), std::end(c), std::begin(d_ans)); + std::adjacent_difference( + std::begin(c), std::begin(c), std::begin(d_ans)); - HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), - [](auto lhs, auto rhs) { return lhs == rhs; })); + HPX_TEST(std::begin(d) == result); + HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), + [](auto lhs, auto rhs) { return lhs == rhs; })); + } - HPX_TEST(std::end(d) == hpx::get<0>(*result)); + { + // edge case: range of size one + auto snd_result = tt::sync_wait( + ex::just(std::begin(c), ++std::begin(c), std::begin(d)) | + hpx::adjacent_difference(policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + std::adjacent_difference( + std::begin(c), ++std::begin(c), std::begin(d_ans)); + + HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), + [](auto lhs, auto rhs) { return lhs == rhs; })); + HPX_TEST(++std::begin(d) == result); + } } +#endif template void test_adjacent_difference_async(ExPolicy&& p) @@ -139,14 +171,10 @@ void test_adjacent_difference_async_direct(Policy l, ExPolicy&& p) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) + auto result = tt::sync_wait(hpx::adjacent_difference( p.on(exec), std::begin(c), std::end(c), std::begin(d))); -#else - auto result = hpx::adjacent_difference( - p.on(exec), std::begin(c), std::end(c), std::begin(d)) | - tt::sync_wait(); -#endif + std::adjacent_difference(std::begin(c), std::end(c), std::begin(d_ans)); HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), diff --git a/libs/core/algorithms/tests/unit/algorithms/adjacentfind_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/adjacentfind_sender.cpp new file mode 100644 index 000000000000..d28d96227e99 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/adjacentfind_sender.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "adjacentfind_tests.hpp" + +template +void adjacent_find_sender_test() +{ + using namespace hpx::execution; + + test_adjacent_find_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_adjacent_find_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_adjacent_find_sender(hpx::launch::async, par(task), IteratorTag()); + test_adjacent_find_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + adjacent_find_sender_test(); + adjacent_find_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/adjacentfind_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/adjacentfind_tests.hpp index ff5f6fd11b21..5e34a2527791 100644 --- a/libs/core/algorithms/tests/unit/algorithms/adjacentfind_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/adjacentfind_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,7 +7,9 @@ #pragma once +#include #include +#include #include #include @@ -52,6 +55,56 @@ void test_adjacent_find(ExPolicy policy, IteratorTag) HPX_TEST(index == iterator(test_index)); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_adjacent_find_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + // fill vector with random values about 1 + std::vector c(10007); + std::iota(std::begin(c), std::end(c), dis(gen)); + + std::size_t random_pos = dist(gen); //-V101 + + c[random_pos] = 1; + c[random_pos + 1] = 1; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + hpx::adjacent_find(ex_policy.on(exec))); + + iterator index = hpx::get<0>(*snd_result); + base_iterator test_index = std::begin(c) + random_pos; + + HPX_TEST(index == iterator(test_index)); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::adjacent_find(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(iterator(std::begin(c)) == result); + } +} +#endif + template void test_adjacent_find_async(ExPolicy p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/all_of_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/all_of_sender.cpp new file mode 100644 index 000000000000..d94231cdc183 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/all_of_sender.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include + +#include "all_of_tests.hpp" + +template +void all_of_sender_test() +{ + using namespace hpx::execution; + test_all_of_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_all_of_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_all_of_sender(hpx::launch::async, par(task), IteratorTag()); + test_all_of_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + all_of_sender_test(); + all_of_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/all_of_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/all_of_tests.hpp index 6b72044e514a..e229122b5633 100644 --- a/libs/core/algorithms/tests/unit/algorithms/all_of_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/all_of_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include #include @@ -67,6 +70,42 @@ void test_all_of(ExPolicy&& policy, IteratorTag) } } +#if defined(HPX_HAVE_STDEXEC) +template +void test_all_of_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + int iseq[] = {0, 23, 10007}; + for (int i : iseq) + { + std::vector c = test::fill_all_any_none(10007, i); //-V106 + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + [](auto v) { return v != 0; }) | + hpx::all_of(ex_policy.on(exec))); + bool result = hpx::get<0>(*snd_result); + + // verify values + bool expected = std::all_of( + std::begin(c), std::end(c), [](auto v) { return v != 0; }); + + HPX_TEST_EQ(result, expected); + } +} +#endif + template void test_all_of_ranges_seq(IteratorTag, Proj proj = Proj()) { diff --git a/libs/core/algorithms/tests/unit/algorithms/any_of_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/any_of_sender.cpp new file mode 100644 index 000000000000..51975e5a9591 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/any_of_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "any_of_tests.hpp" + +template +void any_of_sender_test() +{ + using namespace hpx::execution; + test_any_of_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_any_of_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_any_of_sender(hpx::launch::async, par(task), IteratorTag()); + test_any_of_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + any_of_sender_test(); + any_of_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/any_of_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/any_of_tests.hpp index 25308ce6be04..d4b601249b37 100644 --- a/libs/core/algorithms/tests/unit/algorithms/any_of_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/any_of_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,7 +7,9 @@ #pragma once +#include #include +#include #include #include #include @@ -67,6 +70,43 @@ void test_any_of(ExPolicy&& policy, IteratorTag) } } +#if defined(HPX_HAVE_STDEXEC) +template +void test_any_of_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + int iseq[] = {0, 23, 10007}; + for (int i : iseq) + { + std::vector c = test::fill_all_any_none(10007, i); //-V106 + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + [](auto v) { return v != 0; }) | + hpx::any_of(ex_policy.on(exec))); + bool result = hpx::get<0>(*snd_result); + + // verify values + bool expected = std::any_of( + std::begin(c), std::end(c), [](auto v) { return v != 0; }); + + HPX_TEST_EQ(result, expected); + } +} +#endif + template void test_any_of_ranges_seq(IteratorTag, Proj proj = Proj()) { diff --git a/libs/core/algorithms/tests/unit/algorithms/copy_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/copy_sender.cpp new file mode 100644 index 000000000000..f2273c43f9ab --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/copy_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "copy_tests.hpp" + +template +void copy_sender_test() +{ + using namespace hpx::execution; + test_copy_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_copy_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_copy_sender(hpx::launch::async, par(task), IteratorTag()); + test_copy_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + copy_sender_test(); + copy_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/copy_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/copy_tests.hpp index 1e0de1f182cd..1e678fb6d29c 100644 --- a/libs/core/algorithms/tests/unit/algorithms/copy_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/copy_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2021 Srinivas Yadav // Copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -70,6 +73,42 @@ void test_copy(ExPolicy&& policy, IteratorTag) HPX_TEST_EQ(count, d.size()); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_copy_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), gen()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d)) | + hpx::copy(ex_policy.on(exec))); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} +#endif + template void test_copy_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/copyn_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/copyn_sender.cpp new file mode 100644 index 000000000000..b57ef69ab87a --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/copyn_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "copyn_tests.hpp" + +template +void copy_n_sender_test() +{ + using namespace hpx::execution; + test_copy_n_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_copy_n_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_copy_n_sender(hpx::launch::async, par(task), IteratorTag()); + test_copy_n_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + copy_n_sender_test(); + copy_n_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/copyn_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/copyn_tests.hpp index cf00cbe47e43..316541d19db4 100644 --- a/libs/core/algorithms/tests/unit/algorithms/copyn_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/copyn_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2021 Srinivas Yadav // Copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,7 @@ #pragma once +#include #include #include #include @@ -72,6 +74,45 @@ void test_copy_n(ExPolicy&& policy, IteratorTag) HPX_TEST_EQ(count, d.size()); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_copy_n_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), gen()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), -1, std::begin(d)) | + hpx::copy_n(ex_policy.on(exec))); + HPX_TEST(std::all_of(std::begin(d), std::end(d), + [](std::size_t i) { return i == std::size_t{}; })); + + tt::sync_wait(ex::just(iterator(std::begin(c)), c.size(), std::begin(d)) | + hpx::copy_n(ex_policy.on(exec))); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} +#endif + template void test_copy_n_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/count_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/count_sender.cpp new file mode 100644 index 000000000000..16a59cffb22b --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/count_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "count_tests.hpp" + +template +void count_sender_test() +{ + using namespace hpx::execution; + test_count_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_count_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_count_sender(hpx::launch::async, par(task), IteratorTag()); + test_count_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + count_sender_test(); + count_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/count_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/count_tests.hpp index 4c2ad3e5bc16..27062db0d269 100644 --- a/libs/core/algorithms/tests/unit/algorithms/count_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/count_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2016 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include @@ -32,7 +35,7 @@ void test_count(IteratorTag) typedef test::test_iterator iterator; std::vector c(10007); - // assure gen() does not evalulate to zero + // assure gen() does not evaluate to zero std::iota(std::begin(c), std::end(c), gen() + 1); std::size_t find_count = dis(gen); //-V101 for (std::size_t i = 0; i != find_count && i != c.size(); ++i) @@ -56,7 +59,7 @@ void test_count(ExPolicy&& policy, IteratorTag) typedef test::test_iterator iterator; std::vector c(10007); - // assure gen() does not evalulate to zero + // assure gen() does not evaluate to zero std::iota(std::begin(c), std::end(c), gen() + 1); std::size_t find_count = dis(gen); //-V101 for (std::size_t i = 0; i != find_count && i != c.size(); ++i) @@ -70,6 +73,41 @@ void test_count(ExPolicy&& policy, IteratorTag) HPX_TEST_EQ(num_items, static_cast(find_count)); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_count_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + // assure gen() does not evaluate to zero + std::iota(std::begin(c), std::end(c), gen() + 1); + std::size_t find_count = dis(gen); //-V101 + for (std::size_t i = 0; i != find_count && i != c.size(); ++i) + { + c[i] = 0; + } + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), int(0)) | + hpx::count(ex_policy.on(exec))); + + std::int64_t num_items = hpx::get<0>(*snd_result); + + HPX_TEST_EQ(num_items, static_cast(find_count)); +} +#endif + template void test_count_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/countif_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/countif_sender.cpp new file mode 100644 index 000000000000..0d7d2a1255a8 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/countif_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "countif_tests.hpp" + +template +void count_if_sender_test() +{ + using namespace hpx::execution; + test_count_if_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_count_if_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_count_if_sender(hpx::launch::async, par(task), IteratorTag()); + test_count_if_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + count_if_sender_test(); + count_if_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/countif_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/countif_tests.hpp index 2e4a01ab4f62..3dd04ff8be96 100644 --- a/libs/core/algorithms/tests/unit/algorithms/countif_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/countif_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2016 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include @@ -80,6 +83,38 @@ void test_count_if(ExPolicy&& policy, IteratorTag) HPX_TEST_EQ(num_items, 50u); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_count_if_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using diff_type = std::vector::difference_type; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(100007); + std::iota(std::begin(c), std::begin(c) + 50, 0); + std::iota(std::begin(c) + 50, std::end(c), dis(gen) + 50); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + smaller_than_50()) | + hpx::count_if(ex_policy.on(exec))); + + diff_type num_items = hpx::get<0>(*snd_result); + + HPX_TEST_EQ(num_items, 50u); +} +#endif + template void test_count_if_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/destroy_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/destroy_sender.cpp new file mode 100644 index 000000000000..bb58c966eb1e --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/destroy_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "destroy_tests.hpp" + +template +void destroy_sender_test() +{ + using namespace hpx::execution; + test_destroy_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_destroy_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_destroy_sender(hpx::launch::async, par(task), IteratorTag()); + test_destroy_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + destroy_sender_test(); + destroy_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/destroy_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/destroy_tests.hpp index 984ca68bfb36..68f7a1deaa41 100644 --- a/libs/core/algorithms/tests/unit/algorithms/destroy_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/destroy_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,7 @@ #pragma once +#include #include #include #include @@ -96,6 +98,41 @@ void test_destroy(ExPolicy&& policy, IteratorTag) std::free(p); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_destroy_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = destructable*; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + destructable* p = + (destructable*) std::malloc(data_size * sizeof(destructable)); + + // value-initialize data in array + std::for_each(p, p + data_size, [](destructable& d) { + ::new (static_cast(std::addressof(d))) destructable; + }); + + destruct_count.store(0); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(p), iterator(p + data_size)) | + hpx::destroy(ex_policy.on(exec))); + + HPX_TEST_EQ(destruct_count.load(), data_size); + + std::free(p); +} +#endif + template void test_destroy_async(ExPolicy&& policy, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/destroyn_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/destroyn_sender.cpp new file mode 100644 index 000000000000..9330c89a0527 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/destroyn_sender.cpp @@ -0,0 +1,136 @@ +// Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +std::atomic destruct_count(0); +unsigned int seed = std::random_device{}(); +std::mt19937 gen(seed); + +struct destructable +{ + destructable() + : value_(0) + { + } + + ~destructable() + { + ++destruct_count; + } + + std::uint32_t value_; +}; + +std::size_t const data_size = 10007; + +//////////////////////////////////////////////////////////////////////////// +template +void test_destroy_n_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = destructable*; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + destructable* p = + (destructable*) std::malloc(data_size * sizeof(destructable)); + + // value-initialize data in array + std::for_each(p, p + data_size, [](destructable& d) { + ::new (static_cast(std::addressof(d))) destructable; + }); + + destruct_count.store(0); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait( + ex::just(iterator(p), -1) | hpx::destroy_n(ex_policy.on(exec))); + HPX_TEST_EQ(destruct_count.load(), static_cast(0)); + + tt::sync_wait( + ex::just(iterator(p), data_size) | hpx::destroy_n(ex_policy.on(exec))); + + HPX_TEST_EQ(destruct_count.load(), data_size); + + std::free(p); +} + +template +void destroy_n_sender_test() +{ + using namespace hpx::execution; + test_destroy_n_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_destroy_n_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_destroy_n_sender(hpx::launch::async, par(task), IteratorTag()); + test_destroy_n_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + destroy_n_sender_test(); + destroy_n_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/ends_with_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/ends_with_sender.cpp new file mode 100644 index 000000000000..55a842ae6f0e --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/ends_with_sender.cpp @@ -0,0 +1,148 @@ +// Copyright (c) 2018 Christopher Ogle +// Copyright (c) 2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +unsigned int seed = std::random_device{}(); +std::mt19937 gen(seed); + +//////////////////////////////////////////////////////////////////////////// +template +void test_ends_with_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + + using scheduler_t = ex::thread_pool_policy_scheduler; + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::uniform_int_distribution dis1(3, 10007); + auto end1 = dis1(gen); + std::uniform_int_distribution dis2(2, end1 - 1); + auto end2 = dis2(gen); + + auto some_ints = std::vector(end1); + std::iota(some_ints.begin(), some_ints.end(), 1); + auto some_more_ints = std::vector(end1 - end2 + 1); + std::iota(some_more_ints.begin(), some_more_ints.end(), end2); + auto some_wrong_ints = std::vector(end2); + std::iota(some_wrong_ints.begin(), some_wrong_ints.end(), 1); + + { + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(some_ints)), + iterator(std::end(some_ints)), + iterator(std::begin(some_more_ints)), + iterator(std::end(some_more_ints))) | + hpx::ends_with(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + HPX_TEST(result); + } + + { + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(some_ints)), + iterator(std::end(some_ints)), + iterator(std::begin(some_wrong_ints)), + iterator(std::end(some_wrong_ints))) | + hpx::ends_with(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + HPX_TEST(!result); + } + + { + // edge case: second ranger large than the first + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(some_more_ints)), + iterator(std::end(some_more_ints)), + iterator(std::begin(some_ints)), + iterator(std::end(some_ints))) | + hpx::ends_with(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + HPX_TEST(!result); + } +} + +template +void ends_with_sender_test() +{ + using namespace hpx::execution; + test_ends_with_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_ends_with_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_ends_with_sender(hpx::launch::async, par(task), IteratorTag()); + test_ends_with_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +void ends_with_test() +{ + ends_with_sender_test(); + ends_with_sender_test(); +} + +//////////////////////////////////////////////////////////////////////////// +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + gen.seed(seed); + + ends_with_test(); + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/equal_binary.cpp b/libs/core/algorithms/tests/unit/algorithms/equal_binary.cpp index cf2f64421f99..0396e8cbc05b 100644 --- a/libs/core/algorithms/tests/unit/algorithms/equal_binary.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/equal_binary.cpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -102,6 +103,26 @@ void equal_binary_bad_alloc_test() test_equal_binary_bad_alloc(); } +//////////////////////////////////////////////////////////////////////////////// + +template +void test_equal_binary_edge_cases() +{ + using namespace hpx::execution; + + test_equal_binary_edge_cases(seq, IteratorTag()); + test_equal_binary_edge_cases(par, IteratorTag()); + + test_equal_binary_edge_cases(seq(task), IteratorTag()); + test_equal_binary_edge_cases(par(task), IteratorTag()); +} + +void equal_binary_edge_cases_test() +{ + test_equal_binary_edge_cases(); + test_equal_binary_edge_cases(); +} + /////////////////////////////////////////////////////////////////////////////// int hpx_main(hpx::program_options::variables_map& vm) { @@ -115,6 +136,7 @@ int hpx_main(hpx::program_options::variables_map& vm) equal_binary_test2(); equal_binary_exception_test(); equal_binary_bad_alloc_test(); + equal_binary_edge_cases_test(); return hpx::local::finalize(); } diff --git a/libs/core/algorithms/tests/unit/algorithms/equal_binary_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/equal_binary_sender.cpp new file mode 100644 index 000000000000..e9f5b648b754 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/equal_binary_sender.cpp @@ -0,0 +1,84 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "equal_binary_tests.hpp" + +template +void equal_binary_sender_test() +{ + using namespace hpx::execution; + test_equal_binary_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_equal_binary_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_equal_binary_sender(hpx::launch::async, par(task), IteratorTag()); + test_equal_binary_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +template +void equal_binary_edge_cases_sender_test() +{ + using namespace hpx::execution; + + test_equal_binary_edge_cases_sender( + hpx::launch::sync, seq(task), IteratorTag()); + test_equal_binary_edge_cases_sender( + hpx::launch::sync, unseq(task), IteratorTag()); + + test_equal_binary_edge_cases_sender( + hpx::launch::async, par(task), IteratorTag()); + test_equal_binary_edge_cases_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + equal_binary_sender_test(); + equal_binary_sender_test(); + + equal_binary_edge_cases_sender_test(); + equal_binary_edge_cases_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/equal_binary_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/equal_binary_tests.hpp index c9a63df771f3..9754a0934e99 100644 --- a/libs/core/algorithms/tests/unit/algorithms/equal_binary_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/equal_binary_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include @@ -459,3 +462,177 @@ void test_equal_binary_bad_alloc_async(ExPolicy&& p, IteratorTag) HPX_TEST(caught_bad_alloc); HPX_TEST(returned_from_algorithm); } + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(HPX_HAVE_STDEXEC) +template +void test_equal_binary_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c1(10007); + std::vector c2(c1.size()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + auto policy = ex_policy.on(exec); + + int first_value = gen(); //-V101 + std::iota(std::begin(c1), std::end(c1), first_value); + std::iota(std::begin(c2), std::end(c2), first_value); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::end(c2)) | + hpx::equal(policy)); + bool result = hpx::get<0>(*snd_result); + + bool expected = + std::equal(std::begin(c1), std::end(c1), std::begin(c2)); + + // verify values + HPX_TEST_EQ(result, expected); + } + + { + std::uniform_int_distribution<> dis(0, c1.size() - 1); + c1[dis(gen)] += 1; //-V104 + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::end(c2)) | + hpx::equal(policy)); + bool result = hpx::get<0>(*snd_result); + + bool expected = + std::equal(std::begin(c1), std::end(c1), std::begin(c2)); + + // verify values + HPX_TEST_EQ(result, expected); + } +} +#endif + +//////////////////////////////////////////////////////////////////////////////// + +template +void test_equal_binary_edge_cases(ExPolicy&& policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + std::vector c1{0, 1, 2, 3, 4}; + std::vector c2{5, 6, 7, 8, 9}; + + { + // both ranges empty + auto result = hpx::equal(policy, iterator(std::begin(c1)), + iterator(std::begin(c1)), std::begin(c2), std::begin(c2)); + HPX_TEST(hpx::unwrap(result)); + } + + { + // only first range empty + auto result = hpx::equal(policy, iterator(std::begin(c1)), + iterator(std::begin(c1)), std::begin(c1), std::end(c1)); + HPX_TEST(!hpx::unwrap(result)); + } + + { + // only second range empty + auto result = hpx::equal(policy, iterator(std::begin(c1)), + iterator(std::end(c1)), std::begin(c1), std::begin(c1)); + HPX_TEST(!hpx::unwrap(result)); + } + + { + // ranges of different length + auto result = hpx::equal(policy, iterator(std::begin(c1)), + iterator(std::begin(c1) + 1), std::begin(c1), std::begin(c1) + 2); + HPX_TEST(!hpx::unwrap(result)); + } +} + +#if defined(HPX_HAVE_STDEXEC) +template +void test_equal_binary_edge_cases_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c1{0, 1, 2, 3, 4}; + std::vector c2{5, 6, 7, 8, 9}; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + auto policy = ex_policy.on(exec); + + { + // both ranges empty + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::begin(c1)), + std::begin(c2), std::begin(c2)) | + hpx::equal(policy)); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(result); + } + + { + // only first range empty + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::begin(c1)), + std::begin(c1), std::end(c1)) | + hpx::equal(policy)); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(!result); + } + + { + // only second range empty + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c1), std::begin(c1)) | + hpx::equal(policy)); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(!result); + } + + { + // ranges of different length + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::begin(c1) + 1), + std::begin(c1), std::begin(c1) + 2) | + hpx::equal(policy)); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(!result); + } +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/equal_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/equal_sender.cpp new file mode 100644 index 000000000000..1a7349e46f52 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/equal_sender.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "equal_tests.hpp" + +template +void equal_sender_test1() +{ + using namespace hpx::execution; + test_equal1_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_equal1_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_equal1_sender(hpx::launch::async, par(task), IteratorTag()); + test_equal1_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +template +void equal_sender_test2() +{ + using namespace hpx::execution; + test_equal2_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_equal2_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_equal2_sender(hpx::launch::async, par(task), IteratorTag()); + test_equal2_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + equal_sender_test1(); + equal_sender_test1(); + + equal_sender_test2(); + equal_sender_test2(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/equal_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/equal_tests.hpp index 2ac0d664c34a..81ca559e8e8a 100644 --- a/libs/core/algorithms/tests/unit/algorithms/equal_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/equal_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include @@ -102,6 +105,76 @@ void test_equal1(ExPolicy&& policy, IteratorTag) } } +#if defined(HPX_HAVE_STDEXEC) +template +void test_equal1_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c1(10007); + std::vector c2(c1.size()); + + int first_value = gen(); //-V101 + std::iota(std::begin(c1), std::end(c1), first_value); + std::iota(std::begin(c2), std::end(c2), first_value); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(std::begin(c1), std::end(c1), std::begin(c2)) | + hpx::equal(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = + std::equal(std::begin(c1), std::end(c1), std::begin(c2)); + + // verify values + HPX_TEST_EQ(result, expected); + } + + { + std::uniform_int_distribution<> dis(0, c1.size() - 1); + ++c1[dis(gen)]; //-V104 + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c1)), + iterator(std::end(c1)), std::begin(c2)) | + hpx::equal(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = + std::equal(std::begin(c1), std::end(c1), std::begin(c2)); + + // verify values + HPX_TEST_EQ(result, expected); + } + + { + // edge case: empty range + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c1)), + iterator(std::begin(c1)), std::begin(c2)) | + hpx::equal(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(result); + } +} +#endif + template void test_equal1_async(ExPolicy&& p, IteratorTag) { @@ -223,6 +296,77 @@ void test_equal2(ExPolicy&& policy, IteratorTag) } } +#if defined(HPX_HAVE_STDEXEC) +template +void test_equal2_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c1(10007); + std::vector c2(c1.size()); + + int first_value = gen(); //-V101 + std::iota(std::begin(c1), std::end(c1), first_value); + std::iota(std::begin(c2), std::end(c2), first_value); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::equal_to<>()) | + hpx::equal(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = + std::equal(std::begin(c1), std::end(c1), std::begin(c2)); + + // verify values + HPX_TEST_EQ(result, expected); + } + + { + std::uniform_int_distribution<> dis(0, c1.size() - 1); + ++c1[dis(gen)]; //-V104 + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::equal_to<>()) | + hpx::equal(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = + std::equal(std::begin(c1), std::end(c1), std::begin(c2)); + + // verify values + HPX_TEST_EQ(result, expected); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::begin(c1)), + std::begin(c2), std::equal_to<>()) | + hpx::equal(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(result); + } +} +#endif + template void test_equal2_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/fill_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/fill_sender.cpp new file mode 100644 index 000000000000..42a08a6ff375 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/fill_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "fill_tests.hpp" + +template +void fill_sender_test() +{ + using namespace hpx::execution; + test_fill_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_fill_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_fill_sender(hpx::launch::async, par(task), IteratorTag()); + test_fill_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + fill_sender_test(); + fill_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/fill_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/fill_tests.hpp index 4a9945562b56..82423d46762e 100644 --- a/libs/core/algorithms/tests/unit/algorithms/fill_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/fill_tests.hpp @@ -1,6 +1,7 @@ // Copyright (c) 2014 Grant Mercer // Copyright (c) 2017-2020 Hartmut Kaiser // Copyright (c) 2021 Srinivas Yadav +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -8,6 +9,8 @@ #pragma once +#include +#include #include #include @@ -68,6 +71,38 @@ void test_fill(ExPolicy policy, IteratorTag) HPX_TEST_EQ(count, c.size()); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_fill_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), gen()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), 10) | + hpx::fill(ex_policy.on(exec))); + + // verify values + std::size_t count = 0; + std::for_each(std::begin(c), std::end(c), [&count](int v) -> void { + HPX_TEST_EQ(v, int(10)); + ++count; + }); + HPX_TEST_EQ(count, c.size()); +} +#endif + template void test_fill_async(ExPolicy p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/filln_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/filln_sender.cpp new file mode 100644 index 000000000000..015419916d8b --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/filln_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "filln_tests.hpp" + +template +void fill_n_sender_test() +{ + using namespace hpx::execution; + test_fill_n_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_fill_n_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_fill_n_sender(hpx::launch::async, par(task), IteratorTag()); + test_fill_n_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + fill_n_sender_test(); + fill_n_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/filln_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/filln_tests.hpp index d1c4ce3dfac3..829e11e5ca14 100644 --- a/libs/core/algorithms/tests/unit/algorithms/filln_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/filln_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2014 Grant Mercer // Copyright (c) 2017-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -67,6 +70,38 @@ void test_fill_n(ExPolicy policy, IteratorTag) HPX_TEST_EQ(count, c.size()); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_fill_n_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), gen()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), c.size(), 10) | + hpx::fill_n(ex_policy.on(exec))); + + // verify values + std::size_t count = 0; + std::for_each(std::begin(c), std::end(c), [&count](int v) -> void { + HPX_TEST_EQ(v, int(10)); + ++count; + }); + HPX_TEST_EQ(count, c.size()); +} +#endif + template void test_fill_n_async(ExPolicy p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/find_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/find_tests.hpp index 33bb3de9b3f7..966ef7fb9c00 100644 --- a/libs/core/algorithms/tests/unit/algorithms/find_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/find_tests.hpp @@ -1,6 +1,7 @@ // Copyright (c) 2021 Srinivas Yadav // Copyright (c) 2014 Grant Mercer // Copyright (c) 2022 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -8,6 +9,7 @@ #pragma once +#include #include #include #include @@ -69,6 +71,7 @@ void test_find(ExPolicy&& policy, IteratorTag) HPX_TEST(index == iterator(test_index)); } +#if defined(HPX_HAVE_STDEXEC) template void test_find_explicit_sender_direct(Policy l, ExPolicy&& policy, IteratorTag) { @@ -100,8 +103,8 @@ void test_find_explicit_sender_direct(Policy l, ExPolicy&& policy, IteratorTag) template void test_find_explicit_sender(Policy l, ExPolicy&& policy, IteratorTag) { - static_assert(hpx::is_execution_policy_v, - "hpx::is_execution_policy_v"); + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); typedef std::vector::iterator base_iterator; typedef test::test_iterator iterator; @@ -117,20 +120,16 @@ void test_find_explicit_sender(Policy l, ExPolicy&& policy, IteratorTag) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) + auto result = tt::sync_wait( ex::just(iterator(std::begin(c)), iterator(std::end(c)), int(1)) | hpx::find(policy.on(exec))); -#else - auto result = - ex::just(iterator(std::begin(c)), iterator(std::end(c)), int(1)) | - hpx::find(policy.on(exec)) | tt::sync_wait(); -#endif base_iterator test_index = std::begin(c) + c.size() / 2; HPX_TEST(hpx::get<0>(*result) == iterator(test_index)); } +#endif template void test_find_async(ExPolicy&& p, IteratorTag) @@ -156,11 +155,12 @@ void test_find_async(ExPolicy&& p, IteratorTag) HPX_TEST(f.get() == iterator(test_index)); } +#if defined(HPX_HAVE_STDEXEC) template void test_find_explicit_sender_direct_async(Policy l, ExPolicy&& p, IteratorTag) { - static_assert(hpx::is_execution_policy_v, - "hpx::is_execution_policy_v"); + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); typedef std::vector::iterator base_iterator; typedef test::test_iterator iterator; @@ -176,19 +176,17 @@ void test_find_explicit_sender_direct_async(Policy l, ExPolicy&& p, IteratorTag) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) - auto result = tt::sync_wait(hpx::find( + + auto snd_result = tt::sync_wait(hpx::find( p.on(exec), iterator(std::begin(c)), iterator(std::end(c)), int(1))); -#else - auto result = hpx::find(p.on(exec), iterator(std::begin(c)), - iterator(std::end(c)), int(1)) | - tt::sync_wait(); -#endif + auto result = hpx::get<0>(*snd_result); + // create iterator at position of value to be found base_iterator test_index = std::begin(c) + c.size() / 2; - HPX_TEST(hpx::get<0>(*result) == iterator(test_index)); + HPX_TEST(result == iterator(test_index)); } +#endif /////////////////////////////////////////////////////////////////////////////// template diff --git a/libs/core/algorithms/tests/unit/algorithms/findend_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/findend_sender.cpp new file mode 100644 index 000000000000..b576c478f96e --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/findend_sender.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "findend_tests.hpp" + +template +void find_end_sender_test1() +{ + using namespace hpx::execution; + test_find_end1_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_find_end1_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_find_end1_sender(hpx::launch::async, par(task), IteratorTag()); + test_find_end1_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +template +void find_end_sender_test2() +{ + using namespace hpx::execution; + test_find_end2_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_find_end2_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_find_end2_sender(hpx::launch::async, par(task), IteratorTag()); + test_find_end2_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + find_end_sender_test1(); + find_end_sender_test1(); + + find_end_sender_test2(); + find_end_sender_test2(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/findend_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/findend_tests.hpp index d2e53d19c3f5..ce8afac9652d 100644 --- a/libs/core/algorithms/tests/unit/algorithms/findend_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/findend_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2021 Srinivas Yadav -// copyright (c) 2014 Grant Mercer +// Copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -78,6 +81,72 @@ void test_find_end1(ExPolicy&& policy, IteratorTag) HPX_TEST(index == test_index); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_find_end1_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), dis(gen)); + // create subsequence in middle of vector + c[c.size() / 2] = 1; + c[c.size() / 2 + 1] = 2; + + int h[] = {1, 2}; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(h), std::end(h)) | + hpx::find_end(ex_policy.on(exec))); + + iterator index = hpx::get<0>(*snd_result); + + iterator test_index = std::find_end(iterator(std::begin(c)), + iterator(std::end(c)), std::begin(h), std::end(h)); + + HPX_TEST(index == test_index); + } + + { + // edge case: first2 == end2 + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(h), std::begin(h)) | + hpx::find_end(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(iterator(std::end(c)) == result); + } + + { + // edge case: distance(first2, end2) > distance(first1, end1) + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + std::begin(h), std::end(h)) | + hpx::find_end(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(iterator(std::begin(c)) == result); + } +} +#endif + template void test_find_end1_async(ExPolicy&& p, IteratorTag) { @@ -158,6 +227,47 @@ void test_find_end2(ExPolicy&& policy, IteratorTag) HPX_TEST(index == test_index); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_find_end2_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + // fill vector with random values about 2 + std::fill(std::begin(c), std::end(c), dis(gen)); + // create subsequence at start and end + c[0] = 1; + c[1] = 2; + c[c.size() - 1] = 2; + c[c.size() - 2] = 1; + + int h[] = {1, 2}; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(h), std::end(h)) | + hpx::find_end(ex_policy.on(exec))); + iterator index = hpx::get<0>(*snd_result); + + iterator test_index = std::find_end(iterator(std::begin(c)), + iterator(std::end(c)), std::begin(h), std::end(h)); + + HPX_TEST(index == test_index); +} +#endif + template void test_find_end2_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/findfirstof_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/findfirstof_sender.cpp new file mode 100644 index 000000000000..e7938ee56639 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/findfirstof_sender.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "findfirstof_tests.hpp" + +template +void find_first_of_sender_test() +{ + using namespace hpx::execution; + test_find_first_of_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_find_first_of_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_find_first_of_sender(hpx::launch::async, par(task), IteratorTag()); + test_find_first_of_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + find_first_of_sender_test(); + find_first_of_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/findfirstof_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/findfirstof_tests.hpp index 7fb6c1c4473b..97cff1921a5f 100644 --- a/libs/core/algorithms/tests/unit/algorithms/findfirstof_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/findfirstof_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2021 Srinivas Yadav // copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -73,6 +76,44 @@ void test_find_first_of(ExPolicy&& policy, IteratorTag) HPX_TEST(index == iterator(test_index)); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_find_first_of_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + int find_first_of_pos = dis(gen); + int random_sub_seq_pos = dist(gen); + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), gen() + 19); + int h[] = {1, 7, 18, 3}; + c[find_first_of_pos] = h[random_sub_seq_pos]; //-V108 + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(h), std::end(h)) | + hpx::find_first_of(ex_policy.on(exec))); + + iterator index = hpx::get<0>(*snd_result); + + base_iterator test_index = std::begin(c) + find_first_of_pos; + + HPX_TEST(index == iterator(test_index)); +} +#endif + template void test_find_first_of_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/findif_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/findif_sender.cpp new file mode 100644 index 000000000000..c27448d41053 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/findif_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "findif_tests.hpp" + +template +void find_if_sender_test() +{ + using namespace hpx::execution; + test_find_if_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_find_if_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_find_if_sender(hpx::launch::async, par(task), IteratorTag()); + test_find_if_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + find_if_sender_test(); + find_if_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/findif_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/findif_tests.hpp index f47793254d46..bea933478008 100644 --- a/libs/core/algorithms/tests/unit/algorithms/findif_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/findif_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2021 Srinivas Yadav // copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -66,6 +69,40 @@ void test_find_if(ExPolicy&& policy, IteratorTag) HPX_TEST(index == iterator(test_index)); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_find_if_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + //fill vector with random values about 1 + std::fill(std::begin(c), std::end(c), dis(gen)); + c.at(c.size() / 2) = 1; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + [](auto v) { return v == int(1); }) | + hpx::find_if(ex_policy.on(exec))); + + iterator index = hpx::get<0>(*snd_result); + + base_iterator test_index = std::begin(c) + c.size() / 2; + + HPX_TEST(index == iterator(test_index)); +} +#endif + template void test_find_if_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/findifnot_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/findifnot_sender.cpp new file mode 100644 index 000000000000..f3912d2d3e5d --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/findifnot_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "findifnot_tests.hpp" + +template +void find_if_not_sender_test() +{ + using namespace hpx::execution; + test_find_if_not_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_find_if_not_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_find_if_not_sender(hpx::launch::async, par(task), IteratorTag()); + test_find_if_not_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + find_if_not_sender_test(); + find_if_not_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/findifnot_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/findifnot_tests.hpp index f80946105bd3..2607e4cd9bf6 100644 --- a/libs/core/algorithms/tests/unit/algorithms/findifnot_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/findifnot_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2021 Srinivas Yadav // copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -65,6 +68,41 @@ void test_find_if_not(ExPolicy&& policy, IteratorTag) HPX_TEST(index == iterator(test_index)); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_find_if_not_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + //fill vector with random values about 1 + std::fill(std::begin(c), std::end(c), dis(gen)); + c.at(c.size() / 2) = 1; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + [](auto v) { return v != int(1); }) | + hpx::find_if_not(ex_policy.on(exec))); + + iterator index = hpx::get<0>(*snd_result); + + base_iterator test_index = std::begin(c) + c.size() / 2; + + HPX_TEST(index == iterator(test_index)); +} +#endif + template void test_find_if_not_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/for_loop_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/for_loop_sender.cpp deleted file mode 100644 index 301894b444ee..000000000000 --- a/libs/core/algorithms/tests/unit/algorithms/for_loop_sender.cpp +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (c) 2016-2022 Hartmut Kaiser -// -// SPDX-License-Identifier: BSL-1.0 -// 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) - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "test_utils.hpp" - -/////////////////////////////////////////////////////////////////////////////// -unsigned int seed = std::random_device{}(); -std::mt19937 gen(seed); - -template -void test_for_loop_sender_direct(Policy l, ExPolicy&& policy, IteratorTag) -{ - static_assert(hpx::is_execution_policy_v, - "hpx::is_execution_policy_v"); - - typedef std::vector::iterator base_iterator; - typedef test::test_iterator iterator; - - std::vector c(10007); - std::iota(std::begin(c), std::end(c), gen()); - - namespace ex = hpx::execution::experimental; - - using scheduler_t = ex::thread_pool_policy_scheduler; - - auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); - hpx::experimental::for_loop(policy.on(exec), iterator(std::begin(c)), - iterator(std::end(c)), [](iterator it) { *it = 42; }); - - // verify values - std::size_t count = 0; - std::for_each(std::begin(c), std::end(c), [&count](std::size_t v) -> void { - HPX_TEST_EQ(v, std::size_t(42)); - ++count; - }); - HPX_TEST_EQ(count, c.size()); -} - -template -void test_for_loop_sender_direct_async(Policy l, ExPolicy&& policy, IteratorTag) -{ - static_assert(hpx::is_async_execution_policy_v, - "hpx::is_async_execution_policy_v"); - - typedef std::vector::iterator base_iterator; - typedef test::test_iterator iterator; - - std::vector c(10007); - std::iota(std::begin(c), std::end(c), gen()); - - namespace ex = hpx::execution::experimental; - namespace tt = hpx::this_thread::experimental; - - using scheduler_t = ex::thread_pool_policy_scheduler; - - auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) - tt::sync_wait( - hpx::experimental::for_loop(policy.on(exec), iterator(std::begin(c)), - iterator(std::end(c)), [](iterator it) { *it = 42; })); -#else - hpx::experimental::for_loop(policy.on(exec), iterator(std::begin(c)), - iterator(std::end(c)), [](iterator it) { *it = 42; }) | - tt::sync_wait(); -#endif - - // verify values - std::size_t count = 0; - std::for_each(std::begin(c), std::end(c), [&count](std::size_t v) -> void { - HPX_TEST_EQ(v, std::size_t(42)); - ++count; - }); - HPX_TEST_EQ(count, c.size()); -} - -template -void test_for_loop() -{ - using namespace hpx::execution; - - test_for_loop_sender_direct(hpx::launch::sync, seq, IteratorTag()); - test_for_loop_sender_direct(hpx::launch::sync, unseq, IteratorTag()); - test_for_loop_sender_direct(hpx::launch::async, par, IteratorTag()); - test_for_loop_sender_direct(hpx::launch::async, par_unseq, IteratorTag()); - - test_for_loop_sender_direct_async( - hpx::launch::sync, seq(task), IteratorTag()); - test_for_loop_sender_direct_async( - hpx::launch::sync, unseq(task), IteratorTag()); - test_for_loop_sender_direct_async( - hpx::launch::async, par(task), IteratorTag()); - test_for_loop_sender_direct_async( - hpx::launch::async, par_unseq(task), IteratorTag()); -} - -void for_loop_test() -{ - test_for_loop(); - test_for_loop(); -} - -template -void test_for_loop_sender(Policy l, ExPolicy&& policy, IteratorTag) -{ - static_assert(hpx::is_async_execution_policy_v, - "hpx::is_async_execution_policy_v"); - - typedef std::vector::iterator base_iterator; - typedef test::test_iterator iterator; - - std::vector c(10007); - std::iota(std::begin(c), std::end(c), gen()); - - namespace ex = hpx::execution::experimental; - namespace tt = hpx::this_thread::experimental; - - auto f = [](iterator it) { *it = 42; }; - - using scheduler_t = ex::thread_pool_policy_scheduler; - - auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) - tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), f) | - hpx::experimental::for_loop(policy.on(exec))); -#else - ex::just(iterator(std::begin(c)), iterator(std::end(c)), f) | - hpx::experimental::for_loop(policy.on(exec)) | tt::sync_wait(); -#endif - - // verify values - std::size_t count = 0; - std::for_each(std::begin(c), std::end(c), [&count](std::size_t v) -> void { - HPX_TEST_EQ(v, std::size_t(42)); - ++count; - }); - HPX_TEST_EQ(count, c.size()); -} - -template -void test_for_loop_sender() -{ - using namespace hpx::execution; - - test_for_loop_sender(hpx::launch::sync, seq(task), IteratorTag()); - test_for_loop_sender(hpx::launch::sync, unseq(task), IteratorTag()); - test_for_loop_sender(hpx::launch::async, par(task), IteratorTag()); - test_for_loop_sender(hpx::launch::async, par_unseq(task), IteratorTag()); -} - -void for_loop_test_sender() -{ - test_for_loop_sender(); - test_for_loop_sender(); -} - -/////////////////////////////////////////////////////////////////////////////// -int hpx_main(hpx::program_options::variables_map& vm) -{ - if (vm.count("seed")) - seed = vm["seed"].as(); - - std::cout << "using seed: " << seed << std::endl; - gen.seed(seed); - - for_loop_test(); - for_loop_test_sender(); - - return hpx::local::finalize(); -} - -int main(int argc, char* argv[]) -{ - // add command line option which controls the random number generator seed - using namespace hpx::program_options; - options_description desc_commandline( - "Usage: " HPX_APPLICATION_STRING " [options]"); - - desc_commandline.add_options()("seed,s", value(), - "the random number generator seed to use for this run"); - - // By default this test should run on all available cores - std::vector const cfg = {"hpx.os_threads=all"}; - - // Initialize and run HPX - hpx::local::init_params init_args; - init_args.desc_cmdline = desc_commandline; - init_args.cfg = cfg; - - HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, - "HPX main exited with non-zero status"); - - return hpx::util::report_errors(); -} diff --git a/libs/core/algorithms/tests/unit/algorithms/foreach_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/foreach_sender.cpp index 8c738a582201..2944aeb5e5c5 100644 --- a/libs/core/algorithms/tests/unit/algorithms/foreach_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/foreach_sender.cpp @@ -73,15 +73,12 @@ void test_for_each_explicit_sender_direct_async( using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) - auto result = tt::sync_wait(hpx::for_each( + + auto snd_result = tt::sync_wait(hpx::for_each( policy.on(exec), iterator(std::begin(c)), iterator(std::end(c)), f)); -#else - auto result = hpx::for_each(policy.on(exec), iterator(std::begin(c)), - iterator(std::end(c)), f) | - tt::sync_wait(); -#endif - HPX_TEST(hpx::get<0>(*result) == iterator(std::end(c))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result == iterator(std::end(c))); // verify values std::size_t count = 0; @@ -113,15 +110,13 @@ void test_for_each_explicit_sender(Policy l, ExPolicy&& policy, IteratorTag) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) - auto result = tt::sync_wait( + + auto snd_result = tt::sync_wait( ex::just(iterator(std::begin(c)), iterator(std::end(c)), f) | hpx::for_each(policy.on(exec))); -#else - auto result = ex::just(iterator(std::begin(c)), iterator(std::end(c)), f) | - hpx::for_each(policy.on(exec)) | tt::sync_wait(); -#endif - HPX_TEST(hpx::get<0>(*result) == iterator(std::end(c))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result == iterator(std::end(c))); // verify values std::size_t count = 0; diff --git a/libs/core/algorithms/tests/unit/algorithms/foreach_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/foreach_tests.hpp index 3c02b4873327..81427e4ea3e7 100644 --- a/libs/core/algorithms/tests/unit/algorithms/foreach_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/foreach_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2023 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,7 @@ #pragma once +#include #include #include #include @@ -371,6 +373,45 @@ void test_for_each_n(ExPolicy policy, IteratorTag) HPX_TEST_EQ(count, c.size()); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_for_each_n_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), gen()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), c.size(), set_42()) | + hpx::for_each_n(ex_policy.on(exec))); + + iterator result = hpx::get<0>(*snd_result); + + iterator end = iterator(std::end(c)); + HPX_TEST(result == end); + + // verify values + std::size_t count = 0; + std::for_each(std::begin(c), std::end(c), [&count](int v) -> void { + HPX_TEST_EQ(v, int(42)); + ++count; + }); + HPX_TEST_EQ(count, c.size()); +} +#endif + template void test_for_each_n_async(ExPolicy p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/foreachn_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/foreachn_sender.cpp new file mode 100644 index 000000000000..a707f7b0aaf8 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/foreachn_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "foreach_tests.hpp" + +template +void for_each_n_sender_test() +{ + using namespace hpx::execution; + test_for_each_n_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_for_each_n_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_for_each_n_sender(hpx::launch::async, par(task), IteratorTag()); + test_for_each_n_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + for_each_n_sender_test(); + for_each_n_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/generate_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/generate_sender.cpp new file mode 100644 index 000000000000..9d1411bb7019 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/generate_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "generate_tests.hpp" + +template +void generate_sender_test() +{ + using namespace hpx::execution; + test_generate_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_generate_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_generate_sender(hpx::launch::async, par(task), IteratorTag()); + test_generate_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + generate_sender_test(); + generate_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/generate_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/generate_tests.hpp index e88f041e68d7..0cb50f8cb23b 100644 --- a/libs/core/algorithms/tests/unit/algorithms/generate_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/generate_tests.hpp @@ -1,6 +1,7 @@ // Copyright (c) 2014 Grant Mercer // Copyright (c) 2020 Hartmut Kaiser // Copyright (c) 2021 Srinivas Yadav +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -8,7 +9,9 @@ #pragma once +#include #include +#include #include #include @@ -69,6 +72,40 @@ void test_generate(ExPolicy&& policy, IteratorTag) HPX_TEST_EQ(count, c.size()); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_generate_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + + auto gen = []() { return std::size_t(10); }; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), gen) | + hpx::generate(ex_policy.on(exec))); + + // verify values + std::size_t count = 0; + std::for_each(std::begin(c), std::end(c), [&count](std::size_t v) -> void { + HPX_TEST_EQ(v, std::size_t(10)); + ++count; + }); + HPX_TEST_EQ(count, c.size()); +} +#endif + template void test_generate_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/generaten_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/generaten_sender.cpp new file mode 100644 index 000000000000..d37496952280 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/generaten_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "generaten_tests.hpp" + +template +void generate_n_sender_test() +{ + using namespace hpx::execution; + test_generate_n_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_generate_n_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_generate_n_sender(hpx::launch::async, par(task), IteratorTag()); + test_generate_n_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + generate_n_sender_test(); + generate_n_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/generaten_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/generaten_tests.hpp index d88109baf641..0ac7b9b44a8e 100644 --- a/libs/core/algorithms/tests/unit/algorithms/generaten_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/generaten_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2014 Grant Mercer // Copyright (c) 2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,7 +8,9 @@ #pragma once +#include #include +#include #include #include @@ -65,6 +68,40 @@ void test_generate_n(ExPolicy&& policy, IteratorTag) HPX_TEST_EQ(count, c.size()); } +#if defined(HPX_HAVE_STDEXEC) +template +void test_generate_n_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + + auto gen = []() { return std::size_t(10); }; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), c.size(), gen) | + hpx::generate_n(ex_policy.on(exec))); + + // verify values + std::size_t count = 0; + std::for_each(std::begin(c), std::end(c), [&count](std::size_t v) -> void { + HPX_TEST_EQ(v, std::size_t(10)); + ++count; + }); + HPX_TEST_EQ(count, c.size()); +} +#endif + template void test_generate_n_async(ExPolicy&& p, IteratorTag) { diff --git a/libs/core/algorithms/tests/unit/algorithms/includes_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/includes_sender.cpp new file mode 100644 index 000000000000..1727a9bfdb9b --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/includes_sender.cpp @@ -0,0 +1,333 @@ +// Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +/////////////////////////////////////////////////////////////////////////////// +int seed = std::random_device{}(); +std::mt19937 gen(seed); + +/////////////////////////////////////////////////////////////////////////////// +template +void test_includes1_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c1(10007); + std::uniform_int_distribution<> dis(0, c1.size() - 1); + std::size_t start = dis(gen); + std::uniform_int_distribution<> dist(0, c1.size() - start - 1); + std::size_t end = start + dist(gen); + + std::size_t first_value = gen(); //-V101 + std::iota(std::begin(c1), std::end(c1), first_value); + + HPX_TEST_LTE(start, end); + + base_iterator start_it = std::next(std::begin(c1), start); + base_iterator end_it = std::next(std::begin(c1), end); + + { + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c1)), + iterator(std::end(c1)), start_it, end_it) | + hpx::includes(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = + std::includes(std::begin(c1), std::end(c1), start_it, end_it); + + // verify values + HPX_TEST_EQ(result, expected); + } + + { + // make sure std::less is not violated by incrementing one of the + // elements + std::transform(std::begin(c1), std::end(c1), std::begin(c1), + [](std::size_t val) { return 2 * val; }); + + std::vector c2; + std::copy(start_it, end_it, std::back_inserter(c2)); + + if (!c2.empty()) + { + std::uniform_int_distribution<> dis(0, c2.size() - 1); + ++c2[dis(gen)]; //-V104 + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::end(c2)) | + hpx::includes(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = std::includes( + std::begin(c1), std::end(c1), std::begin(c2), std::end(c2)); + + // verify values + HPX_TEST_EQ(result, expected); + } + } +} + +template +void test_includes2_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c1(10007); + std::size_t first_value = gen(); //-V101 + std::iota(std::begin(c1), std::end(c1), first_value); + + std::uniform_int_distribution<> dis(0, c1.size() - 1); + std::size_t start = dis(gen); + std::uniform_int_distribution<> dist(0, c1.size() - start - 1); + std::size_t end = start + dist(gen); + + HPX_TEST_LTE(start, end); + + base_iterator start_it = std::next(std::begin(c1), start); + base_iterator end_it = std::next(std::begin(c1), end); + + { + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), start_it, + end_it, std::less()) | + hpx::includes(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = std::includes(std::begin(c1), std::end(c1), start_it, + end_it, std::less()); + + // verify values + HPX_TEST_EQ(result, expected); + } + + { + // make sure std::less is not violated by incrementing one of the + // elements + std::transform(std::begin(c1), std::end(c1), std::begin(c1), + [](std::size_t val) { return 2 * val; }); + + std::vector c2; + std::copy(start_it, end_it, std::back_inserter(c2)); + + if (!c2.empty()) + { + std::uniform_int_distribution<> dis(0, c2.size() - 1); + ++c2[dis(gen)]; //-V104 + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::end(c2), std::less()) | + hpx::includes(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = std::includes(std::begin(c1), std::end(c1), + std::begin(c2), std::end(c2), std::less()); + + // verify values + HPX_TEST_EQ(result, expected); + } + } +} + +template +void test_includes_edge_cases_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c(10007); + std::size_t first_value = gen(); //-V101 + std::iota(std::begin(c), std::end(c), first_value); + + { + // only first range empty + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + std::begin(c), std::end(c), std::less{}) | + hpx::includes(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = std::includes(std::begin(c), std::begin(c), + std::begin(c), std::end(c), std::less{}); + + HPX_TEST(!result); + HPX_TEST_EQ(result, expected); + } + + { + // only second range empty + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(c), std::begin(c), std::less{}) | + hpx::includes(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = std::includes(std::begin(c), std::end(c), std::begin(c), + std::begin(c), std::less{}); + + HPX_TEST(result); + HPX_TEST_EQ(result, expected); + } + + { + // both ranges empty + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + std::begin(c), std::begin(c), std::less{}) | + hpx::includes(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool expected = std::includes(std::begin(c), std::begin(c), + std::begin(c), std::begin(c), std::less{}); + + HPX_TEST(result); + HPX_TEST_EQ(result, expected); + } +} + +template +void includes_sender_test1() +{ + using namespace hpx::execution; + test_includes1_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_includes1_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_includes1_sender(hpx::launch::async, par(task), IteratorTag()); + test_includes1_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +template +void includes_sender_test2() +{ + using namespace hpx::execution; + test_includes2_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_includes2_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_includes2_sender(hpx::launch::async, par(task), IteratorTag()); + test_includes2_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +template +void includes_sender_test_edge_cases() +{ + using namespace hpx::execution; + test_includes_edge_cases_sender( + hpx::launch::sync, seq(task), IteratorTag()); + test_includes_edge_cases_sender( + hpx::launch::sync, unseq(task), IteratorTag()); + + test_includes_edge_cases_sender( + hpx::launch::async, par(task), IteratorTag()); + test_includes_edge_cases_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + includes_sender_test1(); + includes_sender_test1(); + + includes_sender_test2(); + includes_sender_test2(); + + includes_sender_test_edge_cases(); + includes_sender_test_edge_cases(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/is_heap_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/is_heap_sender.cpp new file mode 100644 index 000000000000..fe0c81925d00 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/is_heap_sender.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "is_heap_tests.hpp" + +template +void is_heap_sender_test() +{ + using namespace hpx::execution; + test_is_heap_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_is_heap_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_is_heap_sender(hpx::launch::async, par(task), IteratorTag()); + test_is_heap_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + is_heap_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/is_heap_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/is_heap_tests.hpp index 853bdd3f4219..5dba32d5834b 100644 --- a/libs/core/algorithms/tests/unit/algorithms/is_heap_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/is_heap_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2017 Taeguk Kwon // Copyright (c) 2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -140,6 +143,137 @@ void test_is_heap( } } +#if defined(HPX_HAVE_STDEXEC) +template +void test_is_heap_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), std::size_t(gen())); + + auto heap_end_iter = std::next(std::begin(c), dis(gen)); + std::make_heap(std::begin(c), heap_end_iter); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + hpx::is_heap(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool solution = std::is_heap(std::begin(c), std::end(c)); + + HPX_TEST_EQ(result, solution); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::is_heap(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool solution = std::is_heap(std::begin(c), std::begin(c)); + + HPX_TEST(result); + HPX_TEST_EQ(result, solution); + } + + { + // edge case: range with only one element + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(++std::begin(c))) | + hpx::is_heap(ex_policy.on(exec))); + + bool result = hpx::get<0>(*snd_result); + + bool solution = std::is_heap(std::begin(c), ++std::begin(c)); + + HPX_TEST(result); + HPX_TEST_EQ(result, solution); + } +} + +template +void test_is_heap_until_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), std::size_t(gen())); + + auto heap_end_iter = std::next(std::begin(c), dis(gen)); + std::make_heap(std::begin(c), heap_end_iter); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + hpx::is_heap_until(ex_policy.on(exec))); + + iterator result = hpx::get<0>(*snd_result); + + auto solution = std::is_heap_until(std::begin(c), std::end(c)); + + HPX_TEST(result.base() == solution); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::is_heap_until(ex_policy.on(exec))); + + iterator result = hpx::get<0>(*snd_result); + + auto solution = std::is_heap_until(std::begin(c), std::begin(c)); + + HPX_TEST(result.base() == std::begin(c)); + HPX_TEST(result.base() == solution); + } + + { + // edge case: range of length 1 + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(++std::begin(c))) | + hpx::is_heap_until(ex_policy.on(exec))); + + iterator result = hpx::get<0>(*snd_result); + + auto solution = std::is_heap_until(std::begin(c), ++std::begin(c)); + + HPX_TEST(result.base() == ++std::begin(c)); + HPX_TEST(result.base() == solution); + } +} +#endif + template void test_is_heap_with_pred( IteratorTag, DataType, Pred pred, bool test_for_is_heap = true) diff --git a/libs/core/algorithms/tests/unit/algorithms/is_heap_until_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/is_heap_until_sender.cpp new file mode 100644 index 000000000000..b36243d05bdc --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/is_heap_until_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "is_heap_tests.hpp" + +template +void is_heap_until_sender_test() +{ + using namespace hpx::execution; + test_is_heap_until_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_is_heap_until_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_is_heap_until_sender(hpx::launch::async, par(task), IteratorTag()); + test_is_heap_until_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + is_heap_until_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/is_partitioned_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/is_partitioned_sender.cpp new file mode 100644 index 000000000000..2deafe196fa1 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/is_partitioned_sender.cpp @@ -0,0 +1,134 @@ +// Copyright (c) 2015 Daniel Bourgeois +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////////// +int seed = std::random_device{}(); +std::mt19937 gen(seed); +std::uniform_int_distribution<> dis(0, 99); + +template +void test_is_partitioned_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + //fill first half of array with even numbers and second half + //with odd numbers + std::fill(std::begin(c), std::begin(c) + c.size() / 2, 2 * (dis(gen))); + std::fill(std::begin(c) + c.size() / 2, std::end(c), 2 * (dis(gen)) + 1); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + [](std::size_t n) { return n % 2 == 0; }) | + hpx::is_partitioned(ex_policy.on(exec))); + + bool parted = hpx::get<0>(*snd_result); + + HPX_TEST(parted); + } + + { + // edge case: empty range + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + [](std::size_t) { return true; }) | + hpx::is_partitioned(ex_policy.on(exec))); + + auto parted = hpx::get<0>(*snd_result); + + HPX_TEST(parted); + } + + { + // edge case: range of length 1 + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(++std::begin(c)), + [](std::size_t) { return true; }) | + hpx::is_partitioned(ex_policy.on(exec))); + + auto parted = hpx::get<0>(*snd_result); + + HPX_TEST(parted); + } +} + +template +void is_partitioned_sender_test() +{ + using namespace hpx::execution; + test_is_partitioned_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_is_partitioned_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_is_partitioned_sender(hpx::launch::async, par(task), IteratorTag()); + test_is_partitioned_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + is_partitioned_sender_test(); + is_partitioned_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/is_sorted_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/is_sorted_sender.cpp new file mode 100644 index 000000000000..2d10b4033666 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/is_sorted_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "is_sorted_tests.hpp" + +template +void is_sorted_sender_test() +{ + using namespace hpx::execution; + test_is_sorted_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_is_sorted_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_is_sorted_sender(hpx::launch::async, par(task), IteratorTag()); + test_is_sorted_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + is_sorted_sender_test(); + is_sorted_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/is_sorted_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/is_sorted_tests.hpp index 605e7b032216..98c46a6047ef 100644 --- a/libs/core/algorithms/tests/unit/algorithms/is_sorted_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/is_sorted_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2015 Daniel Bourgeois +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,7 @@ #pragma once +#include #include #include #include @@ -420,3 +422,58 @@ void test_sorted_bad_alloc_seq(IteratorTag) HPX_TEST(caught_bad_alloc); } + +#if defined(HPX_HAVE_STDEXEC) +template +void test_is_sorted_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + //Fill with sorted values from 0 to 10006 + std::iota(std::begin(c), std::end(c), 0); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + hpx::is_sorted(ex_policy.on(exec))); + + bool is_ordered = hpx::get<0>(*snd_result); + + HPX_TEST(is_ordered); + } + + { + // edge case: empty range + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::is_sorted(ex_policy.on(exec))); + + bool is_ordered = hpx::get<0>(*snd_result); + + HPX_TEST(is_ordered); + } + + { + // edge case: range of size 1 + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(++std::begin(c))) | + hpx::is_sorted(ex_policy.on(exec))); + + bool is_ordered = hpx::get<0>(*snd_result); + + HPX_TEST(is_ordered); + } +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/is_sorted_until_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/is_sorted_until_sender.cpp new file mode 100644 index 000000000000..1b8110561a50 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/is_sorted_until_sender.cpp @@ -0,0 +1,129 @@ +// Copyright (c) 2015 Daniel Bourgeois +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////////// +int seed = std::random_device{}(); +std::mt19937 gen(seed); +std::uniform_int_distribution<> dis(0, 99); + +template +void test_is_sorted_until_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), 0); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + hpx::is_sorted_until(ex_policy.on(exec))); + + iterator until = hpx::get<0>(*snd_result); + + HPX_TEST(until == iterator(std::end(c))); + } + + { + // edge case: empty range + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::is_sorted_until(ex_policy.on(exec))); + + iterator until = hpx::get<0>(*snd_result); + + HPX_TEST(until == iterator(std::begin(c))); + } + + { + // edge case: range of size 1 + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(++std::begin(c))) | + hpx::is_sorted_until(ex_policy.on(exec))); + + iterator until = hpx::get<0>(*snd_result); + + HPX_TEST(until == iterator(++std::begin(c))); + } +} + +template +void is_sorted_until_sender_test() +{ + using namespace hpx::execution; + test_is_sorted_until_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_is_sorted_until_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_is_sorted_until_sender(hpx::launch::async, par(task), IteratorTag()); + test_is_sorted_until_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + is_sorted_until_sender_test(); + is_sorted_until_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/lexicographical_compare_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/lexicographical_compare_sender.cpp new file mode 100644 index 000000000000..41ae80004357 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/lexicographical_compare_sender.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////// +int seed = std::random_device{}(); +std::mt19937 gen(seed); + +template +void test_lexicographical_compare_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), 0); + + //d is lexicographical less than c + std::vector d(10006); + std::iota(std::begin(d), std::end(d), 0); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d), std::end(d)) | + hpx::lexicographical_compare(ex_policy.on(exec))); + + bool res = hpx::get<0>(*snd_result); + + HPX_TEST(!res); + } + + { + // edge case: only first range is empty + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + std::begin(d), std::end(d)) | + hpx::lexicographical_compare(ex_policy.on(exec))); + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(result); + } + + { + // edge case: only second range is empty + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d), std::begin(d)) | + hpx::lexicographical_compare(ex_policy.on(exec))); + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(!result); + } + + { + // edge case: both ranges are empty + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + std::begin(d), std::begin(d)) | + hpx::lexicographical_compare(ex_policy.on(exec))); + bool result = hpx::get<0>(*snd_result); + + HPX_TEST(!result); + } +} + +template +void lexicographical_compare_sender_test() +{ + using namespace hpx::execution; + test_lexicographical_compare_sender( + hpx::launch::sync, seq(task), IteratorTag()); + test_lexicographical_compare_sender( + hpx::launch::sync, unseq(task), IteratorTag()); + + test_lexicographical_compare_sender( + hpx::launch::async, par(task), IteratorTag()); + test_lexicographical_compare_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + lexicographical_compare_sender_test(); + lexicographical_compare_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/max_element_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/max_element_sender.cpp new file mode 100644 index 000000000000..6d438d61e926 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/max_element_sender.cpp @@ -0,0 +1,132 @@ +// Copyright (c) 2014 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +/////////////////////////////////////////////////////////////////////////////// + +template +void test_max_element_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c = test::random_iota(10007); + + iterator end(std::end(c)); + base_iterator ref_end(std::end(c)); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(end), + std::less()) | + hpx::max_element(ex_policy.on(exec))); + iterator result = hpx::get<0>(*snd_result); + + HPX_TEST(result != end); + + base_iterator ref = std::max_element( + std::begin(c), std::end(c), std::less()); + HPX_TEST(ref != ref_end); + HPX_TEST_EQ(*ref, *result); + } + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + hpx::max_element(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result != end); + + auto ref = std::max_element(std::begin(c), std::end(c)); + HPX_TEST(ref != ref_end); + HPX_TEST_EQ(*ref, *result); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::max_element(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result == iterator(std::begin(c))); + } +} + +template +void max_element_sender_test() +{ + using namespace hpx::execution; + test_max_element_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_max_element_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_max_element_sender(hpx::launch::async, par(task), IteratorTag()); + test_max_element_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + max_element_sender_test(); + max_element_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/merge_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/merge_tests.hpp index e61d22440b5e..d768a9f1444e 100644 --- a/libs/core/algorithms/tests/unit/algorithms/merge_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/merge_tests.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/libs/core/algorithms/tests/unit/algorithms/min_element_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/min_element_sender.cpp new file mode 100644 index 000000000000..313c6fd8fb1d --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/min_element_sender.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2014 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +/////////////////////////////////////////////////////////////////////////////// + +template +void test_min_element_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c = test::random_iota(10007); + + iterator end(std::end(c)); + base_iterator ref_end(std::end(c)); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(end), + std::less()) | + hpx::min_element(ex_policy.on(exec))); + iterator result = hpx::get<0>(*snd_result); + + HPX_TEST(result != end); + + base_iterator ref = std::min_element( + std::begin(c), std::end(c), std::less()); + HPX_TEST(ref != ref_end); + HPX_TEST_EQ(*ref, *result); + } + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + hpx::min_element(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result != end); + + auto ref = std::min_element(std::begin(c), std::end(c)); + HPX_TEST(ref != ref_end); + HPX_TEST_EQ(*ref, *result); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::min_element(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result == iterator(std::begin(c))); + } +} + +template +void min_element_sender_test() +{ + using namespace hpx::execution; + test_min_element_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_min_element_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_min_element_sender(hpx::launch::async, par(task), IteratorTag()); + test_min_element_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + min_element_sender_test(); + min_element_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/minmax_element_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/minmax_element_sender.cpp new file mode 100644 index 000000000000..8a82a3eacbaf --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/minmax_element_sender.cpp @@ -0,0 +1,140 @@ +// Copyright (c) 2014-2016 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +/////////////////////////////////////////////////////////////////////////////// + +template +void test_minmax_element_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c = test::random_iota(10007); + + iterator end(std::end(c)); + base_iterator ref_end(std::end(c)); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(end), + std::less()) | + hpx::minmax_element(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result.min != end && result.max != end); + + auto ref = std::minmax_element( + std::begin(c), std::end(c), std::less()); + HPX_TEST(ref.first != ref_end && ref.second != ref_end); + + HPX_TEST_EQ(*ref.first, *result.min); + HPX_TEST_EQ(*ref.second, *result.max); + } + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c))) | + hpx::minmax_element(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result.min != end && result.max != end); + + auto ref = std::minmax_element(std::begin(c), std::end(c)); + HPX_TEST(ref.first != ref_end && ref.second != ref_end); + + HPX_TEST_EQ(*ref.first, *result.min); + HPX_TEST_EQ(*ref.second, *result.max); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c))) | + hpx::minmax_element(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result.min == iterator(std::begin(c)) && + result.max == iterator(std::begin(c))); + } +} + +template +void minmax_element_sender_test() +{ + using namespace hpx::execution; + test_minmax_element_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_minmax_element_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_minmax_element_sender(hpx::launch::async, par(task), IteratorTag()); + test_minmax_element_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + minmax_element_sender_test(); + minmax_element_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/mismatch_binary_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/mismatch_binary_sender.cpp new file mode 100644 index 000000000000..947f46cbebb5 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/mismatch_binary_sender.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "mismatch_binary_tests.hpp" + +template +void mismatch_binary_sender_test() +{ + using namespace hpx::execution; + + test_mismatch_binary1_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_mismatch_binary1_sender(hpx::launch::sync, unseq(task), IteratorTag()); + test_mismatch_binary1_sender(hpx::launch::async, par(task), IteratorTag()); + test_mismatch_binary1_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); + + test_mismatch_binary2_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_mismatch_binary2_sender(hpx::launch::sync, unseq(task), IteratorTag()); + test_mismatch_binary2_sender(hpx::launch::async, par(task), IteratorTag()); + test_mismatch_binary2_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + mismatch_binary_sender_test(); + mismatch_binary_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/mismatch_binary_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/mismatch_binary_tests.hpp index 011163375bb2..10e5fd4b99a7 100644 --- a/libs/core/algorithms/tests/unit/algorithms/mismatch_binary_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/mismatch_binary_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include @@ -477,3 +480,131 @@ void test_mismatch_binary_bad_alloc_async(ExPolicy&& p, IteratorTag) HPX_TEST(caught_bad_alloc); HPX_TEST(returned_from_algorithm); } + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(HPX_HAVE_STDEXEC) +template +void test_mismatch_binary1_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c1(10007); + std::vector c2(c1.size()); + + int first_value = gen(); //-V101 + std::iota(std::begin(c1), std::end(c1), first_value); + std::iota(std::begin(c2), std::end(c2), first_value); + + iterator begin1 = iterator(std::begin(c1)); + iterator end1 = iterator(std::end(c1)); + + { + auto snd_result = + tt::sync_wait(ex::just(begin1, end1, std::begin(c2), std::end(c2)) | + hpx::mismatch(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + // verify values + HPX_TEST_EQ( + std::size_t(std::distance(begin1, result.first)), c1.size()); + HPX_TEST_EQ(std::size_t(std::distance(std::begin(c2), result.second)), + c2.size()); + } + + { + std::size_t changed_idx = dis(gen); //-V104 + ++c1[changed_idx]; + + auto snd_result = + tt::sync_wait(ex::just(begin1, end1, std::begin(c2), std::end(c2)) | + hpx::mismatch(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + // verify values + HPX_TEST_EQ( + std::size_t(std::distance(begin1, result.first)), changed_idx); + HPX_TEST_EQ(std::size_t(std::distance(std::begin(c2), result.second)), + changed_idx); + } +} + +template +void test_mismatch_binary2_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c1(10007); + std::vector c2(c1.size()); + + int first_value = gen(); //-V101 + std::iota(std::begin(c1), std::end(c1), first_value); + std::iota(std::begin(c2), std::end(c2), first_value); + + iterator begin1 = iterator(std::begin(c1)); + iterator end1 = iterator(std::end(c1)); + + { + auto snd_result = tt::sync_wait(ex::just(begin1, end1, std::begin(c2), + std::end(c2), std::equal_to<>()) | + hpx::mismatch(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + // verify values + HPX_TEST_EQ( + std::size_t(std::distance(begin1, result.first)), c1.size()); + HPX_TEST_EQ(std::size_t(std::distance(std::begin(c2), result.second)), + c2.size()); + } + + { + std::size_t changed_idx = dis(gen); //-V104 + ++c1[changed_idx]; + + auto snd_result = tt::sync_wait(ex::just(begin1, end1, std::begin(c2), + std::end(c2), std::equal_to<>()) | + hpx::mismatch(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + // verify values + HPX_TEST_EQ( + std::size_t(std::distance(begin1, result.first)), changed_idx); + HPX_TEST_EQ(std::size_t(std::distance(std::begin(c2), result.second)), + changed_idx); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c1)), iterator(std::begin(c1)), + std::begin(c2), std::end(c2), std::equal_to<>()) | + hpx::mismatch(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + // verify values + HPX_TEST(result.first.base() == std::begin(c1)); + HPX_TEST(result.second == std::begin(c2)); + } +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/mismatch_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/mismatch_sender.cpp new file mode 100644 index 000000000000..4dc9cf77ffd8 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/mismatch_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "mismatch_tests.hpp" + +template +void mismatch_sender_test() +{ + using namespace hpx::execution; + test_mismatch_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_mismatch_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_mismatch_sender(hpx::launch::async, par(task), IteratorTag()); + test_mismatch_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + mismatch_sender_test(); + mismatch_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/mismatch_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/mismatch_tests.hpp index 8171585a25d1..d8c342a2b613 100644 --- a/libs/core/algorithms/tests/unit/algorithms/mismatch_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/mismatch_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include @@ -475,3 +478,78 @@ void test_mismatch_bad_alloc_async(ExPolicy&& p, IteratorTag) HPX_TEST(caught_bad_alloc); HPX_TEST(returned_from_algorithm); } + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(HPX_HAVE_STDEXEC) +template +void test_mismatch_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + + using scheduler_t = ex::thread_pool_policy_scheduler; + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c1(10007); + std::vector c2(c1.size()); + + int first_value = gen(); //-V101 + std::iota(std::begin(c1), std::end(c1), first_value); + std::iota(std::begin(c2), std::end(c2), first_value); + + iterator begin1 = iterator(std::begin(c1)); + iterator end1 = iterator(std::end(c1)); + + { + auto snd_result = tt::sync_wait(ex::just(begin1, end1, std::begin(c2)) | + hpx::mismatch(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + // verify values + HPX_TEST_EQ( + static_cast(std::distance(begin1, result.first)), + c1.size()); + HPX_TEST_EQ(static_cast( + std::distance(std::begin(c2), result.second)), + c2.size()); + } + + { + std::size_t changed_idx = dis(gen); //-V104 + ++c1[changed_idx]; + + auto snd_result = tt::sync_wait(ex::just(begin1, end1, std::begin(c2)) | + hpx::mismatch(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + // verify values + HPX_TEST_EQ( + static_cast(std::distance(begin1, result.first)), + changed_idx); + HPX_TEST_EQ(static_cast( + std::distance(std::begin(c2), result.second)), + changed_idx); + } + + { + // edge case: empty range + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c1)), + iterator(std::begin(c1)), std::begin(c2)) | + hpx::mismatch(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + // verify values + HPX_TEST(result.first.base() == std::begin(c1)); + HPX_TEST(result.second == std::begin(c2)); + } +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/move_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/move_sender.cpp new file mode 100644 index 000000000000..b7bd0a9e086a --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/move_sender.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////// +int seed = std::random_device{}(); +std::mt19937 gen(seed); + +template +void test_move_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), gen()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d)) | + hpx::move(ex_policy.on(exec))); + + //copy contents of d back into c for testing + std::copy(std::begin(d), std::end(d), std::begin(d)); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} + +template +void move_sender_test() +{ + using namespace hpx::execution; + test_move_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_move_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_move_sender(hpx::launch::async, par(task), IteratorTag()); + test_move_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + move_sender_test(); + move_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/none_of_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/none_of_sender.cpp new file mode 100644 index 000000000000..6e54a1c19fa6 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/none_of_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "none_of_tests.hpp" + +template +void none_of_sender_test() +{ + using namespace hpx::execution; + test_none_of_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_none_of_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_none_of_sender(hpx::launch::async, par(task), IteratorTag()); + test_none_of_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + none_of_sender_test(); + none_of_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/none_of_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/none_of_tests.hpp index 6dd8b6431446..8176df28cf5a 100644 --- a/libs/core/algorithms/tests/unit/algorithms/none_of_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/none_of_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,7 +7,9 @@ #pragma once +#include #include +#include #include #include #include @@ -67,6 +70,42 @@ void test_none_of(ExPolicy&& policy, IteratorTag) } } +#if defined(HPX_HAVE_STDEXEC) +template +void test_none_of_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + int iseq[] = {0, 1, 3}; + for (int i : iseq) + { + std::vector c = test::fill_all_any_none(3, i); //-V106 + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + [](auto v) { return v != 0; }) | + hpx::none_of(ex_policy.on(exec))); + bool result = hpx::get<0>(*snd_result); + + // verify values + bool expected = std::none_of( + std::begin(c), std::end(c), [](auto v) { return v != 0; }); + + HPX_TEST_EQ(result, expected); + } +} +#endif + template void test_none_of_ranges_seq(IteratorTag, Proj proj = Proj()) { diff --git a/libs/core/algorithms/tests/unit/algorithms/reduce_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/reduce_sender.cpp new file mode 100644 index 000000000000..51aa6f76fdce --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/reduce_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "reduce_tests.hpp" + +template +void reduce_sender_test() +{ + using namespace hpx::execution; + test_reduce_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_reduce_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_reduce_sender(hpx::launch::async, par(task), IteratorTag()); + test_reduce_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + reduce_sender_test(); + reduce_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/reduce_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/reduce_tests.hpp index cf346eb7bfef..d1c7c3b60bb1 100644 --- a/libs/core/algorithms/tests/unit/algorithms/reduce_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/reduce_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include @@ -339,3 +342,49 @@ void test_reduce_bad_alloc_async(ExPolicy p, IteratorTag) HPX_TEST(caught_exception); HPX_TEST(returned_from_algorithm); } + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(HPX_HAVE_STDEXEC) +template +void test_reduce_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), gen()); + + int val(42); + auto op = [](auto v1, auto v2) { return v1 * v2; }; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), val, op) | + hpx::reduce(ex_policy.on(exec))); + int result = hpx::get<0>(*snd_result); + + // verify values + int expected = std::accumulate(std::begin(c), std::end(c), val, op); + HPX_TEST_EQ(result, expected); + } + + { + auto snd_result = tt::sync_wait(ex::just(iterator(std::begin(c)), + iterator(std::begin(c)), val, op) | + hpx::reduce(ex_policy.on(exec))); + int result = hpx::get<0>(*snd_result); + + HPX_TEST_EQ(result, val); + } +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/remove_if_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/remove_if_sender.cpp new file mode 100644 index 000000000000..1a72765bfcfe --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/remove_if_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "remove_tests.hpp" + +template +void remove_if_sender_test() +{ + using namespace hpx::execution; + test_remove_if_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_remove_if_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_remove_if_sender(hpx::launch::async, par(task), IteratorTag()); + test_remove_if_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + remove_if_sender_test(); + remove_if_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/remove_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/remove_sender.cpp new file mode 100644 index 000000000000..35848610024f --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/remove_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "remove_tests.hpp" + +template +void remove_sender_test() +{ + using namespace hpx::execution; + test_remove_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_remove_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_remove_sender(hpx::launch::async, par(task), IteratorTag()); + test_remove_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + remove_sender_test(); + remove_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/remove_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/remove_tests.hpp index 15b6ad82e474..a4d8e9dcb2a5 100644 --- a/libs/core/algorithms/tests/unit/algorithms/remove_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/remove_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2017-2018 Taeguk Kwon +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include #include @@ -688,3 +691,81 @@ void test_remove_bad_alloc(bool test_for_remove_if = false) test_remove_bad_alloc_async(seq(task), IteratorTag(), test_for_remove_if); test_remove_bad_alloc_async(par(task), IteratorTag(), test_for_remove_if); } + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(HPX_HAVE_STDEXEC) +template +void test_remove_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + int rand_base = g(); + int value = rand_base + 2; + + std::size_t const size = 10007; + std::vector c(size), d; + std::generate(std::begin(c), std::end(c), random_fill(rand_base, 6)); + d = c; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), value) | + hpx::remove(ex_policy.on(exec))); + + auto result = hpx::get<0>(*snd_result); + + auto solution = std::remove(std::begin(d), std::end(d), value); + + bool equality = + test::equal(std::begin(c), result.base(), std::begin(d), solution); + + HPX_TEST(equality); +} + +template +void test_remove_if_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + int rand_base = g(); + auto pred = [rand_base](const int a) -> bool { return a == rand_base; }; + + std::size_t const size = 10007; + std::vector c(size), d; + std::generate(std::begin(c), std::end(c), random_fill(rand_base, 6)); + d = c; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), pred) | + hpx::remove_if(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + auto solution = std::remove_if(std::begin(d), std::end(d), pred); + + bool equality = + test::equal(std::begin(c), result.base(), std::begin(d), solution); + + HPX_TEST(equality); +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/replace_copy_if_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/replace_copy_if_sender.cpp new file mode 100644 index 000000000000..10df61881dcd --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/replace_copy_if_sender.cpp @@ -0,0 +1,129 @@ +// Copyright (c) 2014-2017 Hartmut Kaiser +// Copyright (c) 2021 Giannis Gonidelis +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////// +struct equal_f +{ + equal_f(std::size_t val) + : val_(val) + { + } + + bool operator()(std::size_t lhs) const + { + return lhs == val_; + } + + std::size_t val_; +}; + +template +void test_replace_copy_if_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d1(c.size()); + std::vector d2(c.size()); //-V656 + + std::iota(std::begin(c), std::end(c), std::rand()); + + std::size_t idx = std::rand() % c.size(); //-V104 + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d1), equal_f(c[idx]), c[idx] + 1) | + hpx::replace_copy_if(ex_policy.on(exec))); + + std::replace_copy_if(std::begin(c), std::end(c), std::begin(d2), + equal_f(c[idx]), c[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d1.size()); +} + +template +void replace_copy_if_sender_test() +{ + using namespace hpx::execution; + test_replace_copy_if_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_replace_copy_if_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_replace_copy_if_sender(hpx::launch::async, par(task), IteratorTag()); + test_replace_copy_if_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + replace_copy_if_sender_test(); + replace_copy_if_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/replace_copy_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/replace_copy_sender.cpp new file mode 100644 index 000000000000..a46f960d8885 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/replace_copy_sender.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2014-2017 Hartmut Kaiser +// Copyright (c) 2021 Giannis Gonidelis +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +template +void test_replace_copy_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d1(c.size()); + std::vector d2(c.size()); //-V656 + + std::iota(std::begin(c), std::end(c), std::rand()); + + std::size_t idx = std::rand() % c.size(); //-V104 + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d1), c[idx], c[idx] + 1) | + hpx::replace_copy(ex_policy.on(exec))); + + std::replace_copy( + std::begin(c), std::end(c), std::begin(d2), c[idx], c[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d1.size()); +} + +template +void replace_copy_sender_test() +{ + using namespace hpx::execution; + test_replace_copy_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_replace_copy_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_replace_copy_sender(hpx::launch::async, par(task), IteratorTag()); + test_replace_copy_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + replace_copy_sender_test(); + replace_copy_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/replace_if.cpp b/libs/core/algorithms/tests/unit/algorithms/replace_if.cpp index 0799cb82814a..e18e2818fa57 100644 --- a/libs/core/algorithms/tests/unit/algorithms/replace_if.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/replace_if.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2014-2017 Hartmut Kaiser -// Copyright (c) 2921 Giannis Gonidelis +// Copyright (c) 2021 Giannis Gonidelis // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying diff --git a/libs/core/algorithms/tests/unit/algorithms/replace_if_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/replace_if_sender.cpp new file mode 100644 index 000000000000..f5dd2b234433 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/replace_if_sender.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2014-2017 Hartmut Kaiser +// Copyright (c) 2021 Giannis Gonidelis +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////// +struct equal_f +{ + equal_f(std::size_t val) + : val_(val) + { + } + + bool operator()(std::size_t lhs) const + { + return lhs == val_; + } + + std::size_t val_; +}; + +template +void test_replace_if_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + std::copy(std::begin(c), std::end(c), std::begin(d)); + + std::size_t idx = std::rand() % c.size(); //-V104 + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + equal_f(c[idx]), c[idx] + 1) | + hpx::replace_if(ex_policy.on(exec))); + + std::replace_if(std::begin(d), std::end(d), equal_f(d[idx]), d[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} + +template +void replace_if_sender_test() +{ + using namespace hpx::execution; + test_replace_if_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_replace_if_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_replace_if_sender(hpx::launch::async, par(task), IteratorTag()); + test_replace_if_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + replace_if_sender_test(); + replace_if_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/replace_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/replace_sender.cpp new file mode 100644 index 000000000000..d29956744d72 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/replace_sender.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2014-2017 Hartmut Kaiser +// Copyright (c) 2021 Giannis Gonidelis +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +template +void test_replace_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + std::copy(std::begin(c), std::end(c), std::begin(d)); + + std::size_t idx = std::rand() % c.size(); //-V104 + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + c[idx], c[idx] + 1) | + hpx::replace(ex_policy.on(exec))); + + std::replace(std::begin(d), std::end(d), d[idx], d[idx] + 1); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} + +template +void replace_sender_test() +{ + using namespace hpx::execution; + test_replace_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_replace_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_replace_sender(hpx::launch::async, par(task), IteratorTag()); + test_replace_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + replace_sender_test(); + replace_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/reverse_copy_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/reverse_copy_sender.cpp new file mode 100644 index 000000000000..4ff131840484 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/reverse_copy_sender.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2007-2017 Hartmut Kaiser +// Copyright (c) 2021 Giannis Gonidelis +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +template +void test_reverse_copy_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d1(c.size()); + std::vector d2(c.size()); //-V656 + + std::iota(std::begin(c), std::end(c), std::rand()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d1)) | + hpx::reverse_copy(ex_policy.on(exec))); + + std::reverse_copy(std::begin(c), std::end(c), std::begin(d2)); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d1.size()); +} + +template +void reverse_copy_sender_test() +{ + using namespace hpx::execution; + test_reverse_copy_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_reverse_copy_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_reverse_copy_sender(hpx::launch::async, par(task), IteratorTag()); + test_reverse_copy_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + reverse_copy_sender_test(); + reverse_copy_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/reverse_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/reverse_sender.cpp index 153e7e7a4c90..e6f2eae95b8c 100644 --- a/libs/core/algorithms/tests/unit/algorithms/reverse_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/reverse_sender.cpp @@ -79,13 +79,9 @@ void test_reverse(Policy l, ExPolicy&& policy, IteratorTag) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c))) | hpx::reverse(policy.on(exec))); -#else - ex::just(iterator(std::begin(c)), iterator(std::end(c))) | - hpx::reverse(policy.on(exec)) | tt::sync_wait(); -#endif std::reverse(std::begin(d1), std::end(d1)); @@ -117,13 +113,9 @@ void test_reverse_async_direct(Policy l, ExPolicy&& p, IteratorTag) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) + tt::sync_wait(hpx::reverse( p.on(exec), iterator(std::begin(c)), iterator(std::end(c)))); -#else - hpx::reverse(p.on(exec), iterator(std::begin(c)), iterator(std::end(c))) | - tt::sync_wait(); -#endif std::reverse(std::begin(d1), std::end(d1)); diff --git a/libs/core/algorithms/tests/unit/algorithms/rotate_copy_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/rotate_copy_sender.cpp new file mode 100644 index 000000000000..0d57a6bb1e6a --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/rotate_copy_sender.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2007-2014 Hartmut Kaiser +// Copyright (c) 2021 Chuanqiu He +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +template +void test_rotate_copy_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d1(c.size()); + std::vector d2(c.size()); //-V656 + + std::iota(std::begin(c), std::end(c), std::rand()); + + base_iterator mid = std::begin(c); + std::advance(mid, std::rand() % c.size()); //-V104 + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(mid), + iterator(std::end(c)), std::begin(d1)) | + hpx::rotate_copy(ex_policy.on(exec))); + + std::rotate_copy(std::begin(c), mid, std::end(c), std::begin(d2)); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d1.size()); +} + +template +void rotate_copy_sender_test() +{ + using namespace hpx::execution; + test_rotate_copy_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_rotate_copy_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_rotate_copy_sender(hpx::launch::async, par(task), IteratorTag()); + test_rotate_copy_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + rotate_copy_sender_test(); + rotate_copy_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/rotate_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/rotate_sender.cpp index a14dc2135c83..72ab77d0fde9 100644 --- a/libs/core/algorithms/tests/unit/algorithms/rotate_sender.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/rotate_sender.cpp @@ -95,14 +95,10 @@ void test_rotate(Policy l, ExPolicy&& policy, IteratorTag) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(mid), iterator(std::end(c))) | hpx::rotate(policy.on(exec))); -#else - ex::just(iterator(std::begin(c)), iterator(mid), iterator(std::end(c))) | - hpx::rotate(policy.on(exec)) | tt::sync_wait(); -#endif base_iterator mid1 = std::begin(d1); std::advance(mid1, mid_pos); @@ -141,14 +137,9 @@ void test_rotate_async_direct(Policy l, ExPolicy&& p, IteratorTag) using scheduler_t = ex::thread_pool_policy_scheduler; auto exec = ex::explicit_scheduler_executor(scheduler_t(l)); -#if defined(HPX_HAVE_STDEXEC) + tt::sync_wait(hpx::rotate(p.on(exec), iterator(std::begin(c)), iterator(mid), iterator(std::end(c)))); -#else - hpx::rotate(p.on(exec), iterator(std::begin(c)), iterator(mid), - iterator(std::end(c))) | - tt::sync_wait(); -#endif base_iterator mid1 = std::begin(d1); std::advance(mid1, mid_pos); diff --git a/libs/core/algorithms/tests/unit/algorithms/search_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/search_sender.cpp new file mode 100644 index 000000000000..3c1f31b2919e --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/search_sender.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +template +void test_search_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c(10007); + // fill vector with random values above 2 + std::fill(std::begin(c), std::end(c), (std::rand() % 100) + 3); + // create subsequence in middle of vector + c[c.size() / 2] = 1; + c[c.size() / 2 + 1] = 2; + + std::vector h{1, 2}; + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(h), std::end(h)) | + hpx::search(ex_policy.on(exec))); + iterator index = hpx::get<0>(*snd_result); + + base_iterator test_index = std::begin(c) + c.size() / 2; + + HPX_TEST(index == iterator(test_index)); + } + + { + // edge case: only second range is empty + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(h), std::begin(h)) | + hpx::search(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + auto expected = std::search(iterator(std::begin(c)), + iterator(std::end(c)), std::begin(h), std::begin(h)); + + HPX_TEST(result.base() == std::begin(c)); + HPX_TEST(result == expected); + } + + { + // edge case: both ranges are empty + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), + std::begin(h), std::begin(h)) | + hpx::search(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + auto expected = std::search(iterator(std::begin(c)), + iterator(std::begin(c)), std::begin(h), std::begin(h)); + + HPX_TEST(result.base() == std::begin(c)); + HPX_TEST(result == expected); + } + + { + // edge case: second range is larger than the first range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(h)), iterator(std::end(h)), + std::begin(c), std::end(c)) | + hpx::search(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + auto expected = std::search(iterator(std::begin(h)), + iterator(std::end(h)), std::begin(c), std::end(c)); + + HPX_TEST(result.base() == std::end(h)); + HPX_TEST(result == expected); + } +} + +template +void search_sender_test() +{ + using namespace hpx::execution; + test_search_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_search_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_search_sender(hpx::launch::async, par(task), IteratorTag()); + test_search_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + search_sender_test(); + search_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/starts_with_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/starts_with_sender.cpp new file mode 100644 index 000000000000..0d4dad175b7b --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/starts_with_sender.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2018 Christopher Ogle +// Copyright (c) 2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +unsigned int seed = std::random_device{}(); +std::mt19937 gen(seed); + +template +void test_starts_with_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::uniform_int_distribution dis1(1, 10007); + auto end1 = dis1(gen); + std::uniform_int_distribution dis2(1, end1); + auto end2 = dis2(gen); + auto some_ints = std::vector(end1); + std::iota(some_ints.begin(), some_ints.end(), 1); + auto some_more_ints = std::vector(end2); + std::iota(some_more_ints.begin(), some_more_ints.end(), 1); + auto some_wrong_ints = std::vector(end2); + std::iota( + some_wrong_ints.begin(), some_wrong_ints.end(), std::rand() % end2 + 2); + + { + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(some_ints)), + iterator(std::end(some_ints)), + iterator(std::begin(some_more_ints)), + iterator(std::end(some_more_ints))) | + hpx::starts_with(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST_EQ(result, true); + } + + { + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(some_ints)), + iterator(std::end(some_ints)), + iterator(std::begin(some_wrong_ints)), + iterator(std::end(some_wrong_ints))) | + hpx::starts_with(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST_EQ(result, false); + } +} + +template +void starts_with_sender_test() +{ + using namespace hpx::execution; + test_starts_with_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_starts_with_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_starts_with_sender(hpx::launch::async, par(task), IteratorTag()); + test_starts_with_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + starts_with_sender_test(); + starts_with_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/swapranges_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/swapranges_sender.cpp new file mode 100644 index 000000000000..c886af750eea --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/swapranges_sender.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2014 Grant Mercer +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +template +void test_swap_ranges_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + std::fill(std::begin(d), std::end(d), std::rand()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d)) | + hpx::swap_ranges(ex_policy.on(exec))); + + //equal begins at one, therefore counter is started at 1 + std::size_t count = 1; + HPX_TEST(std::equal(std::begin(c) + 1, std::end(c), std::begin(c), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, c.size()); + + count = 1; + HPX_TEST(std::equal(std::begin(d) + 1, std::end(d), std::begin(d), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_NEQ(v1, v2); + ++count; + return !(v1 == v2); + })); + HPX_TEST_EQ(count, d.size()); +} + +template +void swap_ranges_sender_test() +{ + using namespace hpx::execution; + test_swap_ranges_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_swap_ranges_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_swap_ranges_sender(hpx::launch::async, par(task), IteratorTag()); + test_swap_ranges_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + swap_ranges_sender_test(); + swap_ranges_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/transform_binary2_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/transform_binary2_sender.cpp new file mode 100644 index 000000000000..1c4d1a41f577 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/transform_binary2_sender.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "transform_binary2_tests.hpp" + +template +void transform_binary2_sender_test() +{ + using namespace hpx::execution; + test_transform_binary2_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_transform_binary2_sender( + hpx::launch::sync, unseq(task), IteratorTag()); + + test_transform_binary2_sender(hpx::launch::async, par(task), IteratorTag()); + test_transform_binary2_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + transform_binary2_sender_test(); + transform_binary2_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/transform_binary2_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/transform_binary2_tests.hpp index 985291a3e0ed..56ab761f3e52 100644 --- a/libs/core/algorithms/tests/unit/algorithms/transform_binary2_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/transform_binary2_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2014-2016 Hartmut Kaiser // Copyright (c) 2021 Giannis Gonidelis +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -346,3 +349,56 @@ void test_transform_binary2_bad_alloc_async(ExPolicy p, IteratorTag) HPX_TEST(caught_bad_alloc); HPX_TEST(returned_from_algorithm); } + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(HPX_HAVE_STDEXEC) +template +void test_transform_binary2_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c1(10007); + std::vector c2(c1.size()); + std::vector d1(c1.size()); //-V656 + std::iota(std::begin(c1), std::end(c1), + std::rand() % ((std::numeric_limits::max)() / 2)); + std::iota(std::begin(c2), std::end(c2), + std::rand() % ((std::numeric_limits::max)() / 2)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::end(c2), std::begin(d1), add()) | + hpx::ranges::transform(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result.in1 == iterator(std::end(c1))); + HPX_TEST(result.in2 == std::end(c2)); + HPX_TEST(result.out == std::end(d1)); + + // verify values + std::vector d2(c1.size()); + std::transform( + std::begin(c1), std::end(c1), std::begin(c2), std::begin(d2), add()); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](int v1, int v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d2.size()); +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/transform_binary_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/transform_binary_sender.cpp new file mode 100644 index 000000000000..c82f48d5e9cb --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/transform_binary_sender.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "transform_binary_tests.hpp" + +template +void transform_binary_sender_test() +{ + using namespace hpx::execution; + test_transform_binary_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_transform_binary_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_transform_binary_sender(hpx::launch::async, par(task), IteratorTag()); + test_transform_binary_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + transform_binary_sender_test(); + transform_binary_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/transform_binary_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/transform_binary_tests.hpp index a05063e484cf..6911ace4a7ee 100644 --- a/libs/core/algorithms/tests/unit/algorithms/transform_binary_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/transform_binary_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2014-2016 Hartmut Kaiser // Copyright (c) 2021 Giannis Gonidelis +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -335,3 +338,54 @@ void test_transform_binary_bad_alloc_async(ExPolicy p, IteratorTag) HPX_TEST(caught_bad_alloc); HPX_TEST(returned_from_algorithm); } + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(HPX_HAVE_STDEXEC) +template +void test_transform_binary_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c1(10007); + std::vector c2(c1.size()); + std::vector d1(c1.size()); //-V656 + std::iota(std::begin(c1), std::end(c1), + std::rand() % ((std::numeric_limits::max)() / 2)); + std::iota(std::begin(c2), std::end(c2), + std::rand() % ((std::numeric_limits::max)() / 2)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c1)), iterator(std::end(c1)), + std::begin(c2), std::begin(d1), add()) | + hpx::transform(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result == std::end(d1)); + + // verify values + std::vector d2(c1.size()); + std::transform( + std::begin(c1), std::end(c1), std::begin(c2), std::begin(d2), add()); + + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(d1), std::end(d1), std::begin(d2), + [&count](int v1, int v2) -> bool { + HPX_TEST_EQ(v1, v2); + ++count; + return v1 == v2; + })); + HPX_TEST_EQ(count, d2.size()); +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/transform_reduce_binary_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/transform_reduce_binary_sender.cpp new file mode 100644 index 000000000000..3a8909aecce5 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/transform_reduce_binary_sender.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "transform_reduce_binary_tests.hpp" + +template +void transform_reduce_binary_sender_test() +{ + using namespace hpx::execution; + test_transform_reduce_binary_sender( + hpx::launch::sync, seq(task), IteratorTag()); + test_transform_reduce_binary_sender( + hpx::launch::sync, unseq(task), IteratorTag()); + + test_transform_reduce_binary_sender( + hpx::launch::async, par(task), IteratorTag()); + test_transform_reduce_binary_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + transform_reduce_binary_sender_test(); + transform_reduce_binary_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/transform_reduce_binary_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/transform_reduce_binary_tests.hpp index e22be56bfa8e..78dbcf757a5c 100644 --- a/libs/core/algorithms/tests/unit/algorithms/transform_reduce_binary_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/transform_reduce_binary_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,7 +7,9 @@ #pragma once +#include #include +#include #include #include #include @@ -80,3 +83,55 @@ void test_transform_reduce_binary_async(ExPolicy&& p, IteratorTag) HPX_TEST_EQ(fut_r.get(), std::inner_product(std::begin(c), std::end(c), std::begin(d), init)); } + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(HPX_HAVE_STDEXEC) +template +void test_transform_reduce_binary_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + std::vector c = test::random_iota(1007); + std::vector d = test::random_iota(1007); + int init = std::rand() % 1007; //-V101 + + { + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), + iterator(std::end(c)), std::begin(d), init) | + hpx::transform_reduce(ex_policy.on(exec))); + int result = hpx::get<0>(*snd_result); + + HPX_TEST_EQ(result, + std::inner_product( + std::begin(c), std::end(c), std::begin(d), init)); + } + + { + // edge case: empty range + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), + iterator(std::begin(c)), std::begin(d), init) | + hpx::transform_reduce(ex_policy.on(exec))); + int result = hpx::get<0>(*snd_result); + + HPX_TEST_EQ(init, result); + HPX_TEST_EQ(result, + std::inner_product( + std::begin(c), std::begin(c), std::begin(d), init)); + } +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/transform_reduce_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/transform_reduce_sender.cpp new file mode 100644 index 000000000000..01796a23a924 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/transform_reduce_sender.cpp @@ -0,0 +1,145 @@ +// Copyright (c) 2014-2020 Hartmut Kaiser +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +template +void test_transform_reduce_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::iota(std::begin(c), std::end(c), std::rand()); + + using result_type = hpx::tuple; + + using hpx::get; + using hpx::make_tuple; + + auto reduce_op = [](result_type v1, result_type v2) -> result_type { + return make_tuple(get<0>(v1) * get<0>(v2), get<1>(v1) * get<1>(v2)); + }; + + auto convert_op = [](std::size_t val) -> result_type { + return make_tuple(val, val); + }; + + result_type const init = make_tuple(std::size_t(1), std::size_t(1)); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), init, + reduce_op, convert_op) | + hpx::transform_reduce(ex_policy.on(exec))); + result_type result = hpx::get<0>(*snd_result); + + // verify values + result_type expected = std::accumulate(std::begin(c), std::end(c), init, + [&reduce_op, &convert_op](result_type res, std::size_t val) { + return reduce_op(res, convert_op(val)); + }); + + HPX_TEST_EQ(get<0>(result), get<0>(expected)); + HPX_TEST_EQ(get<1>(result), get<1>(expected)); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), init, + reduce_op, convert_op) | + hpx::transform_reduce(ex_policy.on(exec))); + result_type result = hpx::get<0>(*snd_result); + + // verify values + result_type expected = std::accumulate(std::begin(c), std::begin(c), + init, [&reduce_op, &convert_op](result_type res, std::size_t val) { + return reduce_op(res, convert_op(val)); + }); + + HPX_TEST(result == init); + HPX_TEST_EQ(get<0>(result), get<0>(expected)); + HPX_TEST_EQ(get<1>(result), get<1>(expected)); + } +} + +template +void transform_reduce_sender_test() +{ + using namespace hpx::execution; + test_transform_reduce_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_transform_reduce_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_transform_reduce_sender(hpx::launch::async, par(task), IteratorTag()); + test_transform_reduce_sender( + hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + transform_reduce_sender_test(); + transform_reduce_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/transform_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/transform_sender.cpp new file mode 100644 index 000000000000..7d3c12a37d13 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/transform_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "transform_tests.hpp" + +template +void transform_sender_test() +{ + using namespace hpx::execution; + test_transform_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_transform_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_transform_sender(hpx::launch::async, par(task), IteratorTag()); + test_transform_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + transform_sender_test(); + transform_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/transform_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/transform_tests.hpp index 3c0c3d43f8fc..d78c061dbadb 100644 --- a/libs/core/algorithms/tests/unit/algorithms/transform_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/transform_tests.hpp @@ -1,5 +1,6 @@ // Copyright (c) 2014-2016 Hartmut Kaiser // Copyright (c) 2021 Giannis Gonidelis +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -7,6 +8,8 @@ #pragma once +#include +#include #include #include @@ -298,3 +301,46 @@ void test_transform_bad_alloc_async(ExPolicy p, IteratorTag) HPX_TEST(caught_bad_alloc); HPX_TEST(returned_from_algorithm); } + +//////////////////////////////////////////////////////////////////////////////// + +#if defined(HPX_HAVE_STDEXEC) +template +void test_transform_sender( + LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + auto snd_result = + tt::sync_wait(ex::just(iterator(std::begin(c)), iterator(std::end(c)), + std::begin(d), add_one()) | + hpx::transform(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + HPX_TEST(result == std::end(d)); + + // verify values + std::size_t count = 0; + HPX_TEST(std::equal(std::begin(c), std::end(c), std::begin(d), + [&count](std::size_t v1, std::size_t v2) -> bool { + HPX_TEST_EQ(v1 + 1, v2); + ++count; + return v1 + 1 == v2; + })); + HPX_TEST_EQ(count, d.size()); +} +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/unique_sender.cpp b/libs/core/algorithms/tests/unit/algorithms/unique_sender.cpp new file mode 100644 index 000000000000..5d0d0464890b --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/unique_sender.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Tobias Wukovitsch +// +// SPDX-License-Identifier: BSL-1.0 +// 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) + +#include + +#include +#include +#include +#include + +#include "unique_tests.hpp" + +template +void unique_sender_test() +{ + using namespace hpx::execution; + test_unique_sender(hpx::launch::sync, seq(task), IteratorTag()); + test_unique_sender(hpx::launch::sync, unseq(task), IteratorTag()); + + test_unique_sender(hpx::launch::async, par(task), IteratorTag()); + test_unique_sender(hpx::launch::async, par_unseq(task), IteratorTag()); +} + +int hpx_main(hpx::program_options::variables_map& vm) +{ + unsigned int seed = (unsigned int) std::time(nullptr); + if (vm.count("seed")) + seed = vm["seed"].as(); + + std::cout << "using seed: " << seed << std::endl; + std::srand(seed); + + unique_sender_test(); + unique_sender_test(); + + return hpx::local::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace hpx::program_options; + options_description desc_commandline( + "Usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options()("seed,s", value(), + "the random number generator seed to use for this run"); + + // By default this test should run on all available cores + std::vector const cfg = {"hpx.os_threads=all"}; + + // Initialize and run HPX + hpx::local::init_params init_args; + init_args.desc_cmdline = desc_commandline; + init_args.cfg = cfg; + + HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv, init_args), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +} diff --git a/libs/core/algorithms/tests/unit/algorithms/unique_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/unique_tests.hpp index d1f82225a645..b8639a196093 100644 --- a/libs/core/algorithms/tests/unit/algorithms/unique_tests.hpp +++ b/libs/core/algorithms/tests/unit/algorithms/unique_tests.hpp @@ -1,4 +1,5 @@ // Copyright (c) 2017-2018 Taeguk Kwon +// Copyright (c) 2024 Tobias Wukovitsch // // SPDX-License-Identifier: BSL-1.0 // Distributed under the Boost Software License, Version 1.0. (See accompanying @@ -6,6 +7,8 @@ #pragma once +#include +#include #include #include #include @@ -501,3 +504,77 @@ void test_unique_bad_alloc() test_unique_bad_alloc_async(seq(task), IteratorTag()); test_unique_bad_alloc_async(par(task), IteratorTag()); } + +//////////////////////////////////////////////////////////////////////////////// +#if defined(HPX_HAVE_STDEXEC) +template +void test_unique_sender(LnPolicy ln_policy, ExPolicy&& ex_policy, IteratorTag) +{ + static_assert(hpx::is_async_execution_policy_v, + "hpx::is_async_execution_policy_v"); + + using base_iterator = std::vector::iterator; + using iterator = test::test_iterator; + + namespace ex = hpx::execution::experimental; + namespace tt = hpx::this_thread::experimental; + using scheduler_t = ex::thread_pool_policy_scheduler; + + auto exec = ex::explicit_scheduler_executor(scheduler_t(ln_policy)); + + const int rand_base = std::rand(); + auto pred = [](const std::size_t a, const std::size_t b) -> bool { + return a == b; + }; + std::size_t const size = 10007; + std::vector c(size), d; + std::generate(std::begin(c), std::end(c), random_fill(rand_base, 6)); + d = c; + + { + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::end(c)), pred) | + hpx::unique(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + auto solution = std::unique(std::begin(d), std::end(d), pred); + + bool equality = + test::equal(std::begin(c), result.base(), std::begin(d), solution); + + HPX_TEST(equality); + } + + { + // edge case: empty range + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(std::begin(c)), pred) | + hpx::unique(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + auto solution = std::unique(std::begin(d), std::begin(d), pred); + + bool equality = + test::equal(std::begin(c), result.base(), std::begin(d), solution); + + HPX_TEST(equality); + } + + { + // edge case: one element + + auto snd_result = tt::sync_wait( + ex::just(iterator(std::begin(c)), iterator(++std::begin(c)), pred) | + hpx::unique(ex_policy.on(exec))); + auto result = hpx::get<0>(*snd_result); + + auto solution = std::unique(std::begin(d), ++std::begin(d), pred); + + bool equality = + test::equal(std::begin(c), result.base(), std::begin(d), solution); + + HPX_TEST(equality); + } +} +#endif diff --git a/libs/core/execution_base/tests/unit/any_sender.cpp b/libs/core/execution_base/tests/unit/any_sender.cpp index 479f3988dbe4..50c6b1ea7e19 100644 --- a/libs/core/execution_base/tests/unit/any_sender.cpp +++ b/libs/core/execution_base/tests/unit/any_sender.cpp @@ -556,11 +556,12 @@ void test_unique_any_sender_set_error() // This tests that the empty vtable types used in the implementation of any_* // are not destroyed too early. We use ensure_started inside the function to // trigger the use of the empty vtables for any_receiver and -// any_operation_state. If the empty vtables are function-local statics they -// would get constructed after s_global is constructed, and thus destroyed -// before s_global is destroyed. This will typically lead to a segfault. If the -// empty vtables are (constant) global variables they should be constructed -// before s_global is constructed and destroyed after s_global is destroyed. +// any_operation_state. If the empty vtables are function-local +// static variables they would get constructed after s_global +// is constructed, and thus destroyed before s_global is destroyed. This will +// typically lead to a segfault. If the empty vtables are (constant) global +// variables they should be constructed before s_global is constructed and +// destroyed after s_global is destroyed. ex::unique_any_sender<> global_unique_any_sender{ex::just()}; ex::any_sender<> global_any_sender{ex::just()}; diff --git a/libs/core/executors/include/hpx/executors/explicit_scheduler_executor.hpp b/libs/core/executors/include/hpx/executors/explicit_scheduler_executor.hpp index c4c0ad8cebd7..0d4311ef27c7 100644 --- a/libs/core/executors/include/hpx/executors/explicit_scheduler_executor.hpp +++ b/libs/core/executors/include/hpx/executors/explicit_scheduler_executor.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include namespace hpx::execution::experimental { @@ -181,17 +183,11 @@ namespace hpx::execution::experimental { !std::is_integral_v )> // clang-format on - friend auto tag_invoke(hpx::parallel::execution::bulk_async_execute_t, + friend decltype(auto) tag_invoke( + hpx::parallel::execution::bulk_async_execute_t, explicit_scheduler_executor const& exec, F&& f, S const& shape, Ts&&... ts) { - using shape_element = - typename hpx::traits::range_traits::value_type; - using result_type = hpx::util::detail::invoke_deferred_result_t; - - static_assert( - std::is_void_v, "std::is_void_v"); #if defined(HPX_HAVE_STDEXEC) // We are using HPX's bulk implementation for now, so this works for // other types too. @@ -200,8 +196,58 @@ namespace hpx::execution::experimental { // "P2300 expects bulk to be called only with integral types" // ); #endif - return bulk(schedule(exec.sched_), shape, - hpx::bind_back(HPX_FORWARD(F, f), HPX_FORWARD(Ts, ts)...)); + + using shape_element = + typename hpx::traits::range_traits::value_type; + using result_type = hpx::util::detail::invoke_deferred_result_t; + + /* A boolean as result_type is disallowed because the elements of a + * vector cannot be modified concurrently. */ + static_assert(!std::is_same_v, + "Using an invocable that returns a boolean with " + "explicit_scheduler_executor::bulk_async_execution " + "can result in data races!"); + + if constexpr (std::is_void_v) + { + return bulk(schedule(exec.sched_), shape, + hpx::bind_back(HPX_FORWARD(F, f), HPX_FORWARD(Ts, ts)...)); + } + else + { + using size_type = decltype(util::size(shape)); + const size_type shape_size = util::size(shape); + + using result_vector_type = std::vector; + result_vector_type result_vector(shape_size); + + auto f_wrapper = [](const size_type i, + result_vector_type& result_vector, + const S& shape, F& f, Ts&... ts) { + auto it = util::begin(shape); + std::advance(it, i); + result_vector[i] = HPX_INVOKE(f, *it, ts...); + }; + + auto get_result = [](result_vector_type&& result_vector, + const S&, F&&, Ts&&...) { + return HPX_MOVE(result_vector); + }; + +#if defined(HPX_HAVE_STDEXEC) + return just(HPX_MOVE(result_vector), shape, HPX_FORWARD(F, f), + HPX_FORWARD(Ts, ts)...) | + continue_on(exec.sched_) | + bulk(shape_size, HPX_MOVE(f_wrapper)) | + then(HPX_MOVE(get_result)); +#else + return transfer_just(exec.sched_, HPX_MOVE(result_vector), + shape, HPX_FORWARD(F, f), HPX_FORWARD(Ts, ts)...) | + bulk(shape_size, HPX_MOVE(f_wrapper)) | + then(HPX_MOVE(get_result)); +#endif + } } // clang-format off