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