151c0b2f7Stbbdev /*
2*a088cfa0SKonstantin Boyarinov Copyright (c) 2005-2023 Intel Corporation
351c0b2f7Stbbdev
451c0b2f7Stbbdev Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev you may not use this file except in compliance with the License.
651c0b2f7Stbbdev You may obtain a copy of the License at
751c0b2f7Stbbdev
851c0b2f7Stbbdev http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev
1051c0b2f7Stbbdev Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev See the License for the specific language governing permissions and
1451c0b2f7Stbbdev limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev
1751c0b2f7Stbbdev #include "common/parallel_for_each_common.h"
1851c0b2f7Stbbdev
1951c0b2f7Stbbdev //! \file conformance_parallel_for_each.cpp
2051c0b2f7Stbbdev //! \brief Test for [algorithms.parallel_for_each] specification
2151c0b2f7Stbbdev
2251c0b2f7Stbbdev //! Test input access iterator support
2351c0b2f7Stbbdev //! \brief \ref requirement \ref interface
2451c0b2f7Stbbdev TEST_CASE("Input iterator support") {
2551c0b2f7Stbbdev for ( auto concurrency_level : utils::concurrency_range() ) {
2649e08aacStbbdev oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
2751c0b2f7Stbbdev
2851c0b2f7Stbbdev for( size_t depth = 0; depth <= depths_nubmer; ++depth ) {
2951c0b2f7Stbbdev g_tasks_expected = 0;
3051c0b2f7Stbbdev for ( size_t i=0; i < depth; ++i )
3151c0b2f7Stbbdev g_tasks_expected += FindNumOfTasks(g_depths[i].value());
32b15aabb3Stbbdev TestIterator_Const<utils::InputIterator<value_t>>(depth);
33b15aabb3Stbbdev TestIterator_Move<utils::InputIterator<value_t>>(depth);
34b15aabb3Stbbdev #if __TBB_CPP14_GENERIC_LAMBDAS_PRESENT
35b15aabb3Stbbdev TestGenericLambdasCommon<utils::InputIterator<value_t>>(depth);
36b15aabb3Stbbdev #endif
3751c0b2f7Stbbdev }
3851c0b2f7Stbbdev }
3951c0b2f7Stbbdev }
4051c0b2f7Stbbdev
4151c0b2f7Stbbdev //! Test container based overload
4251c0b2f7Stbbdev //! \brief \ref requirement \ref interface
4351c0b2f7Stbbdev TEST_CASE("Container based overload - input iterator based container") {
44b15aabb3Stbbdev container_based_overload_test_case<utils::InputIterator, incremental_functor_const>(/*expected_value*/0);
4551c0b2f7Stbbdev }
4651c0b2f7Stbbdev
4751c0b2f7Stbbdev const size_t elements = 10000;
4851c0b2f7Stbbdev const size_t init_sum = 0;
4951c0b2f7Stbbdev std::atomic<size_t> element_counter;
5051c0b2f7Stbbdev
5151c0b2f7Stbbdev template<size_t K>
5251c0b2f7Stbbdev struct set_to {
operator ()set_to5351c0b2f7Stbbdev void operator()(size_t& x) const {
5451c0b2f7Stbbdev x = K;
5551c0b2f7Stbbdev ++element_counter;
5651c0b2f7Stbbdev }
5751c0b2f7Stbbdev };
5851c0b2f7Stbbdev
5951c0b2f7Stbbdev #include "common/range_based_for_support.h"
6051c0b2f7Stbbdev #include <functional>
6151c0b2f7Stbbdev #include <deque>
6251c0b2f7Stbbdev
6351c0b2f7Stbbdev template<typename... Context>
WorkProducingTest(Context &...context)6451c0b2f7Stbbdev void WorkProducingTest(Context&... context) {
6551c0b2f7Stbbdev for ( auto concurrency_level : utils::concurrency_range() ) {
6649e08aacStbbdev oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level);
6751c0b2f7Stbbdev
6851c0b2f7Stbbdev using namespace range_based_for_support_tests;
6951c0b2f7Stbbdev std::deque<size_t> v(elements, 0);
7051c0b2f7Stbbdev
7151c0b2f7Stbbdev element_counter = 0;
7249e08aacStbbdev oneapi::tbb::parallel_for_each(v.begin(), v.end(), set_to<0>(), context...);
7351c0b2f7Stbbdev REQUIRE_MESSAGE((element_counter == v.size() && element_counter == elements),
7451c0b2f7Stbbdev "not all elements were set");
7551c0b2f7Stbbdev REQUIRE_MESSAGE(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == init_sum,
7651c0b2f7Stbbdev "elements of v not all ones");
7751c0b2f7Stbbdev
7851c0b2f7Stbbdev element_counter = 0;
7949e08aacStbbdev oneapi::tbb::parallel_for_each(v, set_to<1>(), context...);
8051c0b2f7Stbbdev REQUIRE_MESSAGE((element_counter == v.size() && element_counter == elements),
8151c0b2f7Stbbdev "not all elements were set");
8251c0b2f7Stbbdev REQUIRE_MESSAGE(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == v.size(),
8351c0b2f7Stbbdev "elements of v not all ones");
8451c0b2f7Stbbdev
8551c0b2f7Stbbdev element_counter = 0;
8649e08aacStbbdev oneapi::tbb::parallel_for_each(oneapi::tbb::blocked_range<std::deque<size_t>::iterator>(v.begin(), v.end()), set_to<0>(), context...);
8751c0b2f7Stbbdev REQUIRE_MESSAGE((element_counter == v.size() && element_counter == elements),
8851c0b2f7Stbbdev "not all elements were set");
8951c0b2f7Stbbdev REQUIRE_MESSAGE(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == init_sum,
9051c0b2f7Stbbdev "elements of v not all zeros");
9151c0b2f7Stbbdev }
9251c0b2f7Stbbdev }
9351c0b2f7Stbbdev
94*a088cfa0SKonstantin Boyarinov #if __TBB_CPP17_INVOKE_PRESENT
95*a088cfa0SKonstantin Boyarinov
96*a088cfa0SKonstantin Boyarinov class ForEachInvokeItem {
97*a088cfa0SKonstantin Boyarinov public:
ForEachInvokeItem(std::size_t rv,std::vector<std::size_t> & cv)98*a088cfa0SKonstantin Boyarinov ForEachInvokeItem(std::size_t rv, std::vector<std::size_t>& cv) : real_value(rv), change_vector(cv) {}
99*a088cfa0SKonstantin Boyarinov
do_action() const100*a088cfa0SKonstantin Boyarinov void do_action() const { ++change_vector[real_value]; }
101*a088cfa0SKonstantin Boyarinov
do_action_and_feed(oneapi::tbb::feeder<ForEachInvokeItem> & feeder) const102*a088cfa0SKonstantin Boyarinov void do_action_and_feed(oneapi::tbb::feeder<ForEachInvokeItem>& feeder) const {
103*a088cfa0SKonstantin Boyarinov CHECK_MESSAGE(change_vector.size() % 2 == 0, "incorrect test setup");
104*a088cfa0SKonstantin Boyarinov std::size_t shift = change_vector.size() / 2;
105*a088cfa0SKonstantin Boyarinov ++change_vector[real_value];
106*a088cfa0SKonstantin Boyarinov if (real_value < shift) {
107*a088cfa0SKonstantin Boyarinov feeder.add(ForEachInvokeItem(real_value + shift, change_vector));
108*a088cfa0SKonstantin Boyarinov }
109*a088cfa0SKonstantin Boyarinov }
110*a088cfa0SKonstantin Boyarinov private:
111*a088cfa0SKonstantin Boyarinov std::size_t real_value;
112*a088cfa0SKonstantin Boyarinov std::vector<std::size_t>& change_vector;
113*a088cfa0SKonstantin Boyarinov };
114*a088cfa0SKonstantin Boyarinov
115*a088cfa0SKonstantin Boyarinov template <template <class T> typename IteratorType>
test_pfor_each_invoke_basic()116*a088cfa0SKonstantin Boyarinov void test_pfor_each_invoke_basic() {
117*a088cfa0SKonstantin Boyarinov const std::size_t items_count = 10;
118*a088cfa0SKonstantin Boyarinov std::vector<ForEachInvokeItem> items_to_proceed;
119*a088cfa0SKonstantin Boyarinov std::vector<std::size_t> change_vector(2 * items_count, 0);
120*a088cfa0SKonstantin Boyarinov
121*a088cfa0SKonstantin Boyarinov for (std::size_t i = 0; i < items_count; ++i) {
122*a088cfa0SKonstantin Boyarinov items_to_proceed.emplace_back(i, change_vector);
123*a088cfa0SKonstantin Boyarinov }
124*a088cfa0SKonstantin Boyarinov
125*a088cfa0SKonstantin Boyarinov using iterator_type = IteratorType<ForEachInvokeItem>;
126*a088cfa0SKonstantin Boyarinov
127*a088cfa0SKonstantin Boyarinov // Test without feeder
128*a088cfa0SKonstantin Boyarinov oneapi::tbb::parallel_for_each(iterator_type(items_to_proceed.data()),
129*a088cfa0SKonstantin Boyarinov iterator_type(items_to_proceed.data() + items_count),
130*a088cfa0SKonstantin Boyarinov &ForEachInvokeItem::do_action);
131*a088cfa0SKonstantin Boyarinov
132*a088cfa0SKonstantin Boyarinov for (std::size_t i = 0; i < items_count; ++i) {
133*a088cfa0SKonstantin Boyarinov CHECK(change_vector[i] == 1);
134*a088cfa0SKonstantin Boyarinov CHECK(change_vector[i + items_count] == 0);
135*a088cfa0SKonstantin Boyarinov change_vector[i] = 0; // reset
136*a088cfa0SKonstantin Boyarinov }
137*a088cfa0SKonstantin Boyarinov
138*a088cfa0SKonstantin Boyarinov // Test with feeder
139*a088cfa0SKonstantin Boyarinov oneapi::tbb::parallel_for_each(iterator_type(items_to_proceed.data()),
140*a088cfa0SKonstantin Boyarinov iterator_type(items_to_proceed.data() + items_count),
141*a088cfa0SKonstantin Boyarinov &ForEachInvokeItem::do_action_and_feed);
142*a088cfa0SKonstantin Boyarinov
143*a088cfa0SKonstantin Boyarinov for (auto item : change_vector) {
144*a088cfa0SKonstantin Boyarinov CHECK(item == 1);
145*a088cfa0SKonstantin Boyarinov }
146*a088cfa0SKonstantin Boyarinov }
147*a088cfa0SKonstantin Boyarinov
148*a088cfa0SKonstantin Boyarinov #endif
149*a088cfa0SKonstantin Boyarinov
15051c0b2f7Stbbdev //! Test that all elements were produced
15151c0b2f7Stbbdev //! \brief \ref requirement \ref stress
15251c0b2f7Stbbdev TEST_CASE("Test that all elements in range were produced through body (without task_group_context)") {
15351c0b2f7Stbbdev WorkProducingTest();
15451c0b2f7Stbbdev }
15551c0b2f7Stbbdev
15651c0b2f7Stbbdev //! Test that all elements were produced (with task_group_context)
15751c0b2f7Stbbdev //! \brief \ref requirement \ref interface \ref stress
15851c0b2f7Stbbdev TEST_CASE("Test that all elements in range were produced through body (with task_group_context)") {
15949e08aacStbbdev oneapi::tbb::task_group_context context;
16051c0b2f7Stbbdev WorkProducingTest(context);
16151c0b2f7Stbbdev }
16251c0b2f7Stbbdev
16351c0b2f7Stbbdev //! Move iterator test for class that supports both move and copy semantics
16451c0b2f7Stbbdev //! \brief \ref requirement \ref interface
16551c0b2f7Stbbdev TEST_CASE("Move Semantics Test | Item: MovePreferable") {
16651c0b2f7Stbbdev DoTestMoveSemantics<TestMoveSem::MovePreferable>();
16751c0b2f7Stbbdev }
16851c0b2f7Stbbdev
16951c0b2f7Stbbdev //! Move semantic test for move only class
17051c0b2f7Stbbdev //! \brief \ref requirement \ref interface
17151c0b2f7Stbbdev TEST_CASE("Move Semantics | Item: MoveOnly") {
17251c0b2f7Stbbdev // parallel_for_each uses is_copy_constructible to support non-copyable types
17351c0b2f7Stbbdev DoTestMoveSemantics<TestMoveSem::MoveOnly>();
17451c0b2f7Stbbdev }
175*a088cfa0SKonstantin Boyarinov
176*a088cfa0SKonstantin Boyarinov #if __TBB_CPP17_INVOKE_PRESENT
177*a088cfa0SKonstantin Boyarinov //! Test that parallel_for_each uses std::invoke to run the body
178*a088cfa0SKonstantin Boyarinov //! \brief \ref requirement
179*a088cfa0SKonstantin Boyarinov TEST_CASE("parallel_for_each and std::invoke") {
180*a088cfa0SKonstantin Boyarinov test_pfor_each_invoke_basic<utils::InputIterator>();
181*a088cfa0SKonstantin Boyarinov test_pfor_each_invoke_basic<utils::ForwardIterator>();
182*a088cfa0SKonstantin Boyarinov test_pfor_each_invoke_basic<utils::RandomIterator>();
183*a088cfa0SKonstantin Boyarinov }
184*a088cfa0SKonstantin Boyarinov
185*a088cfa0SKonstantin Boyarinov #endif
186