1 /*
2     Copyright (c) 2005-2021 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 #include "common/concepts_common.h"
19 #include <vector>
20 #include <iterator>
21 
22 //! \file test_parallel_for_each.cpp
23 //! \brief Test for [algorithms.parallel_for_each]
24 
25 //! Test forward access iterator support
26 //! \brief \ref error_guessing \ref interface
27 TEST_CASE("Forward iterator support") {
28     for ( auto concurrency_level : utils::concurrency_range() ) {
29         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
30         for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
31             g_tasks_expected = 0;
32             for (size_t i=0; i < depth; ++i)
33                 g_tasks_expected += FindNumOfTasks(g_depths[i].value());
34             TestIterator_Modifiable<utils::ForwardIterator<value_t>>(depth);
35         }
36     }
37 }
38 
39 //! Test random access iterator support
40 //! \brief \ref error_guessing \ref interface
41 TEST_CASE("Random access iterator support") {
42     for ( auto concurrency_level : utils::concurrency_range() ) {
43         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
44         for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
45             g_tasks_expected = 0;
46             for (size_t i=0; i < depth; ++i)
47                 g_tasks_expected += FindNumOfTasks(g_depths[i].value());
48             TestIterator_Modifiable<value_t*>(depth);
49         }
50     }
51 }
52 
53 //! Test const random access iterator support
54 //! \brief \ref error_guessing \ref interface
55 TEST_CASE("Const random access iterator support") {
56     for ( auto concurrency_level : utils::concurrency_range() ) {
57         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
58         for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
59             g_tasks_expected = 0;
60             for (size_t i=0; i < depth; ++i)
61                 g_tasks_expected += FindNumOfTasks(g_depths[i].value());
62             TestIterator_Const<utils::ConstRandomIterator<value_t>>(depth);
63         }
64     }
65 
66 }
67 
68 //! Test container based overload
69 //! \brief \ref error_guessing \ref interface
70 TEST_CASE("Container based overload - forward iterator based container") {
71     container_based_overload_test_case<utils::ForwardIterator>(/*expected_value*/1);
72 }
73 
74 //! Test container based overload
75 //! \brief \ref error_guessing \ref interface
76 TEST_CASE("Container based overload - random access iterator based container") {
77     container_based_overload_test_case<utils::RandomIterator>(/*expected_value*/1);
78 }
79 
80 // Test for iterators over values convertible to work item type
81 //! \brief \ref error_guessing \ref interface
82 TEST_CASE("Using with values convertible to work item type") {
83     for ( auto concurrency_level : utils::concurrency_range() ) {
84         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
85         using Iterator = size_t*;
86         for(size_t depth = 0; depth <= depths_nubmer; ++depth) {
87             g_tasks_expected = 0;
88             for (size_t i=0; i < depth; ++i)
89                 g_tasks_expected += FindNumOfTasks(g_depths[i].value());
90             // Test for iterators over values convertible to work item type
91             TestIterator_Common<Iterator>(depth);
92             TestBody<FakeTaskGeneratorBody_RvalueRefVersion, Iterator>(depth);
93             TestBody<TaskGeneratorBody_RvalueRefVersion, Iterator>(depth);
94         }
95     }
96 }
97 
98 //! Testing workers going to sleep
99 //! \brief \ref resource_usage \ref stress
100 TEST_CASE("That all workers sleep when no work") {
101     const std::size_t N = 100000;
102     std::vector<std::size_t> vec(N, 0);
103 
104     tbb::parallel_for_each(vec.begin(), vec.end(), [&](std::size_t& in) {
105         for (int i = 0; i < 1000; ++i) {
106             ++in;
107         }
108     });
109     TestCPUUserTime(utils::get_platform_max_threads());
110 }
111 
112 #if __TBB_CPP20_CONCEPTS_PRESENT
113 
114 template <typename Iterator, typename Body>
115 concept can_call_parallel_for_each_with_iterator = requires( Iterator it, const Body& body, tbb::task_group_context ctx ) {
116     tbb::parallel_for_each(it, it, body);
117     tbb::parallel_for_each(it, it, body, ctx);
118 };
119 
120 template <typename ContainerBasedSequence, typename Body>
121 concept can_call_parallel_for_each_with_cbs = requires( ContainerBasedSequence cbs,
122                                                         const ContainerBasedSequence const_cbs,
123                                                         const Body& body, tbb::task_group_context ctx ) {
124     tbb::parallel_for_each(cbs, body);
125     tbb::parallel_for_each(cbs, body, ctx);
126     tbb::parallel_for_each(const_cbs, body);
127     tbb::parallel_for_each(const_cbs, body, ctx);
128 };
129 
130 template <typename Body>
131 concept can_call_parallel_for_each =
132     can_call_parallel_for_each_with_iterator<test_concepts::container_based_sequence::iterator, Body> &&
133     can_call_parallel_for_each_with_cbs<test_concepts::container_based_sequence::Correct, Body>;
134 
135 template <typename Iterator>
136 using CorrectBody = test_concepts::parallel_for_each_body::Correct<decltype(*std::declval<Iterator>())>;
137 
138 void test_pfor_each_iterator_constraints() {
139     using CorrectIterator = typename std::vector<int>::iterator; // random_access_iterator
140     using IncorrectIterator = std::ostream_iterator<int>; // output_iterator
141     static_assert(can_call_parallel_for_each_with_iterator<CorrectIterator, CorrectBody<CorrectIterator>>);
142     static_assert(!can_call_parallel_for_each_with_iterator<IncorrectIterator, CorrectBody<IncorrectIterator>>);
143 }
144 
145 void test_pfor_each_container_based_sequence_constraints() {
146     using namespace test_concepts::container_based_sequence;
147     using iterator = test_concepts::container_based_sequence::iterator;
148     static_assert(can_call_parallel_for_each_with_cbs<Correct, CorrectBody<iterator>>);
149     static_assert(!can_call_parallel_for_each_with_cbs<NoBegin, CorrectBody<iterator>>);
150     static_assert(!can_call_parallel_for_each_with_cbs<NoEnd, CorrectBody<iterator>>);
151 }
152 
153 void test_pfor_each_body_constraints() {
154     using namespace test_concepts::parallel_for_each_body;
155     static_assert(can_call_parallel_for_each<Correct<int>>);
156     static_assert(can_call_parallel_for_each<WithFeeder<int>>);
157     static_assert(!can_call_parallel_for_each<NoOperatorRoundBrackets<int>>);
158     static_assert(!can_call_parallel_for_each<WithFeederNoOperatorRoundBrackets<int>>);
159     static_assert(!can_call_parallel_for_each<OperatorRoundBracketsNonConst<int>>);
160     static_assert(!can_call_parallel_for_each<WithFeederOperatorRoundBracketsNonConst<int>>);
161     static_assert(!can_call_parallel_for_each<WrongInputOperatorRoundBrackets<int>>);
162     static_assert(!can_call_parallel_for_each<WithFeederWrongFirstInputOperatorRoundBrackets<int>>);
163     static_assert(!can_call_parallel_for_each<WithFeederWrongSecondInputOperatorRoundBrackets<int>>);
164 }
165 
166 //! \brief \ref error_guessing
167 TEST_CASE("parallel_for_each constraints") {
168     test_pfor_each_iterator_constraints();
169     test_pfor_each_container_based_sequence_constraints();
170     test_pfor_each_body_constraints();
171 }
172 
173 #endif // __TBB_CPP20_CONCEPTS_PRESENT
174