From 7df1fad592df04100391c18a9ebe7abd1bde23e6 Mon Sep 17 00:00:00 2001 From: Taeguk Kwon Date: Tue, 11 Jul 2017 22:07:59 +0900 Subject: [PATCH 1/8] Implement parallel::unique_copy. And add docs for that. --- docs/CMakeLists.txt | 1 + docs/manual/parallel_algorithms.qbk | 5 + hpx/include/parallel_unique.hpp | 12 + hpx/parallel/algorithms/partition.hpp | 6 +- hpx/parallel/algorithms/unique.hpp | 367 ++++++++++++++++++++++++++ 5 files changed, 388 insertions(+), 3 deletions(-) create mode 100644 hpx/include/parallel_unique.hpp create mode 100644 hpx/parallel/algorithms/unique.hpp diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index a096833a1aa3..d7b383179d78 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -107,6 +107,7 @@ set(doxygen_dependencies "${PROJECT_SOURCE_DIR}/hpx/parallel/algorithms/uninitialized_fill.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/algorithms/uninitialized_move.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/algorithms/uninitialized_value_construct.hpp" + "${PROJECT_SOURCE_DIR}/hpx/parallel/algorithms/unique.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/copy.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/for_each.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/generate.hpp" diff --git a/docs/manual/parallel_algorithms.qbk b/docs/manual/parallel_algorithms.qbk index dd33c4b936ab..2b54a13cd632 100644 --- a/docs/manual/parallel_algorithms.qbk +++ b/docs/manual/parallel_algorithms.qbk @@ -330,6 +330,11 @@ __hpx__ provides implementations of the following parallel algorithms: [``] [[cpprefalgodocs transform]] ] + [[ [algoref unique_copy] ] + [Applies a function to a range of elements.] + [``] + [[cpprefalgodocs unique_copy]] + ] ] [table Set operations on sorted sequences (In Header: ``) diff --git a/hpx/include/parallel_unique.hpp b/hpx/include/parallel_unique.hpp new file mode 100644 index 000000000000..f37c498760bb --- /dev/null +++ b/hpx/include/parallel_unique.hpp @@ -0,0 +1,12 @@ +// Copyright (c) 2017 Taeguk Kwon +// +// 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 !defined(HPX_PARALLEL_UNIQUE_JUL_07_2017_1631PM) +#define HPX_PARALLEL_UNIQUE_JUL_07_2017_1631PM + +#include + +#endif + diff --git a/hpx/parallel/algorithms/partition.hpp b/hpx/parallel/algorithms/partition.hpp index 3ac21b27cd50..7cdb51a161cb 100644 --- a/hpx/parallel/algorithms/partition.hpp +++ b/hpx/parallel/algorithms/partition.hpp @@ -548,9 +548,9 @@ namespace hpx { namespace parallel { inline namespace v1 /// \a tagged_tuple /// otherwise. /// The \a partition_copy algorithm returns the tuple of - /// the input iterator \a last, - /// the output iterator to the end of the \a dest_true range, and - /// the output iterator to the end of the \a dest_false range. + /// the source iterator \a last, + /// the destination iterator to the end of the \a dest_true range, and + /// the destination iterator to the end of the \a dest_false range. /// template +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace hpx { namespace parallel { inline namespace v1 +{ + ///////////////////////////////////////////////////////////////////////////// + // unique_copy + namespace detail + { + /// \cond NOINTERNAL + + // sequential unique_copy with projection function + template + std::pair + sequential_unique_copy(FwdIter first, FwdIter last, OutIter dest, + Pred && pred, Proj && proj, std::true_type) + { + if (first == last) + return std::make_pair(std::move(last), std::move(dest)); + + FwdIter base = first; + + *dest++ = *first; + + while (++first != last) + { + if (!hpx::util::invoke(pred, + hpx::util::invoke(proj, *base), + hpx::util::invoke(proj, *first))) + { + base = first; + *dest++ = *first; + } + } + return std::make_pair(std::move(last), std::move(dest)); + } + + // sequential unique_copy with projection function + template + std::pair + sequential_unique_copy(InIter first, InIter last, OutIter dest, + Pred && pred, Proj && proj, std::false_type) + { + if (first == last) + return std::make_pair(std::move(last), std::move(dest)); + + auto base_val = *first; + + *dest++ = *first; + + while (++first != last) + { + if (!hpx::util::invoke(pred, + hpx::util::invoke(proj, base_val), + hpx::util::invoke(proj, *first))) + { + base_val = *first; + *dest++ = *first; + } + } + return std::make_pair(std::move(last), std::move(dest)); + } + + template + struct unique_copy : public detail::algorithm, IterPair> + { + unique_copy() + : unique_copy::algorithm("unique_copy") + {} + + template + static std::pair + sequential(ExPolicy, InIter first, InIter last, OutIter dest, + Pred && pred, Proj && proj/* = Proj()*/) + { + return sequential_unique_copy(first, last, dest, + std::forward(pred), std::forward(proj), + hpx::traits::is_forward_iterator()); + } + + template + static typename util::detail::algorithm_result< + ExPolicy, std::pair + >::type + parallel(ExPolicy && policy, FwdIter1 first, FwdIter1 last, + FwdIter2 dest, Pred && pred, Proj && proj/* = Proj()*/) + { + typedef hpx::util::zip_iterator zip_iterator; + typedef util::detail::algorithm_result< + ExPolicy, std::pair + > result; + typedef typename std::iterator_traits::difference_type + difference_type; + + if (first == last) + return result::get( + std::make_pair(std::move(last), std::move(dest))); + + difference_type count = std::distance(first, last); + + *dest++ = *first; + + if (count == 1) + return result::get( + std::make_pair(std::move(last), std::move(dest))); + + boost::shared_array flags(new bool[count - 1]); + std::size_t init = 0; + + using hpx::util::get; + using hpx::util::make_zip_iterator; + typedef util::scan_partitioner< + ExPolicy, std::pair, std::size_t + > scan_partitioner_type; + + auto f1 = + [pred, proj, flags, policy] + ( + zip_iterator part_begin, std::size_t part_size + ) -> std::size_t + { + HPX_UNUSED(flags); + HPX_UNUSED(policy); + + FwdIter1 base = get<0>(part_begin.get_iterator_tuple()); + std::size_t curr = 0; + + // MSVC complains if pred or proj is captured by ref below + util::loop_n( + ++part_begin, part_size, + [base, pred, proj, &curr](zip_iterator it) mutable + { + using hpx::util::invoke; + + bool f = invoke(pred, + invoke(proj, *base), + invoke(proj, get<0>(*it))); + + if (!(get<1>(*it) = f)) + { + base = get<0>(it.get_iterator_tuple()); + ++curr; + } + }); + + return curr; + }; + auto f3 = + [dest, flags, policy]( + zip_iterator part_begin, std::size_t part_size, + hpx::shared_future curr, + hpx::shared_future next + ) mutable -> void + { + HPX_UNUSED(flags); + HPX_UNUSED(policy); + + next.get(); // rethrow exceptions + + std::advance(dest, curr.get()); + util::loop_n( + ++part_begin, part_size, + [&dest](zip_iterator it) mutable + { + if(!get<1>(*it)) + *dest++ = get<0>(*it); + }); + }; + + return scan_partitioner_type::call( + std::forward(policy), + make_zip_iterator(first, flags.get()), + count - 1, init, + // step 1 performs first part of scan algorithm + std::move(f1), + // step 2 propagates the partition results from left + // to right + hpx::util::unwrapped(std::plus()), + // step 3 runs final accumulation on each partition + std::move(f3), + // step 4 use this return value + [last, dest, flags]( + std::vector > && items, + std::vector > &&) mutable + -> std::pair + { + HPX_UNUSED(flags); + + std::advance(dest, items.back().get()); + return std::make_pair(std::move(last), std::move(dest)); + }); + } + }; + /// \endcond + } + + /// Copies the elements from the range [first, last), + /// to another range beginning at \a dest in such a way that + /// there are no consecutive equal elements. Only the first element of + /// each group of equal elements is copied. + /// + /// \note Complexity: Performs not more than \a last - \a first + /// assignments, exactly \a last - \a first - 1 applications of + /// the predicate \a pred. + /// + /// \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 FwdIter1 The type of the source iterators used (deduced). + /// This iterator type must meet the requirements of an + /// forward iterator. + /// \tparam FwdIter2 The type of the iterator representing the + /// destination range (deduced). + /// This iterator type must meet the requirements of an + /// forward iterator. + /// \tparam Pred The type of the function/function object to use + /// (deduced). Unlike its sequential form, the parallel + /// overload of \a unique_copy requires \a Pred to meet the + /// requirements of \a CopyConstructible. This defaults + /// to std::equal_to<> + /// \tparam Proj The type of an optional projection function. This + /// defaults to \a util::projection_identity + /// + /// \param policy The execution policy to use for the scheduling of + /// the iterations. + /// \param first Refers to the beginning of the sequence of elements + /// the algorithm will be applied to. + /// \param last Refers to the end of the sequence of elements the + /// algorithm will be applied to. + /// \param dest Refers to the beginning of the destination range. + /// \param pred Specifies the function (or function object) which + /// will be invoked for each of the elements in the + /// sequence specified by [first, last).This is an + /// binary predicate which returns \a true for the + /// required elements. The signature of this predicate + /// should be equivalent to: + /// \code + /// bool pred(const Type &a, const Type &b); + /// \endcode \n + /// The signature does not need to have const&, but + /// the function must not modify the objects passed to + /// it. The type \a Type must be such that an object of + /// type \a FwdIter1 can be dereferenced and then + /// implicitly converted to \a Type. + /// \param proj Specifies the function (or function object) which + /// will be invoked for each of the elements as a + /// projection operation before the actual predicate + /// \a is invoked. + /// + /// The assignments in the parallel \a unique_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 unique_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 unique_copy algorithm returns a + /// \a hpx::future > + /// if the execution policy is of type + /// \a sequenced_task_policy or + /// \a parallel_task_policy and + /// returns \a tagged_pair + /// otherwise. + /// The \a unique_copy algorithm returns the pair of + /// the source iterator to \a last, and + /// the destination iterator to the end of the \a dest range. + /// + template ::value && + hpx::traits::is_iterator::value && + hpx::traits::is_iterator::value && + traits::is_projected::value && + traits::is_indirect_callable< + ExPolicy, Pred, + traits::projected, + traits::projected + >::value)> + typename util::detail::algorithm_result< + ExPolicy, hpx::util::tagged_pair< + tag::in(FwdIter1), tag::out(FwdIter2)> + >::type + unique_copy(ExPolicy&& policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest, + Pred && pred = Pred(), Proj && proj = Proj()) + { +#if defined(HPX_HAVE_ALGORITHM_INPUT_ITERATOR_SUPPORT) + static_assert( + (hpx::traits::is_input_iterator::value), + "Required at least input iterator."); + static_assert( + (hpx::traits::is_output_iterator::value || + hpx::traits::is_forward_iterator::value), + "Requires at least output iterator."); + + typedef std::integral_constant::value || + !hpx::traits::is_forward_iterator::value || + !hpx::traits::is_forward_iterator::value + > is_seq; +#else + static_assert( + (hpx::traits::is_forward_iterator::value), + "Required at least forward iterator."); + static_assert( + (hpx::traits::is_forward_iterator::value), + "Requires at least forward iterator."); + + typedef execution::is_sequenced_execution_policy is_seq; +#endif + + typedef std::pair result_type; + + return hpx::util::make_tagged_pair( + detail::unique_copy().call( + std::forward(policy), is_seq(), + first, last, dest, std::forward(pred), + std::forward(proj))); + } +}}} + +#endif From fa25bab63c442925bf0762a2faed5ffb8957cdde Mon Sep 17 00:00:00 2001 From: Taeguk Kwon Date: Tue, 11 Jul 2017 22:08:23 +0900 Subject: [PATCH 2/8] Add unit tests for parallel::unique_copy. --- tests/unit/parallel/algorithms/CMakeLists.txt | 1 + .../unit/parallel/algorithms/unique_copy.cpp | 87 +++ .../parallel/algorithms/unique_copy_tests.hpp | 497 ++++++++++++++++++ 3 files changed, 585 insertions(+) create mode 100644 tests/unit/parallel/algorithms/unique_copy.cpp create mode 100644 tests/unit/parallel/algorithms/unique_copy_tests.hpp diff --git a/tests/unit/parallel/algorithms/CMakeLists.txt b/tests/unit/parallel/algorithms/CMakeLists.txt index 2d10dd15b60b..d786d5a28a67 100644 --- a/tests/unit/parallel/algorithms/CMakeLists.txt +++ b/tests/unit/parallel/algorithms/CMakeLists.txt @@ -121,6 +121,7 @@ set(tests uninitialized_moven uninitialized_value_construct uninitialized_value_constructn + unique_copy ) if(HPX_WITH_EXECUTOR_COMPATIBILITY) diff --git a/tests/unit/parallel/algorithms/unique_copy.cpp b/tests/unit/parallel/algorithms/unique_copy.cpp new file mode 100644 index 000000000000..66705c3ee8a4 --- /dev/null +++ b/tests/unit/parallel/algorithms/unique_copy.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 2017 Taeguk Kwon +// +// 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 "unique_copy_tests.hpp" +#include "test_utils.hpp" + +//////////////////////////////////////////////////////////////////////////// +void unique_copy_test() +{ + std::cout << "--- unique_copy_test ---" << std::endl; + test_unique_copy(); + test_unique_copy(); + test_unique_copy(); +} + +void unique_copy_exception_test() +{ + std::cout << "--- unique_copy_exception_test ---" << std::endl; + test_unique_copy_exception(); + test_unique_copy_exception(); + test_unique_copy_exception(); +} + +void unique_copy_bad_alloc_test() +{ + std::cout << "--- unique_copy_bad_alloc_test ---" << std::endl; + test_unique_copy_bad_alloc(); + test_unique_copy_bad_alloc(); + test_unique_copy_bad_alloc(); +} + +/////////////////////////////////////////////////////////////////////////////// +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); + + unique_copy_test(); + unique_copy_exception_test(); + unique_copy_bad_alloc_test(); + + std::cout << "Test Finish!" << std::endl; + + 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(); +} diff --git a/tests/unit/parallel/algorithms/unique_copy_tests.hpp b/tests/unit/parallel/algorithms/unique_copy_tests.hpp new file mode 100644 index 000000000000..0eb5185d9de9 --- /dev/null +++ b/tests/unit/parallel/algorithms/unique_copy_tests.hpp @@ -0,0 +1,497 @@ +// Copyright (c) 2017 Taeguk Kwon +// +// 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 !defined(HPX_PARALLEL_TEST_UNIQUE_JUL_07_2017_1940PM) +#define HPX_PARALLEL_TEST_UNIQUE_JUL_07_2017_1940PM + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_utils.hpp" + +/////////////////////////////////////////////////////////////////////////////// + +struct throw_always +{ + template + bool operator()(T const&, T const&) const + { + throw std::runtime_error("test"); + } +}; + +struct throw_bad_alloc +{ + template + bool operator()(T const&, T const&) const + { + throw std::bad_alloc(); + } +}; + +struct user_defined_type +{ + user_defined_type() = default; + user_defined_type(int rand_no) : val(rand_no) {} + + bool operator<(user_defined_type const& t) const + { + if (this->name < t.name) + return true; + else if (this->name > t.name) + return false; + else + return this->val < t.val; + } + + bool operator>(user_defined_type const& t) const + { + if (this->name > t.name) + return true; + else if (this->name < t.name) + return false; + else + return this->val > t.val; + } + + bool operator==(user_defined_type const& t) const + { + return this->name == t.name && this->val == t.val; + } + + user_defined_type const& operator++() + { + static const std::vector name_list = { + "ABB", "ABC", "ACB", "BCA", "CAA", "CAAA", "CAAB" + }; + name = name_list[std::rand() % name_list.size()]; + ++val; + return *this; + } + + std::string name; + int val; +}; + +struct random_fill +{ + random_fill() = default; + random_fill(int rand_base, int range) + : gen(std::rand()), + dist(rand_base - range / 2, rand_base + range / 2) + {} + + int operator()() + { + return dist(gen); + } + + boost::random::mt19937 gen; + boost::random::uniform_int_distribution<> dist; +}; + +/////////////////////////////////////////////////////////////////////////////// +template +void test_unique_copy(ExPolicy policy, IteratorTag, DataType, Pred pred, + int rand_base) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef typename std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + using hpx::util::get; + + std::size_t const size = 10007; + std::vector c(size), dest_res(size), dest_sol(size); + std::generate(std::begin(c), std::end(c), random_fill(rand_base, 6)); + + auto result = hpx::parallel::unique_copy(policy, + iterator(boost::begin(c)), iterator(boost::end(c)), + iterator(boost::begin(dest_res)), + pred); + auto solution = std::unique_copy(std::begin(c), std::end(c), + std::begin(dest_sol), + pred); + + bool equality = std::equal( + std::begin(dest_res), get<1>(result).base(), + std::begin(dest_sol), solution); + + HPX_TEST(equality); +} + +template +void test_unique_copy_async(ExPolicy policy, IteratorTag, DataType, Pred pred, + int rand_base) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef typename std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + using hpx::util::get; + + std::size_t const size = 10007; + std::vector c(size), dest_res(size), dest_sol(size); + std::generate(std::begin(c), std::end(c), random_fill(rand_base, 6)); + + auto f = hpx::parallel::unique_copy(policy, + iterator(boost::begin(c)), iterator(boost::end(c)), + iterator(boost::begin(dest_res)), + pred); + auto result = f.get(); + auto solution = std::unique_copy(std::begin(c), std::end(c), + std::begin(dest_sol), + pred); + + bool equality = std::equal( + std::begin(dest_res), get<1>(result).base(), + std::begin(dest_sol), solution); + + HPX_TEST(equality); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_unique_copy_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::test_iterator iterator; + + std::size_t const size = 10007; + std::vector c(size), dest(size); + std::generate(std::begin(c), std::end(c), random_fill()); + + bool caught_exception = false; + try { + auto result = hpx::parallel::unique_copy(policy, + iterator(boost::begin(c)), iterator(boost::end(c)), + iterator(boost::begin(dest)), + throw_always()); + + 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_unique_copy_exception_async(ExPolicy policy, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::size_t const size = 10007; + std::vector c(size), dest(size); + std::generate(std::begin(c), std::end(c), random_fill()); + + bool caught_exception = false; + bool returned_from_algorithm = false; + try { + auto f = hpx::parallel::unique_copy(policy, + iterator(boost::begin(c)), iterator(boost::end(c)), + iterator(boost::begin(dest)), + throw_always()); + returned_from_algorithm = true; + f.get(); + + 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); + HPX_TEST(returned_from_algorithm); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_unique_copy_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::test_iterator iterator; + + std::size_t const size = 10007; + std::vector c(size), dest(size); + std::generate(std::begin(c), std::end(c), random_fill()); + + bool caught_bad_alloc = false; + try { + auto result = hpx::parallel::unique_copy(policy, + iterator(boost::begin(c)), iterator(boost::end(c)), + iterator(boost::begin(dest)), + throw_bad_alloc()); + + HPX_TEST(false); + } + catch(std::bad_alloc const&) { + caught_bad_alloc = true; + } + catch(...) { + HPX_TEST(false); + } + + HPX_TEST(caught_bad_alloc); +} + +template +void test_unique_copy_bad_alloc_async(ExPolicy policy, IteratorTag) +{ + typedef std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::size_t const size = 10007; + std::vector c(size), dest(size); + std::generate(std::begin(c), std::end(c), random_fill()); + + bool caught_bad_alloc = false; + bool returned_from_algorithm = false; + try { + auto f = hpx::parallel::unique_copy(policy, + iterator(boost::begin(c)), iterator(boost::end(c)), + iterator(boost::begin(dest)), + throw_bad_alloc()); + 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_unique_copy_etc(ExPolicy policy, IteratorTag, + DataType, int rand_base) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + typedef typename std::vector::iterator base_iterator; + + using hpx::util::get; + + std::size_t const size = 10007; + std::vector c(size), dest_res(size), dest_sol(size); + std::generate(std::begin(c), std::end(c), random_fill(rand_base, size / 10)); + + // Test default predicate. + { + typedef test::test_iterator iterator; + + auto result = hpx::parallel::unique_copy(policy, + iterator(boost::begin(c)), iterator(boost::end(c)), + iterator(boost::begin(dest_res))); + auto solution = std::unique_copy(std::begin(c), std::end(c), + std::begin(dest_sol)); + + bool equality = std::equal( + std::begin(dest_res), get<1>(result).base(), + std::begin(dest_sol), solution); + + HPX_TEST(equality); + } + + // Test projection. + { + typedef test::test_iterator iterator; + + DataType val; + auto result = hpx::parallel::unique_copy(policy, + iterator(boost::begin(c)), iterator(boost::end(c)), + iterator(boost::begin(dest_res)), + [](DataType const& a, DataType const& b) -> bool { + return a == b; + }, + [&val](DataType const&) -> DataType { + // This is projection. + return val; + }); + + auto dist = std::distance(std::begin(dest_res), get<1>(result).base()); + HPX_TEST_EQ(dist, 1); + } + + // Test sequential_unique_copy with input_iterator_tag. + { + typedef test::test_iterator iterator; + + auto result = hpx::parallel::v1::detail::sequential_unique_copy( + iterator(boost::begin(c)), iterator(boost::end(c)), + std::begin(dest_res), + [](DataType const& a, DataType const& b) -> bool { + return a == b; + }, + [](DataType& t) -> DataType& { + return t; + }, + std::false_type()); + auto solution = std::unique_copy(std::begin(c), std::end(c), + std::begin(dest_sol)); + + bool equality = std::equal( + std::begin(dest_res), get<1>(result), + std::begin(dest_sol), solution); + + HPX_TEST(equality); + } +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_unique_copy() +{ + using namespace hpx::parallel; + + int rand_base = std::rand(); + + ////////// Test cases for 'int' type. + test_unique_copy(execution::seq, IteratorTag(), int(), + [](const int a, const int b) -> bool { + return a == b; + }, rand_base); + test_unique_copy(execution::par, IteratorTag(), int(), + [rand_base](const int a, const int b) -> bool { + return a == b && b == rand_base; + }, rand_base); + test_unique_copy(execution::par_unseq, IteratorTag(), int(), + [](const int a, const int b) -> bool { + return a == b; + }, rand_base); + + ////////// Test cases for user defined type. + test_unique_copy(execution::seq, IteratorTag(), user_defined_type(), + [](user_defined_type const& a, user_defined_type const& b) -> bool { + return a == b; + }, rand_base); + test_unique_copy(execution::par, IteratorTag(), user_defined_type(), + [](user_defined_type const& a, user_defined_type const& b) -> bool { + return a == b; + }, rand_base); + test_unique_copy(execution::par_unseq, IteratorTag(), user_defined_type(), + [rand_base](user_defined_type const& a, user_defined_type const& b) -> bool { + return a == b && b == rand_base; + }, rand_base); + + ////////// Asynchronous test cases for 'int' type. + test_unique_copy_async(execution::seq(execution::task), IteratorTag(), int(), + [rand_base](const int a, const int b) -> bool { + return a == b && b == rand_base; + }, rand_base); + test_unique_copy_async(execution::par(execution::task), IteratorTag(), int(), + [](const int a, const int b) -> bool { + return a == b; + }, rand_base); + + ////////// Asynchronous test cases for user defined type. + test_unique_copy_async(execution::seq(execution::task), IteratorTag(), + user_defined_type(), + [](user_defined_type const& a, user_defined_type const& b) -> bool { + return a == b; + }, rand_base); + test_unique_copy_async(execution::par(execution::task), IteratorTag(), + user_defined_type(), + [rand_base](user_defined_type const& a, user_defined_type const& b) -> bool { + return a == rand_base && b == rand_base; + }, rand_base); + + ////////// Corner test cases. + test_unique_copy(execution::par, IteratorTag(), int(), + [](const int, const int) -> bool { + return true; + }, rand_base); + test_unique_copy(execution::par_unseq, IteratorTag(), user_defined_type(), + [](user_defined_type const&, user_defined_type const&) -> bool { + return false; + }, rand_base); + + ////////// Another test cases for justifying the implementation. + test_unique_copy_etc(execution::seq, IteratorTag(), + user_defined_type(), rand_base); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_unique_copy_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_unique_copy_exception(execution::seq, IteratorTag()); + test_unique_copy_exception(execution::par, IteratorTag()); + + test_unique_copy_exception_async(execution::seq(execution::task), + IteratorTag()); + test_unique_copy_exception_async(execution::par(execution::task), + IteratorTag()); +} + +/////////////////////////////////////////////////////////////////////////////// +template +void test_unique_copy_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_unique_copy_bad_alloc(execution::seq, IteratorTag()); + test_unique_copy_bad_alloc(execution::par, IteratorTag()); + + test_unique_copy_bad_alloc_async(execution::seq(execution::task), + IteratorTag()); + test_unique_copy_bad_alloc_async(execution::par(execution::task), + IteratorTag()); +} + +#endif From a07d56c3f2e3d17f671a49dd07930e8f4b6520e9 Mon Sep 17 00:00:00 2001 From: Taeguk Kwon Date: Tue, 11 Jul 2017 22:08:42 +0900 Subject: [PATCH 3/8] Add benchmark codes for parallel::unique_copy. --- .../parallel_algorithms/local/CMakeLists.txt | 1 + .../local/benchmark_unique_copy.cpp | 223 ++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp diff --git a/tests/performance/parallel_algorithms/local/CMakeLists.txt b/tests/performance/parallel_algorithms/local/CMakeLists.txt index f77840ab23b0..f96a6d467303 100644 --- a/tests/performance/parallel_algorithms/local/CMakeLists.txt +++ b/tests/performance/parallel_algorithms/local/CMakeLists.txt @@ -7,6 +7,7 @@ set(benchmarks benchmark_is_heap benchmark_is_heap_until benchmark_partition_copy + benchmark_unique_copy ) foreach(benchmark ${benchmarks}) diff --git a/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp b/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp new file mode 100644 index 000000000000..0b2cdbab81db --- /dev/null +++ b/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp @@ -0,0 +1,223 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2017 Taeguk Kwon +// +// 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 "test_utils.hpp" + +/////////////////////////////////////////////////////////////////////////////// +struct random_fill +{ + random_fill(std::size_t random_range) + : gen(std::rand()), + dist(0, random_range - 1) + {} + + int operator()() + { + return dist(gen); + } + + boost::random::mt19937 gen; + boost::random::uniform_int_distribution<> dist; +}; + +/////////////////////////////////////////////////////////////////////////////// +template +double run_unique_copy_benchmark_std(std::size_t test_count, + InIter first, InIter last, OutIter dest) +{ + std::uint64_t time = hpx::util::high_resolution_clock::now(); + + for (int i = 0; i < test_count; ++i) + { + std::unique_copy(first, last, dest); + } + + time = hpx::util::high_resolution_clock::now() - time; + + return (time * 1e-9) / test_count; +} + +/////////////////////////////////////////////////////////////////////////////// +template +double run_unique_copy_benchmark_hpx(std::size_t test_count, ExPolicy policy, + FwdIter1 first, FwdIter1 last, FwdIter2 dest) +{ + std::uint64_t time = hpx::util::high_resolution_clock::now(); + + for (int i = 0; i < test_count; ++i) + { + hpx::parallel::unique_copy(policy, first, last, dest); + } + + time = hpx::util::high_resolution_clock::now() - time; + + return (time * 1e-9) / test_count; +} + +/////////////////////////////////////////////////////////////////////////////// +template +void run_benchmark(std::size_t vector_size, std::size_t random_range, + std::size_t test_count, IteratorTag) +{ + std::cout << "* Preparing Benchmark..." << std::endl; + + typedef typename std::vector::iterator base_iterator; + typedef test::test_iterator iterator; + + std::vector v(vector_size); + std::vector result(v.size()); + iterator first = iterator(boost::begin(v)); + iterator last = iterator(boost::end(v)); + iterator dest = iterator(boost::begin(result)); + + // initialize data + using namespace hpx::parallel; + generate(execution::par, std::begin(v), std::end(v), + random_fill(random_range)); + + auto dest_dist = std::distance(std::begin(result), + std::unique_copy(first, last, dest).base()); + + std::cout << "*** Destination iterator distance : " + << dest_dist << std::endl << std::endl; + + std::cout << "* Running Benchmark..." << std::endl; + std::cout << "--- run_unique_copy_benchmark_std ---" << std::endl; + double time_std = + run_unique_copy_benchmark_std(test_count, + first, last, dest); + + std::cout << "--- run_unique_copy_benchmark_seq ---" << std::endl; + double time_seq = + run_unique_copy_benchmark_hpx(test_count, execution::seq, + first, last, dest); + + std::cout << "--- run_unique_copy_benchmark_par ---" << std::endl; + double time_par = + run_unique_copy_benchmark_hpx(test_count, execution::par, + first, last, dest); + + std::cout << "--- run_unique_copy_benchmark_par_unseq ---" << std::endl; + double time_par_unseq = + run_unique_copy_benchmark_hpx(test_count, execution::par_unseq, + first, last, dest); + + std::cout << "\n-------------- Benchmark Result --------------" << std::endl; + auto fmt = "unique_copy (%1%) : %2%(sec)"; + std::cout << (boost::format(fmt) % "std" % time_std) << std::endl; + std::cout << (boost::format(fmt) % "seq" % time_seq) << std::endl; + std::cout << (boost::format(fmt) % "par" % time_par) << std::endl; + std::cout << (boost::format(fmt) % "par_unseq" % time_par_unseq) << std::endl; + std::cout << "----------------------------------------------" << std::endl; +} + +/////////////////////////////////////////////////////////////////////////////// +std::string correct_iterator_tag_str(std::string iterator_tag) +{ + if (iterator_tag != "random" && + iterator_tag != "bidirectional" && + iterator_tag != "forward") + return "random"; + else + return iterator_tag; +} + +/////////////////////////////////////////////////////////////////////////////// +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::srand(seed); + + // pull values from cmd + std::size_t vector_size = vm["vector_size"].as(); + std::size_t random_range = vm["random_range"].as(); + std::size_t test_count = vm["test_count"].as(); + std::string iterator_tag_str = correct_iterator_tag_str( + vm["iterator_tag"].as()); + + std::size_t const os_threads = hpx::get_os_thread_count(); + + if (random_range < 1) + random_range = 1; + + std::cout << "-------------- Benchmark Config --------------" << std::endl; + std::cout << "seed : " << seed << std::endl; + std::cout << "vector_size : " << vector_size << std::endl; + std::cout << "random_range : " << random_range << std::endl; + std::cout << "iterator_tag : " << iterator_tag_str << std::endl; + std::cout << "test_count : " << test_count << std::endl; + std::cout << "os threads : " << os_threads << std::endl; + std::cout << "----------------------------------------------\n" << std::endl; + + if (iterator_tag_str == "random") + run_benchmark(vector_size, test_count, random_range, + std::random_access_iterator_tag()); + else if (iterator_tag_str == "bidirectional") + run_benchmark(vector_size, test_count, random_range, + std::bidirectional_iterator_tag()); + else // forward + run_benchmark(vector_size, test_count, random_range, + std::forward_iterator_tag()); + + return hpx::finalize(); +} + +int main(int argc, char* argv[]) +{ + using namespace boost::program_options; + options_description desc_commandline( + "usage: " HPX_APPLICATION_STRING " [options]"); + + desc_commandline.add_options() + ("vector_size", + boost::program_options::value()->default_value(1000000), + "size of vector (default: 1000000)") + ("random_range", + boost::program_options::value()->default_value(6), + "range of random numbers [0, x) (default: 6)") + ("iterator_tag", + boost::program_options::value()->default_value("random"), + "the kind of iterator tag (random/bidirectional/forward)") + ("test_count", + boost::program_options::value()->default_value(10), + "number of tests to be averaged (default: 10)") + ("seed,s", boost::program_options::value(), + "the random number generator seed to use for this run") + ; + + // initialize program + 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(); +} From 0f0f720129920fdeee4f6628d304a96e79035179 Mon Sep 17 00:00:00 2001 From: Taeguk Kwon Date: Tue, 11 Jul 2017 23:40:59 +0900 Subject: [PATCH 4/8] Add container version of parallel::unique_copy for Ranges TS (#1668). --- docs/CMakeLists.txt | 1 + hpx/include/parallel_unique.hpp | 1 + hpx/parallel/algorithms/unique.hpp | 2 +- .../container_algorithms/partition.hpp | 10 +- hpx/parallel/container_algorithms/unique.hpp | 134 +++++++++ .../container_algorithms/CMakeLists.txt | 1 + .../unique_copy_range.cpp | 275 ++++++++++++++++++ 7 files changed, 418 insertions(+), 6 deletions(-) create mode 100644 hpx/parallel/container_algorithms/unique.hpp create mode 100644 tests/unit/parallel/container_algorithms/unique_copy_range.cpp diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index d7b383179d78..1d69fa9dc96a 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -119,6 +119,7 @@ set(doxygen_dependencies "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/rotate.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/sort.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/transform.hpp" + "${PROJECT_SOURCE_DIR}/hpx/parallel/container_algorithms/unique.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/executors/auto_chunk_size.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/executors/dynamic_chunk_size.hpp" "${PROJECT_SOURCE_DIR}/hpx/parallel/executors/execution_fwd.hpp" diff --git a/hpx/include/parallel_unique.hpp b/hpx/include/parallel_unique.hpp index f37c498760bb..1a917168b1a2 100644 --- a/hpx/include/parallel_unique.hpp +++ b/hpx/include/parallel_unique.hpp @@ -7,6 +7,7 @@ #define HPX_PARALLEL_UNIQUE_JUL_07_2017_1631PM #include +#include #endif diff --git a/hpx/parallel/algorithms/unique.hpp b/hpx/parallel/algorithms/unique.hpp index 61c16bb1ed13..fba0b49e4a28 100644 --- a/hpx/parallel/algorithms/unique.hpp +++ b/hpx/parallel/algorithms/unique.hpp @@ -271,7 +271,7 @@ namespace hpx { namespace parallel { inline namespace v1 /// \param dest Refers to the beginning of the destination range. /// \param pred Specifies the function (or function object) which /// will be invoked for each of the elements in the - /// sequence specified by [first, last).This is an + /// sequence specified by [first, last). This is an /// binary predicate which returns \a true for the /// required elements. The signature of this predicate /// should be equivalent to: diff --git a/hpx/parallel/container_algorithms/partition.hpp b/hpx/parallel/container_algorithms/partition.hpp index 178483428a01..c2a63d1aff51 100644 --- a/hpx/parallel/container_algorithms/partition.hpp +++ b/hpx/parallel/container_algorithms/partition.hpp @@ -35,7 +35,7 @@ namespace hpx { namespace parallel { inline namespace v1 /// The order of the elements is preserved. /// /// \note Complexity: Performs not more than N assignments, - /// exactly N applications of the predicate \a f, + /// exactly N applications of the predicate \a pred, /// where N = std::distance(begin(rng), end(rng)). /// /// \tparam ExPolicy The type of the execution policy to use (deduced). @@ -54,7 +54,7 @@ namespace hpx { namespace parallel { inline namespace v1 /// destination range for the elements that don't satisfy /// the predicate \a pred (deduced). /// This iterator type must meet the requirements of an - /// output iterator. + /// forward iterator. /// \tparam Pred The type of the function/function object to use /// (deduced). Unlike its sequential form, the parallel /// overload of \a partition_copy requires \a Pred to meet @@ -105,9 +105,9 @@ namespace hpx { namespace parallel { inline namespace v1 /// \a tagged_tuple /// otherwise. /// The \a partition_copy algorithm returns the tuple of - /// the input iterator \a last, - /// the output iterator to the end of the \a dest_true range, and - /// the output iterator to the end of the \a dest_false range. + /// the source iterator \a last, + /// the destination iterator to the end of the \a dest_true range, and + /// the destination iterator to the end of the \a dest_false range. /// template +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +namespace hpx { namespace parallel { inline namespace v1 +{ + /// Copies the elements from the range \a rng, + /// to another range beginning at \a dest in such a way that + /// there are no consecutive equal elements. Only the first element of + /// each group of equal elements is copied. + /// + /// \note Complexity: Performs not more than N assignments, + /// exactly N applications of the predicate \a pred, + /// where N = std::distance(begin(rng), end(rng)). + /// + /// \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 forward iterator. + /// \tparam FwdIter2 The type of the iterator representing the + /// destination range (deduced). + /// This iterator type must meet the requirements of an + /// forward iterator. + /// \tparam Pred The type of the function/function object to use + /// (deduced). Unlike its sequential form, the parallel + /// overload of \a unique_copy requires \a Pred to meet the + /// requirements of \a CopyConstructible. This defaults + /// to std::equal_to<> + /// \tparam Proj The type of an optional projection function. This + /// defaults to \a util::projection_identity + /// + /// \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. + /// \param pred Specifies the function (or function object) which + /// will be invoked for each of the elements in the + /// sequence specified by the range \a rng. This is an + /// binary predicate which returns \a true for the + /// required elements. The signature of this predicate + /// should be equivalent to: + /// \code + /// bool pred(const Type &a, const Type &b); + /// \endcode \n + /// The signature does not need to have const&, but + /// the function must not modify the objects passed to + /// it. The type \a Type must be such that an object of + /// type \a FwdIter1 can be dereferenced and then + /// implicitly converted to \a Type. + /// \param proj Specifies the function (or function object) which + /// will be invoked for each of the elements as a + /// projection operation before the actual predicate + /// \a is invoked. + /// + /// The assignments in the parallel \a unique_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 unique_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 unique_copy algorithm returns a + /// \a hpx::future > + /// if the execution policy is of type + /// \a sequenced_task_policy or + /// \a parallel_task_policy and + /// returns \a tagged_pair + /// otherwise. + /// The \a unique_copy algorithm returns the pair of + /// the source iterator to \a last, and + /// the destination iterator to the end of the \a dest range. + /// + template ::value && + hpx::traits::is_range::value && + hpx::traits::is_iterator::value && + traits::is_projected_range::value && + traits::is_indirect_callable< + ExPolicy, Pred, + traits::projected_range, + traits::projected_range + >::value)> + typename util::detail::algorithm_result< + ExPolicy, + hpx::util::tagged_pair< + tag::in(typename hpx::traits::range_traits::iterator_type), + tag::out(FwdIter2)> + >::type + unique_copy(ExPolicy && policy, Rng && rng, + FwdIter2 dest, Pred && pred = Pred(), + Proj && proj = Proj()) + { + return unique_copy(std::forward(policy), + std::begin(rng), std::end(rng), dest, + std::forward(pred), + std::forward(proj)); + } + /// \endcond +}}} + +#endif diff --git a/tests/unit/parallel/container_algorithms/CMakeLists.txt b/tests/unit/parallel/container_algorithms/CMakeLists.txt index b1e49754c61c..7f1c8262bbd4 100644 --- a/tests/unit/parallel/container_algorithms/CMakeLists.txt +++ b/tests/unit/parallel/container_algorithms/CMakeLists.txt @@ -27,6 +27,7 @@ set(tests transform_range transform_range_binary transform_range_binary2 + unique_copy_range ) foreach(test ${tests}) diff --git a/tests/unit/parallel/container_algorithms/unique_copy_range.cpp b/tests/unit/parallel/container_algorithms/unique_copy_range.cpp new file mode 100644 index 000000000000..3fb1098af33c --- /dev/null +++ b/tests/unit/parallel/container_algorithms/unique_copy_range.cpp @@ -0,0 +1,275 @@ +// Copyright (c) 2017 Taeguk Kwon +// +// 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" + +//////////////////////////////////////////////////////////////////////////// +struct user_defined_type +{ + user_defined_type() = default; + user_defined_type(int rand_no) + : val(rand_no), + name(name_list[std::rand() % name_list.size()]) + {} + + bool operator<(int rand_base) const + { + static std::string const base_name = "BASE"; + + if (this->name < base_name) + return true; + else if (this->name > base_name) + return false; + else + return this->val < rand_base; + } + + bool operator==(user_defined_type const& t) const + { + return this->name == t.name && this->val == t.val; + } + + struct user_defined_type& operator++() { return *this; }; + + static const std::vector name_list; + + int val; + std::string name; +}; + +const std::vector user_defined_type::name_list{ + "ABB", "ABC", "ACB", "BASE", "CAA", "CAAA", "CAAB" +}; + +struct random_fill +{ + random_fill(int rand_base, int range) + : gen(std::rand()), + dist(rand_base - range / 2, rand_base + range / 2) + {} + + int operator()() + { + return dist(gen); + } + + boost::random::mt19937 gen; + boost::random::uniform_int_distribution<> dist; +}; + +//////////////////////////////////////////////////////////////////////////// +template +void test_unique_copy(ExPolicy policy, DataType) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + using hpx::util::get; + + std::size_t const size = 10007; + std::vector c(size), dest_res(size), dest_sol(size); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + + auto result = hpx::parallel::unique_copy(policy, + c, std::begin(dest_res)); + auto solution = std::unique_copy(std::begin(c), std::end(c), + std::begin(dest_sol)); + + HPX_TEST(get<0>(result) == std::end(c)); + + bool equality = std::equal( + std::begin(dest_res), get<1>(result), + std::begin(dest_sol), solution); + + HPX_TEST(equality); +} + +template +void test_unique_copy_async(ExPolicy policy, DataType) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + using hpx::util::get; + + std::size_t const size = 10007; + std::vector c(size), dest_res(size), dest_sol(size); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + + auto f = hpx::parallel::unique_copy(policy, + c, std::begin(dest_res)); + auto result = f.get(); + auto solution = std::unique_copy(std::begin(c), std::end(c), + std::begin(dest_sol)); + + HPX_TEST(get<0>(result) == std::end(c)); + + bool equality = std::equal( + std::begin(dest_res), get<1>(result), + std::begin(dest_sol), solution); + + HPX_TEST(equality); +} + +#if defined(HPX_HAVE_ALGORITHM_INPUT_ITERATOR_SUPPORT) +template +void test_unique_copy_outiter(ExPolicy policy, DataType) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + using hpx::util::get; + + std::size_t const size = 10007; + std::vector c(size), dest_res(0), dest_sol(0); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + + auto result = hpx::parallel::unique_copy(policy, + c, std::back_inserter(dest_res)); + auto solution = std::unique_copy(std::begin(c), std::end(c), + std::back_inserter(dest_sol)); + + HPX_TEST(get<0>(result) == std::end(c)); + + bool equality = std::equal( + std::begin(dest_res), std::end(dest_res), + std::begin(dest_sol), std::end(dest_sol)); + + HPX_TEST(equality); +} + +template +void test_unique_copy_outiter_async(ExPolicy policy, DataType) +{ + static_assert( + hpx::parallel::execution::is_execution_policy::value, + "hpx::parallel::execution::is_execution_policy::value"); + + using hpx::util::get; + + std::size_t const size = 10007; + std::vector c(size), dest_res(0), dest_sol(0); + std::generate(std::begin(c), std::end(c), random_fill(0, 6)); + + auto f = hpx::parallel::unique_copy(policy, + c, std::back_inserter(dest_res)); + auto result = f.get(); + auto solution = std::unique_copy(std::begin(c), std::end(c), + std::back_inserter(dest_sol)); + + HPX_TEST(get<0>(result) == std::end(c)); + + bool equality = std::equal( + std::begin(dest_res), std::end(dest_res), + std::begin(dest_sol), std::end(dest_sol)); + + HPX_TEST(equality); +} +#endif + +template +void test_unique_copy() +{ + using namespace hpx::parallel; + + test_unique_copy(execution::seq, DataType()); + test_unique_copy(execution::par, DataType()); + test_unique_copy(execution::par_unseq, DataType()); + + test_unique_copy_async(execution::seq(execution::task), DataType()); + test_unique_copy_async(execution::par(execution::task), DataType()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_unique_copy(execution_policy(execution::seq), DataType()); + test_unique_copy(execution_policy(execution::par), DataType()); + test_unique_copy(execution_policy(execution::par_unseq), DataType()); + + test_unique_copy(execution_policy(execution::seq(execution::task)), + DataType()); + test_unique_copy(execution_policy(execution::par(execution::task)), + DataType()); +#endif + +#if defined(HPX_HAVE_ALGORITHM_INPUT_ITERATOR_SUPPORT) + test_unique_copy_outiter(execution::seq, DataType()); + test_unique_copy_outiter(execution::par, DataType()); + test_unique_copy_outiter(execution::par_unseq, DataType()); + + test_unique_copy_outiter_async(execution::seq(execution::task), DataType()); + test_unique_copy_outiter_async(execution::par(execution::task), DataType()); + +#if defined(HPX_HAVE_GENERIC_EXECUTION_POLICY) + test_unique_copy_outiter(execution_policy(execution::seq), DataType()); + test_unique_copy_outiter(execution_policy(execution::par), DataType()); + test_unique_copy_outiter(execution_policy(execution::par_unseq), DataType()); + + test_unique_copy_outiter(execution_policy(execution::seq(execution::task)), + DataType()); + test_unique_copy_outiter(execution_policy(execution::par(execution::task)), + DataType()); +#endif +#endif +} + +void test_unique_copy() +{ + test_unique_copy(); + test_unique_copy(); +} + +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); + + test_unique_copy(); + 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(); +} From 7bc3228f719040bbb7010fbdc5cf72df9564dfa3 Mon Sep 17 00:00:00 2001 From: Taeguk Kwon Date: Tue, 11 Jul 2017 23:50:59 +0900 Subject: [PATCH 5/8] Use std::begin instead of boost::begin in some parallel algorithms. --- .../local/benchmark_partition_copy.cpp | 8 ++--- .../local/benchmark_unique_copy.cpp | 6 ++-- tests/unit/parallel/algorithms/is_heap.cpp | 3 -- .../parallel/algorithms/is_heap_until.cpp | 3 -- .../unit/parallel/algorithms/unique_copy.cpp | 3 -- .../parallel/algorithms/unique_copy_tests.hpp | 35 +++++++++---------- 6 files changed, 24 insertions(+), 34 deletions(-) diff --git a/tests/performance/parallel_algorithms/local/benchmark_partition_copy.cpp b/tests/performance/parallel_algorithms/local/benchmark_partition_copy.cpp index 7d62f8a4b51d..394dd2008771 100644 --- a/tests/performance/parallel_algorithms/local/benchmark_partition_copy.cpp +++ b/tests/performance/parallel_algorithms/local/benchmark_partition_copy.cpp @@ -96,10 +96,10 @@ void run_benchmark(std::size_t vector_size, int test_count, IteratorTag) std::vector v(vector_size); std::vector result_true(v.size()), result_false(v.size()); - iterator first = iterator(boost::begin(v)); - iterator last = iterator(boost::end(v)); - iterator dest_true = iterator(boost::begin(result_true)); - iterator dest_false = iterator(boost::begin(result_false)); + iterator first = iterator(std::begin(v)); + iterator last = iterator(std::end(v)); + iterator dest_true = iterator(std::begin(result_true)); + iterator dest_false = iterator(std::begin(result_false)); // initialize data using namespace hpx::parallel; diff --git a/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp b/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp index 0b2cdbab81db..0dbf3bf801f2 100644 --- a/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp +++ b/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp @@ -88,9 +88,9 @@ void run_benchmark(std::size_t vector_size, std::size_t random_range, std::vector v(vector_size); std::vector result(v.size()); - iterator first = iterator(boost::begin(v)); - iterator last = iterator(boost::end(v)); - iterator dest = iterator(boost::begin(result)); + iterator first = iterator(std::begin(v)); + iterator last = iterator(std::end(v)); + iterator dest = iterator(std::begin(result)); // initialize data using namespace hpx::parallel; diff --git a/tests/unit/parallel/algorithms/is_heap.cpp b/tests/unit/parallel/algorithms/is_heap.cpp index 4da94a6bbadf..44d6ed717ac8 100644 --- a/tests/unit/parallel/algorithms/is_heap.cpp +++ b/tests/unit/parallel/algorithms/is_heap.cpp @@ -5,11 +5,8 @@ #include #include -#include #include -#include - #include #include #include diff --git a/tests/unit/parallel/algorithms/is_heap_until.cpp b/tests/unit/parallel/algorithms/is_heap_until.cpp index e5b83896524d..608bd79f6da4 100644 --- a/tests/unit/parallel/algorithms/is_heap_until.cpp +++ b/tests/unit/parallel/algorithms/is_heap_until.cpp @@ -5,11 +5,8 @@ #include #include -#include #include -#include - #include #include #include diff --git a/tests/unit/parallel/algorithms/unique_copy.cpp b/tests/unit/parallel/algorithms/unique_copy.cpp index 66705c3ee8a4..60c9f9093bbd 100644 --- a/tests/unit/parallel/algorithms/unique_copy.cpp +++ b/tests/unit/parallel/algorithms/unique_copy.cpp @@ -5,11 +5,8 @@ #include #include -#include #include -#include - #include #include #include diff --git a/tests/unit/parallel/algorithms/unique_copy_tests.hpp b/tests/unit/parallel/algorithms/unique_copy_tests.hpp index 0eb5185d9de9..61e94c5045d5 100644 --- a/tests/unit/parallel/algorithms/unique_copy_tests.hpp +++ b/tests/unit/parallel/algorithms/unique_copy_tests.hpp @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -122,8 +121,8 @@ void test_unique_copy(ExPolicy policy, IteratorTag, DataType, Pred pred, std::generate(std::begin(c), std::end(c), random_fill(rand_base, 6)); auto result = hpx::parallel::unique_copy(policy, - iterator(boost::begin(c)), iterator(boost::end(c)), - iterator(boost::begin(dest_res)), + iterator(std::begin(c)), iterator(std::end(c)), + iterator(std::begin(dest_res)), pred); auto solution = std::unique_copy(std::begin(c), std::end(c), std::begin(dest_sol), @@ -154,8 +153,8 @@ void test_unique_copy_async(ExPolicy policy, IteratorTag, DataType, Pred pred, std::generate(std::begin(c), std::end(c), random_fill(rand_base, 6)); auto f = hpx::parallel::unique_copy(policy, - iterator(boost::begin(c)), iterator(boost::end(c)), - iterator(boost::begin(dest_res)), + iterator(std::begin(c)), iterator(std::end(c)), + iterator(std::begin(dest_res)), pred); auto result = f.get(); auto solution = std::unique_copy(std::begin(c), std::end(c), @@ -187,8 +186,8 @@ void test_unique_copy_exception(ExPolicy policy, IteratorTag) bool caught_exception = false; try { auto result = hpx::parallel::unique_copy(policy, - iterator(boost::begin(c)), iterator(boost::end(c)), - iterator(boost::begin(dest)), + iterator(std::begin(c)), iterator(std::end(c)), + iterator(std::begin(dest)), throw_always()); HPX_TEST(false); @@ -218,8 +217,8 @@ void test_unique_copy_exception_async(ExPolicy policy, IteratorTag) bool returned_from_algorithm = false; try { auto f = hpx::parallel::unique_copy(policy, - iterator(boost::begin(c)), iterator(boost::end(c)), - iterator(boost::begin(dest)), + iterator(std::begin(c)), iterator(std::end(c)), + iterator(std::begin(dest)), throw_always()); returned_from_algorithm = true; f.get(); @@ -256,8 +255,8 @@ void test_unique_copy_bad_alloc(ExPolicy policy, IteratorTag) bool caught_bad_alloc = false; try { auto result = hpx::parallel::unique_copy(policy, - iterator(boost::begin(c)), iterator(boost::end(c)), - iterator(boost::begin(dest)), + iterator(std::begin(c)), iterator(std::end(c)), + iterator(std::begin(dest)), throw_bad_alloc()); HPX_TEST(false); @@ -286,8 +285,8 @@ void test_unique_copy_bad_alloc_async(ExPolicy policy, IteratorTag) bool returned_from_algorithm = false; try { auto f = hpx::parallel::unique_copy(policy, - iterator(boost::begin(c)), iterator(boost::end(c)), - iterator(boost::begin(dest)), + iterator(std::begin(c)), iterator(std::end(c)), + iterator(std::begin(dest)), throw_bad_alloc()); returned_from_algorithm = true; f.get(); @@ -327,8 +326,8 @@ void test_unique_copy_etc(ExPolicy policy, IteratorTag, typedef test::test_iterator iterator; auto result = hpx::parallel::unique_copy(policy, - iterator(boost::begin(c)), iterator(boost::end(c)), - iterator(boost::begin(dest_res))); + iterator(std::begin(c)), iterator(std::end(c)), + iterator(std::begin(dest_res))); auto solution = std::unique_copy(std::begin(c), std::end(c), std::begin(dest_sol)); @@ -345,8 +344,8 @@ void test_unique_copy_etc(ExPolicy policy, IteratorTag, DataType val; auto result = hpx::parallel::unique_copy(policy, - iterator(boost::begin(c)), iterator(boost::end(c)), - iterator(boost::begin(dest_res)), + iterator(std::begin(c)), iterator(std::end(c)), + iterator(std::begin(dest_res)), [](DataType const& a, DataType const& b) -> bool { return a == b; }, @@ -365,7 +364,7 @@ void test_unique_copy_etc(ExPolicy policy, IteratorTag, std::input_iterator_tag> iterator; auto result = hpx::parallel::v1::detail::sequential_unique_copy( - iterator(boost::begin(c)), iterator(boost::end(c)), + iterator(std::begin(c)), iterator(std::end(c)), std::begin(dest_res), [](DataType const& a, DataType const& b) -> bool { return a == b; From a5c8cd9aa7ba9c4e139ca932c8cad8e57cab77e7 Mon Sep 17 00:00:00 2001 From: Taeguk Kwon Date: Tue, 11 Jul 2017 10:03:18 -0500 Subject: [PATCH 6/8] Fix compile errors in gcc. --- .../parallel_algorithms/local/benchmark_unique_copy.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp b/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp index 0dbf3bf801f2..884dc55a7e43 100644 --- a/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp +++ b/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp @@ -44,7 +44,7 @@ struct random_fill /////////////////////////////////////////////////////////////////////////////// template -double run_unique_copy_benchmark_std(std::size_t test_count, +double run_unique_copy_benchmark_std(int test_count, InIter first, InIter last, OutIter dest) { std::uint64_t time = hpx::util::high_resolution_clock::now(); @@ -61,7 +61,7 @@ double run_unique_copy_benchmark_std(std::size_t test_count, /////////////////////////////////////////////////////////////////////////////// template -double run_unique_copy_benchmark_hpx(std::size_t test_count, ExPolicy policy, +double run_unique_copy_benchmark_hpx(int test_count, ExPolicy policy, FwdIter1 first, FwdIter1 last, FwdIter2 dest) { std::uint64_t time = hpx::util::high_resolution_clock::now(); @@ -79,7 +79,7 @@ double run_unique_copy_benchmark_hpx(std::size_t test_count, ExPolicy policy, /////////////////////////////////////////////////////////////////////////////// template void run_benchmark(std::size_t vector_size, std::size_t random_range, - std::size_t test_count, IteratorTag) + int test_count, IteratorTag) { std::cout << "* Preparing Benchmark..." << std::endl; @@ -156,7 +156,7 @@ int hpx_main(boost::program_options::variables_map& vm) // pull values from cmd std::size_t vector_size = vm["vector_size"].as(); std::size_t random_range = vm["random_range"].as(); - std::size_t test_count = vm["test_count"].as(); + int test_count = vm["test_count"].as(); std::string iterator_tag_str = correct_iterator_tag_str( vm["iterator_tag"].as()); From 3296cf1f3523caf7d6c5e0ebdd64b4b7856511b3 Mon Sep 17 00:00:00 2001 From: Taeguk Kwon Date: Tue, 11 Jul 2017 10:26:37 -0500 Subject: [PATCH 7/8] Fix my mistake in benchmark of parallel::unique_copy. --- .../parallel_algorithms/local/benchmark_unique_copy.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp b/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp index 884dc55a7e43..02b80bc45c17 100644 --- a/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp +++ b/tests/performance/parallel_algorithms/local/benchmark_unique_copy.cpp @@ -78,8 +78,8 @@ double run_unique_copy_benchmark_hpx(int test_count, ExPolicy policy, /////////////////////////////////////////////////////////////////////////////// template -void run_benchmark(std::size_t vector_size, std::size_t random_range, - int test_count, IteratorTag) +void run_benchmark(std::size_t vector_size, int test_count, + std::size_t random_range, IteratorTag) { std::cout << "* Preparing Benchmark..." << std::endl; From f2ad5fa9fd9c272b9c2c50098cc642f28f46007d Mon Sep 17 00:00:00 2001 From: Taeguk Kwon Date: Wed, 12 Jul 2017 03:48:37 +0900 Subject: [PATCH 8/8] Avoid duplicated dereferencing in sequential unique_copy. --- hpx/parallel/algorithms/unique.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hpx/parallel/algorithms/unique.hpp b/hpx/parallel/algorithms/unique.hpp index fba0b49e4a28..d045d9e219b4 100644 --- a/hpx/parallel/algorithms/unique.hpp +++ b/hpx/parallel/algorithms/unique.hpp @@ -84,7 +84,7 @@ namespace hpx { namespace parallel { inline namespace v1 auto base_val = *first; - *dest++ = *first; + *dest++ = base_val; while (++first != last) { @@ -93,7 +93,7 @@ namespace hpx { namespace parallel { inline namespace v1 hpx::util::invoke(proj, *first))) { base_val = *first; - *dest++ = *first; + *dest++ = base_val; } } return std::make_pair(std::move(last), std::move(dest));