From ddbc8f9e4c2164a126cbdc36231f0ab4aba6c274 Mon Sep 17 00:00:00 2001 From: Bruno Pitrus Date: Sun, 26 Nov 2017 14:12:39 +0100 Subject: [PATCH] Container version of move --- hpx/include/parallel_move.hpp | 1 + hpx/parallel/container_algorithms/move.hpp | 90 ++++ .../container_algorithms/CMakeLists.txt | 1 + .../container_algorithms/move_range.cpp | 451 ++++++++++++++++++ 4 files changed, 543 insertions(+) create mode 100644 hpx/parallel/container_algorithms/move.hpp create mode 100644 tests/unit/parallel/container_algorithms/move_range.cpp diff --git a/hpx/include/parallel_move.hpp b/hpx/include/parallel_move.hpp index f72cf423443b..44f9df3fe74c 100644 --- a/hpx/include/parallel_move.hpp +++ b/hpx/include/parallel_move.hpp @@ -8,6 +8,7 @@ #define HPX_PARALLEL_MOVE_JUN_28_2014_0827AM #include +#include #endif diff --git a/hpx/parallel/container_algorithms/move.hpp b/hpx/parallel/container_algorithms/move.hpp new file mode 100644 index 000000000000..3c6487d407ea --- /dev/null +++ b/hpx/parallel/container_algorithms/move.hpp @@ -0,0 +1,90 @@ +// Copyright (c) 2017 Bruno Pitrus +// +// 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) + +/// \file parallel/container_algorithms/move.hpp + +#if !defined(HPX_PARALLEL_CONTAINER_ALGORITHM_MOVE_26_NOV_2017_1248PM) +#define HPX_PARALLEL_CONTAINER_ALGORITHM_MOVE_26_NOV_2017_1248PM + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace hpx { namespace parallel { inline namespace v1 +{ + /// Moves the elements in the range \a rng to another range beginning + /// at \a dest. After this operation the elements in the moved-from + /// range will still contain valid values of the appropriate type, + /// but not necessarily the same values as before the move. + /// + /// \note Complexity: Performs exactly + /// std::distance(begin(rng), end(rng)) assignments. + /// + /// \tparam ExPolicy The type of the execution policy to use (deduced). + /// It describes the manner in which the execution + /// of the algorithm may be parallelized and the manner + /// in which it executes the assignments. + /// \tparam Rng The type of the source range used (deduced). + /// The iterators extracted from this range type must + /// meet the requirements of an input iterator. + /// \tparam OutIter The type of the iterator representing the + /// destination range (deduced). + /// This iterator type must meet the requirements of an + /// output iterator. + /// + /// \param policy The execution policy to use for the scheduling of + /// the iterations. + /// \param rng Refers to the sequence of elements the algorithm + /// will be applied to. + /// \param dest Refers to the beginning of the destination range. + /// + /// The assignments in the parallel \a copy algorithm invoked with an + /// execution policy object of type \a sequenced_policy + /// execute in sequential order in the calling thread. + /// + /// The assignments in the parallel \a copy algorithm invoked with + /// an execution policy object of type \a parallel_policy or + /// \a parallel_task_policy are permitted to execute in an unordered + /// fashion in unspecified threads, and indeterminately sequenced + /// within each thread. + /// + /// \returns The \a move algorithm returns a + /// \a hpx::future), + /// tag::out(FwdIter2)> > if the execution policy is of type + /// \a sequenced_task_policy or \a parallel_task_policy and + /// returns \a tagged_pair), + /// tag::out(FwdIter2)> otherwise. + /// The \a move algorithm returns the pair of the input iterator + /// \a last and the output iterator to the element in the + /// destination range, one past the last element moved. + /// + template ::value && + hpx::traits::is_range::value && + hpx::traits::is_iterator::value)> + typename util::detail::algorithm_result< + ExPolicy, + hpx::util::tagged_pair< + tag::in(typename hpx::traits::range_traits::iterator_type), + tag::out(OutIter) + > + >::type + move(ExPolicy && policy, Rng && rng, OutIter dest) + { + return move(std::forward(policy), + hpx::util::begin(rng), hpx::util::end(rng), dest); + } +}}} + +#endif diff --git a/tests/unit/parallel/container_algorithms/CMakeLists.txt b/tests/unit/parallel/container_algorithms/CMakeLists.txt index 3a5fb21cba24..8b9fa26f96ba 100644 --- a/tests/unit/parallel/container_algorithms/CMakeLists.txt +++ b/tests/unit/parallel/container_algorithms/CMakeLists.txt @@ -18,6 +18,7 @@ set(tests merge_range min_element_range minmax_element_range + move_range none_of_range partition_range partition_copy_range diff --git a/tests/unit/parallel/container_algorithms/move_range.cpp b/tests/unit/parallel/container_algorithms/move_range.cpp new file mode 100644 index 000000000000..5c676efa0398 --- /dev/null +++ b/tests/unit/parallel/container_algorithms/move_range.cpp @@ -0,0 +1,451 @@ +// Copyright (c) 2014 Grant Mercer +// Copyright (c) 2015 Hartmut Kaiser +// Copyright (c) 2017 Bruno Pitrus +// +// 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_move(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + typedef test::test_container, IteratorTag> test_vector; + + test_vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + hpx::parallel::move(policy, c, std::begin(d)); + + //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 test_move_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + typedef test::test_container, IteratorTag> test_vector; + + test_vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + + auto f = hpx::parallel::move(p, c, std::begin(d)); + hpx::future g = f.then( + [&d, &c](hpx::future f) + { + HPX_TEST(!f.has_exception()); + std::copy(std::begin(d), std::end(d), std::begin(c)); + }); + g.wait(); + + 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()); +} + +#if defined(HPX_HAVE_ALGORITHM_INPUT_ITERATOR_SUPPORT) +template +void test_move_outiter(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + typedef test::test_container, IteratorTag> test_vector; + + test_vector c(10007); + std::vector d(0); + std::iota(std::begin(c), std::end(c), std::rand()); + hpx::parallel::move(policy, c, std::back_inserter(d)); + + //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 test_move_outiter_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + typedef test::test_container, IteratorTag> test_vector; + + test_vector c(10007); + std::vector d(0); + std::iota(std::begin(c), std::end(c), std::rand()); + + auto f = hpx::parallel::move(p, c, std::back_inserter(d)); + + hpx::future g = f.then( + [&d, &c](hpx::future f) + { + HPX_TEST(!f.has_exception()); + std::copy(std::begin(d), std::end(d), std::begin(c)); + }); + g.wait(); + + 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_move() +{ + using namespace hpx::parallel; + test_move(execution::seq, IteratorTag()); + test_move(execution::par, IteratorTag()); + test_move(execution::par_unseq, IteratorTag()); + + test_move_async(execution::seq(execution::task), IteratorTag()); + test_move_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_move(execution_policy(execution::seq), IteratorTag()); + test_move(execution_policy(execution::par), IteratorTag()); + test_move(execution_policy(execution::par_unseq), IteratorTag()); + + test_move(execution_policy(execution::seq(execution::task)), IteratorTag()); + test_move(execution_policy(execution::par(execution::task)), IteratorTag()); +#endif + +#if defined(HPX_HAVE_ALGORITHM_INPUT_ITERATOR_SUPPORT) + // assure output iterator will work + test_move_outiter(execution::seq, IteratorTag()); + test_move_outiter(execution::par, IteratorTag()); + test_move_outiter(execution::par_unseq, IteratorTag()); + + test_move_outiter_async(execution::seq(execution::task), IteratorTag()); + test_move_outiter_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_move_outiter(execution_policy(execution::seq), IteratorTag()); + test_move_outiter(execution_policy(execution::par), IteratorTag()); + test_move_outiter(execution_policy(execution::par_unseq), IteratorTag()); + + test_move_outiter(execution_policy(execution::seq(execution::task)), IteratorTag()); + test_move_outiter(execution_policy(execution::par(execution::task)), IteratorTag()); +#endif +#endif +} + +void move_test() +{ + test_move(); + test_move(); +#if defined(HPX_HAVE_ALGORITHM_INPUT_ITERATOR_SUPPORT) + test_move(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_move_exception(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + + bool caught_exception = false; + try { + hpx::parallel::move(policy, + hpx::util::make_iterator_range( + decorated_iterator( + std::begin(c), + [](){ throw std::runtime_error("test"); }), + decorated_iterator(std::end(c))), + std::begin(d)); + 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_move_exception_async(ExPolicy p, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + + bool caught_exception = false; + bool returned_from_algorithm = false; + try { + auto f = + hpx::parallel::move(p, + hpx::util::make_iterator_range( + decorated_iterator( + std::begin(c), + [](){ throw std::runtime_error("test"); }), + decorated_iterator(std::end(c))), + std::begin(d)); + 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_move_exception() +{ + using namespace hpx::parallel; + + // 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_move_exception(execution::seq, IteratorTag()); + test_move_exception(execution::par, IteratorTag()); + + test_move_exception_async(execution::seq(execution::task), IteratorTag()); + test_move_exception_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_move_exception(execution_policy(execution::seq), IteratorTag()); + test_move_exception(execution_policy(execution::par), IteratorTag()); + + test_move_exception(execution_policy(execution::seq(execution::task)), IteratorTag()); + test_move_exception(execution_policy(execution::par(execution::task)), IteratorTag()); +#endif +} + +void move_exception_test() +{ + test_move_exception(); + test_move_exception(); +#if defined(HPX_HAVE_ALGORITHM_INPUT_ITERATOR_SUPPORT) + test_move_exception(); +#endif +} + +////////////////////////////////////////////////////////////////////////////// +template +void test_move_bad_alloc(ExPolicy policy, IteratorTag) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef std::vector::iterator base_iterator; + typedef test::decorated_iterator + decorated_iterator; + + std::vector c(10007); + std::vector d(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + + bool caught_bad_alloc = false; + try { + hpx::parallel::move(policy, + hpx::util::make_iterator_range( + decorated_iterator( + std::begin(c), + [](){ throw std::bad_alloc(); }), + decorated_iterator(std::end(c))), + std::begin(d)); + HPX_TEST(false); + } + catch (std::bad_alloc const&) { + caught_bad_alloc = true; + } + catch (...) { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_move_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(c.size()); + std::iota(std::begin(c), std::end(c), std::rand()); + + bool caught_bad_alloc = false; + bool returned_from_algorithm = false; + try { + auto f = + hpx::parallel::move(p, + hpx::util::make_iterator_range( + decorated_iterator( + std::begin(c), + [](){ throw std::bad_alloc(); }), + decorated_iterator(std::end(c))), + std::begin(d)); + 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); +} + +template +void test_move_bad_alloc() +{ + using namespace hpx::parallel; + + // 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_move_bad_alloc(execution::seq, IteratorTag()); + test_move_bad_alloc(execution::par, IteratorTag()); + + test_move_bad_alloc_async(execution::seq(execution::task), IteratorTag()); + test_move_bad_alloc_async(execution::par(execution::task), IteratorTag()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_move_bad_alloc(execution_policy(execution::seq), IteratorTag()); + test_move_bad_alloc(execution_policy(execution::par), IteratorTag()); + + test_move_bad_alloc(execution_policy(execution::seq(execution::task)), IteratorTag()); + test_move_bad_alloc(execution_policy(execution::par(execution::task)), IteratorTag()); +#endif +} + +void move_bad_alloc_test() +{ + test_move_bad_alloc(); + test_move_bad_alloc(); +#if defined(HPX_HAVE_ALGORITHM_INPUT_ITERATOR_SUPPORT) + test_move_bad_alloc(); +#endif +} + +int hpx_main(boost::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_test(); + move_exception_test(); + move_bad_alloc_test(); + return hpx::finalize(); +} + +int main(int argc, char* argv[]) +{ + // add command line option which controls the random number generator seed + using namespace boost::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_TEST_EQ_MSG(hpx::init(desc_commandline, argc, argv, cfg), 0, + "HPX main exited with non-zero status"); + + return hpx::util::report_errors(); +}