/* Copyright (c) 2005-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef __TBB_test_common_parallel_for_each_common_H #define __TBB_test_common_parallel_for_each_common_H #include "config.h" #include "oneapi/tbb/parallel_for_each.h" #include "oneapi/tbb/global_control.h" #include "test.h" #include "config.h" #include "utils.h" #include "utils_report.h" #include "utils_concurrency_limit.h" #include "iterator.h" #include "cpu_usertime.h" #include #include constexpr std::size_t depths_nubmer = 20; static std::atomic g_values_counter; class value_t { size_t x; value_t& operator=(const value_t&); public: value_t(size_t xx) : x(xx) { ++g_values_counter; } value_t(const value_t& v) : x(v.x) { ++g_values_counter; } value_t(value_t&& v) : x(v.x) { ++g_values_counter; } ~value_t() { --g_values_counter; } size_t value() const volatile { return x; } }; static size_t g_tasks_expected = 0; static std::atomic g_tasks_observed; size_t FindNumOfTasks(size_t max_depth) { if( max_depth == 0 ) return 1; return max_depth * FindNumOfTasks( max_depth - 1 ) + 1; } //! Simplest form of the parallel_for_each functor object. struct FakeTaskGeneratorBody { //! The simplest form of the function call operator /** It does not allow adding new tasks during its execution. **/ void operator()(value_t depth) const { g_tasks_observed += FindNumOfTasks(depth.value()); } }; /** Work item is passed by reference here. **/ struct FakeTaskGeneratorBody_RefVersion { void operator()(value_t& depth) const { g_tasks_observed += FindNumOfTasks(depth.value()); } }; /** Work item is passed by reference to const here. **/ struct FakeTaskGeneratorBody_ConstRefVersion { void operator()(const value_t& depth) const { g_tasks_observed += FindNumOfTasks(depth.value()); } }; /** Work item is passed by reference to volatile here. **/ struct FakeTaskGeneratorBody_VolatileRefVersion { void operator()(volatile value_t& depth, tbb::feeder&) const { g_tasks_observed += FindNumOfTasks(depth.value()); } }; /** Work item is passed by rvalue reference here. **/ struct FakeTaskGeneratorBody_RvalueRefVersion { void operator()(value_t&& depth ) const { g_tasks_observed += FindNumOfTasks(depth.value()); } }; void do_work(const value_t& depth, tbb::feeder& feeder) { ++g_tasks_observed; value_t new_value(depth.value()-1); for(size_t i = 0; i < depth.value(); ++i) { if (i%2) feeder.add( new_value ); // pass lvalue else feeder.add( value_t(depth.value()-1) ); // pass rvalue } } //! Standard form of the parallel_for_each functor object. /** Allows adding new work items on the fly. **/ struct TaskGeneratorBody { //! This form of the function call operator can be used when the body needs to add more work during the processing void operator()(value_t depth, tbb::feeder& feeder) const { do_work(depth, feeder); } private: // Assert that parallel_for_each does not ever access body constructors TaskGeneratorBody () {} TaskGeneratorBody (const TaskGeneratorBody&); // These functions need access to the default constructor template friend void TestBody(size_t); template friend void TestBodyMove(size_t); template friend void TestBodyWithMove(size_t); }; /** Work item is passed by reference here. **/ struct TaskGeneratorBody_RefVersion { void operator()(value_t& depth, tbb::feeder& feeder) const { do_work(depth, feeder); } }; /** Work item is passed as const here. Compilers must ignore the const qualifier. **/ struct TaskGeneratorBody_ConstVersion { void operator()(const value_t depth, tbb::feeder& feeder) const { do_work(depth, feeder); } }; /** Work item is passed by reference to const here. **/ struct TaskGeneratorBody_ConstRefVersion { void operator()(const value_t& depth, tbb::feeder& feeder) const { do_work(depth, feeder); } }; /** Work item is passed by reference to volatile here. **/ struct TaskGeneratorBody_VolatileRefVersion { void operator()(volatile value_t& depth, tbb::feeder& feeder) const { do_work(const_cast(depth), feeder); } }; /** Work item is passed by reference to const volatile here. **/ struct TaskGeneratorBody_ConstVolatileRefVersion { void operator()(const volatile value_t& depth, tbb::feeder& feeder) const { do_work(const_cast(depth), feeder); } }; /** Work item is passed by rvalue reference here. **/ struct TaskGeneratorBody_RvalueRefVersion { void operator()(value_t&& depth, tbb::feeder& feeder) const { do_work(depth, feeder); } }; static value_t g_depths[depths_nubmer] = {0, 1, 2, 3, 4, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; template void TestBodyMove(size_t depth) { typedef typename std::iterator_traits::value_type value_type; value_type a_depths[depths_nubmer] = {0, 1, 2, 3, 4, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; Body body; typedef std::move_iterator MoveIterator; MoveIterator mbegin(Iterator{a_depths}); MoveIterator mend(Iterator{a_depths + depth}); g_tasks_observed = 0; tbb::parallel_for_each(mbegin, mend, body); REQUIRE (g_tasks_observed == g_tasks_expected); } template void TestBody(size_t depth) { typedef typename std::iterator_traits::value_type value_type; value_type a_depths[depths_nubmer] = {0, 1, 2, 3, 4, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; Body body; Iterator begin(a_depths); Iterator end(a_depths + depth); g_tasks_observed = 0; tbb::parallel_for_each(begin, end, body); REQUIRE (g_tasks_observed == g_tasks_expected); } template void TestBodyWithMove(size_t depth) { TestBody(depth); TestBodyMove(depth); } template void TestIterator_Common(size_t depth) { TestBodyWithMove(depth); TestBodyWithMove(depth); TestBodyWithMove(depth); TestBodyWithMove(depth); TestBodyWithMove(depth); } template void TestIterator_Const(size_t depth) { TestIterator_Common(depth); TestBody(depth); } #if __TBB_CPP14_GENERIC_LAMBDAS_PRESENT template void TestGenericLambda(size_t depth, GenericBody body) { typedef typename std::iterator_traits::value_type value_type; value_type a_depths[depths_nubmer] = {0, 1, 2, 3, 4, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; Iterator begin(a_depths); Iterator end(a_depths + depth); g_tasks_observed = 0; tbb::parallel_for_each(begin, end, body); REQUIRE (g_tasks_observed == g_tasks_expected); } template void TestGenericLambdaMove(size_t depth, GenericBody body) { typedef typename std::iterator_traits::value_type value_type; value_type a_depths[depths_nubmer] = {0, 1, 2, 3, 4, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; typedef std::move_iterator MoveIterator; Iterator begin(a_depths); Iterator end(a_depths + depth); MoveIterator mbegin = std::make_move_iterator(begin); MoveIterator mend = std::make_move_iterator(end); g_tasks_observed = 0; tbb::parallel_for_each(mbegin, mend, body); REQUIRE (g_tasks_observed == g_tasks_expected); } template void TestGenericLambdaWithMove(size_t depth, GenericBody body) { TestGenericLambda(depth, body); TestGenericLambdaMove(depth, body); } template void TestGenericLambdasCommon(size_t depth) { TestGenericLambdaWithMove(depth, [](auto item){g_tasks_observed += FindNumOfTasks(item.value());}); TestGenericLambdaWithMove(depth, [](const auto item){g_tasks_observed += FindNumOfTasks(item.value());}); TestGenericLambda(depth, [](volatile auto& item){g_tasks_observed += FindNumOfTasks(item.value());}); TestGenericLambda(depth, [](const volatile auto& item){g_tasks_observed += FindNumOfTasks(item.value());}); TestGenericLambda(depth, [](auto& item){g_tasks_observed += FindNumOfTasks(item.value());}); TestGenericLambdaWithMove(depth, [](const auto& item){g_tasks_observed += FindNumOfTasks(item.value());}); TestGenericLambdaWithMove(depth, [](auto&& item){g_tasks_observed += FindNumOfTasks(item.value());}); TestGenericLambdaWithMove(depth, [](auto item, auto& feeder){do_work(item, feeder);}); TestGenericLambdaWithMove(depth, [](const auto item, auto& feeder){do_work(item, feeder);}); TestGenericLambda(depth, [](volatile auto& item, auto& feeder){do_work(const_cast(item), feeder);}); TestGenericLambda(depth, [](const volatile auto& item, auto& feeder){do_work(const_cast(item), feeder);}); TestGenericLambda(depth, [](auto& item, auto& feeder){do_work(item, feeder);}); TestGenericLambdaWithMove(depth, [](const auto& item, auto& feeder){do_work(item, feeder);}); TestGenericLambdaWithMove(depth, [](auto&& item, auto& feeder){do_work(item, feeder);}); } #endif /*__TBB_CPP14_GENERIC_LAMBDAS_PRESENT*/ template void TestIterator_Move(size_t depth) { TestBodyMove(depth); TestBodyMove(depth); } template void TestIterator_Modifiable(size_t depth) { TestIterator_Const(depth); TestIterator_Move(depth); TestBody(depth); TestBody(depth); TestBody(depth); TestBody(depth); #if __TBB_CPP14_GENERIC_LAMBDAS_PRESENT TestGenericLambdasCommon(depth); #endif } std::atomic task_counter{0}; template