diff --git a/libs/core/algorithms/CMakeLists.txt b/libs/core/algorithms/CMakeLists.txt index 1e533bc20ee0..b66c51279160 100644 --- a/libs/core/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/CMakeLists.txt @@ -17,6 +17,7 @@ set(algorithms_headers hpx/parallel/algorithms/copy.hpp hpx/parallel/algorithms/count.hpp hpx/parallel/algorithms/destroy.hpp + hpx/parallel/algorithms/detail/adjacent_difference.hpp hpx/parallel/algorithms/detail/accumulate.hpp hpx/parallel/algorithms/detail/advance_and_get_distance.hpp hpx/parallel/algorithms/detail/advance_to_sentinel.hpp @@ -141,6 +142,7 @@ set(algorithms_headers hpx/parallel/container_numeric.hpp hpx/parallel/datapar.hpp hpx/parallel/datapar/fill.hpp + hpx/parallel/datapar/adjacent_difference.hpp hpx/parallel/datapar/iterator_helpers.hpp hpx/parallel/datapar/loop.hpp hpx/parallel/datapar/transfer.hpp 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 5b27d713c4c1..ccfffc0e723b 100644 --- a/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/adjacent_difference.hpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -47,7 +48,7 @@ namespace hpx { namespace parallel { inline namespace v1 { static OutIter sequential( ExPolicy, InIter first, InIter last, OutIter dest, Op&& op) { - return std::adjacent_difference( + return sequential_adjacent_difference( first, last, dest, std::forward(op)); } @@ -83,7 +84,7 @@ namespace hpx { namespace parallel { inline namespace v1 { // VS2015RC bails out when op is captured by ref using hpx::get; util::loop_n>( - part_begin, part_size, [op](zip_iterator it) { + part_begin, part_size, [op](auto&& it) { get<2>(*it) = hpx::util::invoke(op, get<0>(*it), get<1>(*it)); }); @@ -188,10 +189,9 @@ namespace hpx { namespace parallel { inline namespace v1 { adjacent_difference( ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest) { - typedef typename std::iterator_traits::value_type value_type; typedef hpx::traits::is_segmented_iterator is_segmented; return detail::adjacent_difference_(std::forward(policy), - first, last, dest, std::minus(), is_segmented()); + first, last, dest, std::minus<>(), is_segmented()); } //////////////////////////////////////////////////////////////////////////// diff --git a/libs/core/algorithms/include/hpx/parallel/algorithms/detail/adjacent_difference.hpp b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/adjacent_difference.hpp new file mode 100644 index 000000000000..c1ba5ef1dd1e --- /dev/null +++ b/libs/core/algorithms/include/hpx/parallel/algorithms/detail/adjacent_difference.hpp @@ -0,0 +1,54 @@ +// Copyright (c) 2021 Srinivas Yadav +// +// 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) + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace hpx { namespace parallel { inline namespace v1 { namespace detail { + + template + struct sequential_adjacent_difference_t + : hpx::functional::tag_fallback< + sequential_adjacent_difference_t> + { + private: + template + friend inline OutIter tag_fallback_dispatch( + sequential_adjacent_difference_t, InIter first, + InIter last, OutIter dest, Op&& op) + { + return std::adjacent_difference( + first, last, dest, std::forward(op)); + } + }; + +#if !defined(HPX_COMPUTE_DEVICE_CODE) + template + HPX_INLINE_CONSTEXPR_VARIABLE sequential_adjacent_difference_t + sequential_adjacent_difference = + sequential_adjacent_difference_t{}; +#else + template + HPX_HOST_DEVICE HPX_FORCEINLINE OutIter sequential_adjacent_difference( + InIter first, InIter last, OutIter dest, Op&& op) + { + return sequential_adjacent_difference_t{}( + first, last, dest, std::forward(op)); + } +#endif + +}}}} // namespace hpx::parallel::v1::detail diff --git a/libs/core/algorithms/include/hpx/parallel/datapar.hpp b/libs/core/algorithms/include/hpx/parallel/datapar.hpp index d8795dbe1add..1e6b9944986c 100644 --- a/libs/core/algorithms/include/hpx/parallel/datapar.hpp +++ b/libs/core/algorithms/include/hpx/parallel/datapar.hpp @@ -12,6 +12,7 @@ #if defined(HPX_HAVE_DATAPAR) #include +#include #include #include #include diff --git a/libs/core/algorithms/include/hpx/parallel/datapar/adjacent_difference.hpp b/libs/core/algorithms/include/hpx/parallel/datapar/adjacent_difference.hpp new file mode 100644 index 000000000000..aab7b3aa9229 --- /dev/null +++ b/libs/core/algorithms/include/hpx/parallel/datapar/adjacent_difference.hpp @@ -0,0 +1,72 @@ +// Copyright (c) 2021 Srinivas Yadav +// +// 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) + +#pragma once + +#include + +#if defined(HPX_HAVE_DATAPAR) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace hpx { namespace parallel { inline namespace v1 { namespace detail { + + /////////////////////////////////////////////////////////////////////////// + template + struct datapar_adjacent_difference + { + template + static inline OutIter call( + InIter first, InIter last, OutIter dest, Op&& op) + { + if (first == last) + return dest; + auto count = std::distance(first, last) - 1; + + InIter prev = first; + *dest++ = *first++; + + if (count == 0) + { + return dest; + } + + using hpx::get; + using hpx::util::make_zip_iterator; + util::loop_n>( + make_zip_iterator(first, prev, dest), count, [op](auto&& it) { + get<2>(*it) = HPX_INVOKE(op, get<0>(*it), get<1>(*it)); + }); + std::advance(dest, count); + return dest; + } + }; + + template ::value&& + hpx::parallel::util::detail::iterator_datapar_compatible< + InIter>::value)> + inline OutIter tag_dispatch(sequential_adjacent_difference_t, + InIter first, InIter last, OutIter dest, Op&& op) + { + return datapar_adjacent_difference::call( + first, last, dest, std::forward(op)); + } +}}}} // namespace hpx::parallel::v1::detail +#endif diff --git a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt index e59285966936..310e00583824 100644 --- a/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/algorithms/CMakeLists.txt @@ -18,8 +18,6 @@ endforeach() # add tests set(tests adjacentdifference - adjacentdifference_exception - adjacentdifference_bad_alloc adjacentfind adjacentfind_exception adjacentfind_bad_alloc diff --git a/libs/core/algorithms/tests/unit/algorithms/adjacentdifference.cpp b/libs/core/algorithms/tests/unit/algorithms/adjacentdifference.cpp index 3eeded2dbc2e..d86821d8edaf 100644 --- a/libs/core/algorithms/tests/unit/algorithms/adjacentdifference.cpp +++ b/libs/core/algorithms/tests/unit/algorithms/adjacentdifference.cpp @@ -1,3 +1,4 @@ +// Copyright (c) 2021 Srinivas Yadav // Copyright (c) 2015 Daniel Bourgeois // // SPDX-License-Identifier: BSL-1.0 @@ -5,69 +6,64 @@ // 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" +#include "adjacentdifference_tests.hpp" -//////////////////////////////////////////////////////////////////////////// -template -void test_adjacent_difference(ExPolicy policy) +void adjacent_difference_test() { - static_assert(hpx::is_execution_policy::value, - "hpx::is_execution_policy::value"); + using namespace hpx::execution; + test_adjacent_difference(seq); + test_adjacent_difference(par); + test_adjacent_difference(par_unseq); - std::vector c = test::random_iota(10007); - std::vector d(10007); - std::vector d_ans(10007); + test_adjacent_difference_async(seq(task)); + test_adjacent_difference_async(par(task)); +} - auto it = hpx::parallel::adjacent_difference( - policy, std::begin(c), std::end(c), std::begin(d)); - std::adjacent_difference(std::begin(c), std::end(c), std::begin(d_ans)); +template +void test_adjacent_difference_exception() +{ + using namespace hpx::execution; - HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), - [](std::size_t lhs, std::size_t rhs) -> bool { return lhs == rhs; })); + // If the execution policy object is of type vector_execution_policy, + // std::terminate shall be called. therefore we do not test exceptions + // with a vector execution policy + test_adjacent_difference_exception(seq, IteratorTag()); + test_adjacent_difference_exception(par, IteratorTag()); - HPX_TEST(std::end(d) == it); + test_adjacent_difference_exception_async(seq(task), IteratorTag()); + test_adjacent_difference_exception_async(par(task), IteratorTag()); } -template -void test_adjacent_difference_async(ExPolicy p) +void adjacent_difference_exception_test() { - static_assert(hpx::is_execution_policy::value, - "hpx::is_execution_policy::value"); - - std::vector c = test::random_iota(10007); - std::vector d(10007); - std::vector d_ans(10007); + test_adjacent_difference_exception(); + test_adjacent_difference_exception(); +} - auto f_it = hpx::parallel::adjacent_difference( - p, std::begin(c), std::end(c), std::begin(d)); - std::adjacent_difference(std::begin(c), std::end(c), std::begin(d_ans)); +template +void test_adjacent_difference_bad_alloc() +{ + using namespace hpx::execution; - f_it.wait(); - HPX_TEST(std::equal(std::begin(d), std::end(d), std::begin(d_ans), - [](std::size_t lhs, std::size_t rhs) -> bool { return lhs == rhs; })); + // If the execution policy object is of type vector_execution_policy, + // std::terminate shall be called. therefore we do not test exceptions + // with a vector execution policy + test_adjacent_difference_bad_alloc(seq, IteratorTag()); + test_adjacent_difference_bad_alloc(par, IteratorTag()); - HPX_TEST(std::end(d) == f_it.get()); + test_adjacent_difference_bad_alloc_async(seq(task), IteratorTag()); + test_adjacent_difference_bad_alloc_async(par(task), IteratorTag()); } -void adjacent_difference_test() +void adjacent_difference_bad_alloc_test() { - using namespace hpx::execution; - test_adjacent_difference(seq); - test_adjacent_difference(par); - test_adjacent_difference(par_unseq); - - test_adjacent_difference_async(seq(task)); - test_adjacent_difference_async(par(task)); + test_adjacent_difference_bad_alloc(); + test_adjacent_difference_bad_alloc(); } int hpx_main(hpx::program_options::variables_map& vm) diff --git a/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp b/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp new file mode 100644 index 000000000000..a99d8733a409 --- /dev/null +++ b/libs/core/algorithms/tests/unit/algorithms/adjacentdifference_tests.hpp @@ -0,0 +1,216 @@ +// Copyright (c) 2015 Daniel Bourgeois +// +// 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) + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////// +template +void test_adjacent_difference(ExPolicy policy) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + std::vector c = test::random_iota(10007); + std::vector d(10007); + std::vector d_ans(10007); + + auto it = hpx::parallel::adjacent_difference( + policy, std::begin(c), std::end(c), std::begin(d)); + 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) == it); +} + +template +void test_adjacent_difference_async(ExPolicy p) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + std::vector c = test::random_iota(10007); + std::vector d(10007); + std::vector d_ans(10007); + + auto f_it = hpx::parallel::adjacent_difference( + p, std::begin(c), std::end(c), std::begin(d)); + std::adjacent_difference(std::begin(c), std::end(c), std::begin(d_ans)); + + f_it.wait(); + 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) == f_it.get()); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_adjacent_difference_exception(ExPolicy policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + std::vector c(10007); + std::vector d(10007); + + bool caught_exception = false; + try + { + hpx::parallel::adjacent_difference(policy, + decorated_iterator(std::begin(c)), decorated_iterator(std::end(c)), + std::begin(d), [](auto lhs, auto rhs) { + throw std::runtime_error("test"); + return lhs - rhs; + }); + HPX_TEST(false); + } + catch (hpx::exception_list const& e) + { + caught_exception = true; + test::test_num_exceptions::call(policy, e); + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_exception); +} + +template +void test_adjacent_difference_exception_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::vector d(10007); + + bool caught_exception = false; + bool returned_from_algorithm = false; + + try + { + hpx::future f = hpx::parallel::adjacent_difference(p, + decorated_iterator(std::begin(c)), decorated_iterator(std::end(c)), + std::begin(d), [](auto lhs, auto rhs) { + throw std::runtime_error("test"); + return lhs - rhs; + }); + + returned_from_algorithm = true; + + f.get(); + + HPX_TEST(false); + } + catch (hpx::exception_list const& e) + { + caught_exception = true; + test::test_num_exceptions::call(p, e); + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_exception); + HPX_TEST(returned_from_algorithm); +} + +////////////////////////////////////////////////////////////////////////////// +template +void test_adjacent_difference_bad_alloc(ExPolicy policy, IteratorTag) +{ + static_assert(hpx::is_execution_policy::value, + "hpx::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::vector d(10007); + + bool caught_bad_alloc = false; + try + { + hpx::parallel::adjacent_difference(policy, + decorated_iterator(std::begin(c)), decorated_iterator(std::end(c)), + std::begin(d), [](auto lhs, auto rhs) { + throw std::bad_alloc(); + return lhs - rhs; + }); + } + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_adjacent_difference_bad_alloc_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::vector d(10007); + + bool caught_bad_alloc = false; + bool returned_from_algorithm = false; + + try + { + hpx::future f = hpx::parallel::adjacent_difference(p, + decorated_iterator(std::begin(c)), decorated_iterator(std::end(c)), + std::begin(d), [](auto lhs, auto rhs) { + throw std::bad_alloc(); + return lhs - rhs; + }); + returned_from_algorithm = true; + + f.get(); + + HPX_TEST(false); + } + catch (std::bad_alloc const&) + { + caught_bad_alloc = true; + } + catch (...) + { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); + HPX_TEST(returned_from_algorithm); +} diff --git a/libs/core/algorithms/tests/unit/datapar_algorithms/CMakeLists.txt b/libs/core/algorithms/tests/unit/datapar_algorithms/CMakeLists.txt index d2f774c39668..4514ff5bd3ec 100644 --- a/libs/core/algorithms/tests/unit/datapar_algorithms/CMakeLists.txt +++ b/libs/core/algorithms/tests/unit/datapar_algorithms/CMakeLists.txt @@ -11,6 +11,7 @@ if(HPX_WITH_DATAPAR_VC OR HPX_WITH_CXX20_EXPERIMENTAL_SIMD) ${tests} copy_datapar copyn_datapar + adjacentdifference_datapar count_datapar countif_datapar fill_datapar diff --git a/libs/core/algorithms/tests/unit/datapar_algorithms/adjacentdifference_datapar.cpp b/libs/core/algorithms/tests/unit/datapar_algorithms/adjacentdifference_datapar.cpp new file mode 100644 index 000000000000..27ee378dff4e --- /dev/null +++ b/libs/core/algorithms/tests/unit/datapar_algorithms/adjacentdifference_datapar.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2021 Srinivas Yadav +// Copyright (c) 2015 Daniel Bourgeois +// +// 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 "../algorithms/adjacentdifference_tests.hpp" + +void adjacent_difference_test() +{ + using namespace hpx::execution; + test_adjacent_difference(simd); + test_adjacent_difference(par_simd); + + test_adjacent_difference_async(simd(task)); + test_adjacent_difference_async(par_simd(task)); +} + +template +void test_adjacent_difference_exception() +{ + using namespace hpx::execution; + test_adjacent_difference_exception(simd, IteratorTag()); + test_adjacent_difference_exception(par_simd, IteratorTag()); + + test_adjacent_difference_exception_async(simd(task), IteratorTag()); + test_adjacent_difference_exception_async(par_simd(task), IteratorTag()); +} + +void adjacent_difference_exception_test() +{ + test_adjacent_difference_exception(); + test_adjacent_difference_exception(); +} + +template +void test_adjacent_difference_bad_alloc() +{ + using namespace hpx::execution; + test_adjacent_difference_bad_alloc(simd, IteratorTag()); + test_adjacent_difference_bad_alloc(par_simd, IteratorTag()); + + test_adjacent_difference_bad_alloc_async(simd(task), IteratorTag()); + test_adjacent_difference_bad_alloc_async(par_simd(task), IteratorTag()); +} + +void adjacent_difference_bad_alloc_test() +{ + test_adjacent_difference_bad_alloc(); + test_adjacent_difference_bad_alloc(); +} + +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_difference_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(); +}