1 /* 2 Copyright (c) 2005-2023 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #include "common/parallel_for_each_common.h" 18 19 //! \file conformance_parallel_for_each.cpp 20 //! \brief Test for [algorithms.parallel_for_each] specification 21 22 //! Test input access iterator support 23 //! \brief \ref requirement \ref interface 24 TEST_CASE("Input iterator support") { 25 for ( auto concurrency_level : utils::concurrency_range() ) { 26 oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level); 27 28 for( size_t depth = 0; depth <= depths_nubmer; ++depth ) { 29 g_tasks_expected = 0; 30 for ( size_t i=0; i < depth; ++i ) 31 g_tasks_expected += FindNumOfTasks(g_depths[i].value()); 32 TestIterator_Const<utils::InputIterator<value_t>>(depth); 33 TestIterator_Move<utils::InputIterator<value_t>>(depth); 34 #if __TBB_CPP14_GENERIC_LAMBDAS_PRESENT 35 TestGenericLambdasCommon<utils::InputIterator<value_t>>(depth); 36 #endif 37 } 38 } 39 } 40 41 //! Test container based overload 42 //! \brief \ref requirement \ref interface 43 TEST_CASE("Container based overload - input iterator based container") { 44 container_based_overload_test_case<utils::InputIterator, incremental_functor_const>(/*expected_value*/0); 45 } 46 47 const size_t elements = 10000; 48 const size_t init_sum = 0; 49 std::atomic<size_t> element_counter; 50 51 template<size_t K> 52 struct set_to { 53 void operator()(size_t& x) const { 54 x = K; 55 ++element_counter; 56 } 57 }; 58 59 #include "common/range_based_for_support.h" 60 #include <functional> 61 #include <deque> 62 63 template<typename... Context> 64 void WorkProducingTest(Context&... context) { 65 for ( auto concurrency_level : utils::concurrency_range() ) { 66 oneapi::tbb::global_control control(oneapi::tbb::global_control::max_allowed_parallelism, concurrency_level); 67 68 using namespace range_based_for_support_tests; 69 std::deque<size_t> v(elements, 0); 70 71 element_counter = 0; 72 oneapi::tbb::parallel_for_each(v.begin(), v.end(), set_to<0>(), context...); 73 REQUIRE_MESSAGE((element_counter == v.size() && element_counter == elements), 74 "not all elements were set"); 75 REQUIRE_MESSAGE(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == init_sum, 76 "elements of v not all ones"); 77 78 element_counter = 0; 79 oneapi::tbb::parallel_for_each(v, set_to<1>(), context...); 80 REQUIRE_MESSAGE((element_counter == v.size() && element_counter == elements), 81 "not all elements were set"); 82 REQUIRE_MESSAGE(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == v.size(), 83 "elements of v not all ones"); 84 85 element_counter = 0; 86 oneapi::tbb::parallel_for_each(oneapi::tbb::blocked_range<std::deque<size_t>::iterator>(v.begin(), v.end()), set_to<0>(), context...); 87 REQUIRE_MESSAGE((element_counter == v.size() && element_counter == elements), 88 "not all elements were set"); 89 REQUIRE_MESSAGE(range_based_for_accumulate(v, std::plus<size_t>(), init_sum) == init_sum, 90 "elements of v not all zeros"); 91 } 92 } 93 94 #if __TBB_CPP17_INVOKE_PRESENT 95 96 class ForEachInvokeItem { 97 public: 98 ForEachInvokeItem(std::size_t rv, std::vector<std::size_t>& cv) : real_value(rv), change_vector(cv) {} 99 100 void do_action() const { ++change_vector[real_value]; } 101 102 void do_action_and_feed(oneapi::tbb::feeder<ForEachInvokeItem>& feeder) const { 103 CHECK_MESSAGE(change_vector.size() % 2 == 0, "incorrect test setup"); 104 std::size_t shift = change_vector.size() / 2; 105 ++change_vector[real_value]; 106 if (real_value < shift) { 107 feeder.add(ForEachInvokeItem(real_value + shift, change_vector)); 108 } 109 } 110 private: 111 std::size_t real_value; 112 std::vector<std::size_t>& change_vector; 113 }; 114 115 template <template <class T> typename IteratorType> 116 void test_pfor_each_invoke_basic() { 117 const std::size_t items_count = 10; 118 std::vector<ForEachInvokeItem> items_to_proceed; 119 std::vector<std::size_t> change_vector(2 * items_count, 0); 120 121 for (std::size_t i = 0; i < items_count; ++i) { 122 items_to_proceed.emplace_back(i, change_vector); 123 } 124 125 using iterator_type = IteratorType<ForEachInvokeItem>; 126 127 // Test without feeder 128 oneapi::tbb::parallel_for_each(iterator_type(items_to_proceed.data()), 129 iterator_type(items_to_proceed.data() + items_count), 130 &ForEachInvokeItem::do_action); 131 132 for (std::size_t i = 0; i < items_count; ++i) { 133 CHECK(change_vector[i] == 1); 134 CHECK(change_vector[i + items_count] == 0); 135 change_vector[i] = 0; // reset 136 } 137 138 // Test with feeder 139 oneapi::tbb::parallel_for_each(iterator_type(items_to_proceed.data()), 140 iterator_type(items_to_proceed.data() + items_count), 141 &ForEachInvokeItem::do_action_and_feed); 142 143 for (auto item : change_vector) { 144 CHECK(item == 1); 145 } 146 } 147 148 #endif 149 150 //! Test that all elements were produced 151 //! \brief \ref requirement \ref stress 152 TEST_CASE("Test that all elements in range were produced through body (without task_group_context)") { 153 WorkProducingTest(); 154 } 155 156 //! Test that all elements were produced (with task_group_context) 157 //! \brief \ref requirement \ref interface \ref stress 158 TEST_CASE("Test that all elements in range were produced through body (with task_group_context)") { 159 oneapi::tbb::task_group_context context; 160 WorkProducingTest(context); 161 } 162 163 //! Move iterator test for class that supports both move and copy semantics 164 //! \brief \ref requirement \ref interface 165 TEST_CASE("Move Semantics Test | Item: MovePreferable") { 166 DoTestMoveSemantics<TestMoveSem::MovePreferable>(); 167 } 168 169 //! Move semantic test for move only class 170 //! \brief \ref requirement \ref interface 171 TEST_CASE("Move Semantics | Item: MoveOnly") { 172 // parallel_for_each uses is_copy_constructible to support non-copyable types 173 DoTestMoveSemantics<TestMoveSem::MoveOnly>(); 174 } 175 176 #if __TBB_CPP17_INVOKE_PRESENT 177 //! Test that parallel_for_each uses std::invoke to run the body 178 //! \brief \ref requirement 179 TEST_CASE("parallel_for_each and std::invoke") { 180 test_pfor_each_invoke_basic<utils::InputIterator>(); 181 test_pfor_each_invoke_basic<utils::ForwardIterator>(); 182 test_pfor_each_invoke_basic<utils::RandomIterator>(); 183 } 184 185 #endif 186