xref: /oneTBB/test/tbb/test_parallel_reduce.cpp (revision c21e688a)
151c0b2f7Stbbdev /*
2*c21e688aSSergey Zheltov     Copyright (c) 2005-2022 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 <atomic>
1851c0b2f7Stbbdev 
1951c0b2f7Stbbdev #include "common/parallel_reduce_common.h"
2051c0b2f7Stbbdev #include "common/cpu_usertime.h"
2151c0b2f7Stbbdev #include "common/exception_handling.h"
22478de5b1Stbbdev #include "common/concepts_common.h"
2351c0b2f7Stbbdev 
2451c0b2f7Stbbdev //! \file test_parallel_reduce.cpp
2551c0b2f7Stbbdev //! \brief Test for [algorithms.parallel_reduce algorithms.parallel_deterministic_reduce] specification
2651c0b2f7Stbbdev 
2755f9b178SIvan Kochin #if _MSC_VER
2855f9b178SIvan Kochin #pragma warning (push)
2955f9b178SIvan Kochin // Suppress conditional expression is constant
3055f9b178SIvan Kochin #pragma warning (disable: 4127)
3155f9b178SIvan Kochin #endif //#if _MSC_VER
3255f9b178SIvan Kochin 
3351c0b2f7Stbbdev using ValueType = uint64_t;
3451c0b2f7Stbbdev 
3551c0b2f7Stbbdev struct Sum {
3651c0b2f7Stbbdev     template<typename T>
operator ()Sum3751c0b2f7Stbbdev     T operator() ( const T& v1, const T& v2 ) const {
3851c0b2f7Stbbdev         return v1 + v2;
3951c0b2f7Stbbdev     }
4051c0b2f7Stbbdev };
4151c0b2f7Stbbdev 
4251c0b2f7Stbbdev struct Accumulator {
operator ()Accumulator4351c0b2f7Stbbdev     ValueType operator() ( const tbb::blocked_range<ValueType*>& r, ValueType value ) const {
4451c0b2f7Stbbdev         for ( ValueType* pv = r.begin(); pv != r.end(); ++pv )
4551c0b2f7Stbbdev             value += *pv;
4651c0b2f7Stbbdev         return value;
4751c0b2f7Stbbdev     }
4851c0b2f7Stbbdev };
4951c0b2f7Stbbdev 
5051c0b2f7Stbbdev class ParallelSumTester {
5151c0b2f7Stbbdev public:
5251c0b2f7Stbbdev     ParallelSumTester( const ParallelSumTester& ) = default;
5351c0b2f7Stbbdev     void operator=( const ParallelSumTester& ) = delete;
5451c0b2f7Stbbdev 
ParallelSumTester()5551c0b2f7Stbbdev     ParallelSumTester() : m_range(nullptr, nullptr) {
5651c0b2f7Stbbdev         m_array = new ValueType[unsigned(count)];
5751c0b2f7Stbbdev         for ( ValueType i = 0; i < count; ++i )
5851c0b2f7Stbbdev             m_array[i] = i + 1;
5951c0b2f7Stbbdev         m_range = tbb::blocked_range<ValueType*>( m_array, m_array + count );
6051c0b2f7Stbbdev     }
~ParallelSumTester()6151c0b2f7Stbbdev     ~ParallelSumTester() { delete[] m_array; }
6251c0b2f7Stbbdev 
6351c0b2f7Stbbdev     template<typename Partitioner>
CheckParallelReduce()6451c0b2f7Stbbdev     void CheckParallelReduce() {
6551c0b2f7Stbbdev         Partitioner partitioner;
6651c0b2f7Stbbdev         ValueType result1 = reduce_invoker<ValueType>( m_range, Accumulator(), Sum(), partitioner );
6751c0b2f7Stbbdev         REQUIRE_MESSAGE( result1 == expected, "Wrong parallel summation result" );
6851c0b2f7Stbbdev         ValueType result2 = reduce_invoker<ValueType>( m_range,
6951c0b2f7Stbbdev             [](const tbb::blocked_range<ValueType*>& r, ValueType value) -> ValueType {
7051c0b2f7Stbbdev                 for ( const ValueType* pv = r.begin(); pv != r.end(); ++pv )
7151c0b2f7Stbbdev                     value += *pv;
7251c0b2f7Stbbdev                 return value;
7351c0b2f7Stbbdev             },
7451c0b2f7Stbbdev             Sum(),
7551c0b2f7Stbbdev             partitioner
7651c0b2f7Stbbdev         );
7751c0b2f7Stbbdev         REQUIRE_MESSAGE( result2 == expected, "Wrong parallel summation result" );
7851c0b2f7Stbbdev     }
7951c0b2f7Stbbdev private:
8051c0b2f7Stbbdev     ValueType* m_array;
8151c0b2f7Stbbdev     tbb::blocked_range<ValueType*> m_range;
8251c0b2f7Stbbdev     static const ValueType count, expected;
8351c0b2f7Stbbdev };
8451c0b2f7Stbbdev 
8551c0b2f7Stbbdev const ValueType ParallelSumTester::count = 1000000;
8651c0b2f7Stbbdev const ValueType ParallelSumTester::expected = count * (count + 1) / 2;
8751c0b2f7Stbbdev 
8851c0b2f7Stbbdev namespace test_cancellation {
8951c0b2f7Stbbdev 
9051c0b2f7Stbbdev struct ReduceToCancel {
operator ()test_cancellation::ReduceToCancel9151c0b2f7Stbbdev     std::size_t operator()( const tbb::blocked_range<std::size_t>&, std::size_t ) const {
9251c0b2f7Stbbdev         ++g_CurExecuted;
9351c0b2f7Stbbdev         Cancellator::WaitUntilReady();
9451c0b2f7Stbbdev         return 1;
9551c0b2f7Stbbdev     }
9651c0b2f7Stbbdev }; // struct ReduceToCancel
9751c0b2f7Stbbdev 
9851c0b2f7Stbbdev struct JoinToCancel {
operator ()test_cancellation::JoinToCancel9951c0b2f7Stbbdev     std::size_t operator()( std::size_t, std::size_t ) const {
10051c0b2f7Stbbdev         ++g_CurExecuted;
10151c0b2f7Stbbdev         Cancellator::WaitUntilReady();
10251c0b2f7Stbbdev         return 1;
10351c0b2f7Stbbdev     }
10451c0b2f7Stbbdev }; // struct Join
10551c0b2f7Stbbdev 
10651c0b2f7Stbbdev struct ReduceFunctorToCancel {
10751c0b2f7Stbbdev     std::size_t result;
10851c0b2f7Stbbdev 
ReduceFunctorToCanceltest_cancellation::ReduceFunctorToCancel10951c0b2f7Stbbdev     ReduceFunctorToCancel() : result(0) {}
ReduceFunctorToCanceltest_cancellation::ReduceFunctorToCancel11051c0b2f7Stbbdev     ReduceFunctorToCancel( ReduceFunctorToCancel&, tbb::split ) : result(0) {}
11151c0b2f7Stbbdev 
operator ()test_cancellation::ReduceFunctorToCancel11251c0b2f7Stbbdev     void operator()( const tbb::blocked_range<std::size_t>& br ) {
11351c0b2f7Stbbdev         result = ReduceToCancel{}(br, result);
11451c0b2f7Stbbdev     }
11551c0b2f7Stbbdev 
jointest_cancellation::ReduceFunctorToCancel11651c0b2f7Stbbdev     void join( ReduceFunctorToCancel& rhs ) {
11751c0b2f7Stbbdev         result = JoinToCancel{}(result, rhs.result);
11851c0b2f7Stbbdev     }
11951c0b2f7Stbbdev }; // struct ReduceFunctorToCancel
12051c0b2f7Stbbdev 
12151c0b2f7Stbbdev static constexpr std::size_t buffer_test_size = 1024;
12251c0b2f7Stbbdev static constexpr std::size_t maxParallelReduceRunnerMode = 9;
12351c0b2f7Stbbdev 
12451c0b2f7Stbbdev template <std::size_t Mode>
12551c0b2f7Stbbdev class ParallelReduceRunner {
12651c0b2f7Stbbdev     tbb::task_group_context& my_ctx;
12751c0b2f7Stbbdev 
12851c0b2f7Stbbdev     static_assert(Mode >= 0 && Mode <= maxParallelReduceRunnerMode, "Incorrect mode for ParallelReduceTask");
12951c0b2f7Stbbdev 
13051c0b2f7Stbbdev     template <typename... Args>
run_parallel_reduce(Args &&...args) const13151c0b2f7Stbbdev     void run_parallel_reduce( Args&&... args ) const {
13251c0b2f7Stbbdev         switch(Mode % 5) {
13351c0b2f7Stbbdev             case 0 : {
13451c0b2f7Stbbdev                 tbb::parallel_reduce(std::forward<Args>(args)..., my_ctx);
13551c0b2f7Stbbdev                 break;
13651c0b2f7Stbbdev             }
13751c0b2f7Stbbdev             case 1 : {
13851c0b2f7Stbbdev                 tbb::parallel_reduce(std::forward<Args>(args)..., tbb::simple_partitioner{}, my_ctx);
13951c0b2f7Stbbdev                 break;
14051c0b2f7Stbbdev             }
14151c0b2f7Stbbdev             case 2 : {
14251c0b2f7Stbbdev                 tbb::parallel_reduce(std::forward<Args>(args)..., tbb::auto_partitioner{}, my_ctx);
14351c0b2f7Stbbdev                 break;
14451c0b2f7Stbbdev             }
14551c0b2f7Stbbdev             case 3 : {
14651c0b2f7Stbbdev                 tbb::parallel_reduce(std::forward<Args>(args)..., tbb::static_partitioner{}, my_ctx);
14751c0b2f7Stbbdev                 break;
14851c0b2f7Stbbdev             }
14951c0b2f7Stbbdev             case 4 : {
15051c0b2f7Stbbdev                 tbb::affinity_partitioner aff;
15151c0b2f7Stbbdev                 tbb::parallel_reduce(std::forward<Args>(args)..., aff, my_ctx);
15251c0b2f7Stbbdev                 break;
15351c0b2f7Stbbdev             }
15451c0b2f7Stbbdev         }
15551c0b2f7Stbbdev     }
15651c0b2f7Stbbdev 
15751c0b2f7Stbbdev public:
ParallelReduceRunner(tbb::task_group_context & ctx)15851c0b2f7Stbbdev     ParallelReduceRunner( tbb::task_group_context& ctx )
15951c0b2f7Stbbdev         : my_ctx(ctx) {}
16051c0b2f7Stbbdev 
operator ()() const16151c0b2f7Stbbdev     void operator()() const {
16251c0b2f7Stbbdev         tbb::blocked_range<std::size_t> br(0, buffer_test_size);
16351c0b2f7Stbbdev         if (Mode < 5) {
16451c0b2f7Stbbdev             ReduceFunctorToCancel functor;
16551c0b2f7Stbbdev             run_parallel_reduce(br, functor);
16651c0b2f7Stbbdev         } else {
167478de5b1Stbbdev             run_parallel_reduce(br, std::size_t(0), ReduceToCancel{}, JoinToCancel{});
16851c0b2f7Stbbdev         }
16951c0b2f7Stbbdev     }
17051c0b2f7Stbbdev }; // class ParallelReduceRunner
17151c0b2f7Stbbdev 
17251c0b2f7Stbbdev static constexpr std::size_t maxParallelDeterministicReduceRunnerMode = 5;
17351c0b2f7Stbbdev 
17451c0b2f7Stbbdev // TODO: unify with ParallelReduceRunner
17551c0b2f7Stbbdev template <std::size_t Mode>
17651c0b2f7Stbbdev class ParallelDeterministicReduceRunner {
17751c0b2f7Stbbdev     tbb::task_group_context& my_ctx;
17851c0b2f7Stbbdev 
17951c0b2f7Stbbdev     static_assert(Mode >= 0 && Mode <= maxParallelDeterministicReduceRunnerMode, "Incorrect Mode for deterministic_reduce task");
18051c0b2f7Stbbdev 
18151c0b2f7Stbbdev     template <typename... Args>
run_parallel_deterministic_reduce(Args &&...args) const18251c0b2f7Stbbdev     void run_parallel_deterministic_reduce( Args&&... args ) const {
18351c0b2f7Stbbdev         switch(Mode % 3) {
18451c0b2f7Stbbdev             case 0 : {
18551c0b2f7Stbbdev                 tbb::parallel_deterministic_reduce(std::forward<Args>(args)..., my_ctx);
18651c0b2f7Stbbdev                 break;
18751c0b2f7Stbbdev             }
18851c0b2f7Stbbdev             case 1 : {
18951c0b2f7Stbbdev                 tbb::parallel_deterministic_reduce(std::forward<Args>(args)..., tbb::simple_partitioner{}, my_ctx);
19051c0b2f7Stbbdev                 break;
19151c0b2f7Stbbdev             }
19251c0b2f7Stbbdev             case 2 : {
19351c0b2f7Stbbdev                 tbb::parallel_deterministic_reduce(std::forward<Args>(args)..., tbb::static_partitioner{}, my_ctx);
19451c0b2f7Stbbdev                 break;
19551c0b2f7Stbbdev             }
19651c0b2f7Stbbdev         }
19751c0b2f7Stbbdev     }
19851c0b2f7Stbbdev 
19951c0b2f7Stbbdev public:
ParallelDeterministicReduceRunner(tbb::task_group_context & ctx)20051c0b2f7Stbbdev     ParallelDeterministicReduceRunner( tbb::task_group_context& ctx )
20151c0b2f7Stbbdev         : my_ctx(ctx) {}
20251c0b2f7Stbbdev 
operator ()() const20351c0b2f7Stbbdev     void operator()() const {
20451c0b2f7Stbbdev         tbb::blocked_range<std::size_t> br(0, buffer_test_size);
20551c0b2f7Stbbdev         if (Mode < 3) {
20651c0b2f7Stbbdev             ReduceFunctorToCancel functor;
20751c0b2f7Stbbdev             run_parallel_deterministic_reduce(br, functor);
20851c0b2f7Stbbdev         } else {
209478de5b1Stbbdev             run_parallel_deterministic_reduce(br, std::size_t(0), ReduceToCancel{}, JoinToCancel{});
21051c0b2f7Stbbdev         }
21151c0b2f7Stbbdev     }
21251c0b2f7Stbbdev }; // class ParallelDeterministicReduceRunner
21351c0b2f7Stbbdev 
21451c0b2f7Stbbdev template <std::size_t Mode>
run_parallel_reduce_cancellation_test()21551c0b2f7Stbbdev void run_parallel_reduce_cancellation_test() {
21651c0b2f7Stbbdev     for ( auto concurrency_level : utils::concurrency_range() ) {
21751c0b2f7Stbbdev         if (concurrency_level < 2) continue;
21851c0b2f7Stbbdev 
21951c0b2f7Stbbdev         tbb::global_control gc(tbb::global_control::max_allowed_parallelism, concurrency_level);
22051c0b2f7Stbbdev         ResetEhGlobals();
22151c0b2f7Stbbdev         RunCancellationTest<ParallelReduceRunner<Mode>, Cancellator>();
22251c0b2f7Stbbdev     }
22351c0b2f7Stbbdev }
22451c0b2f7Stbbdev 
22551c0b2f7Stbbdev template <std::size_t Mode>
run_parallel_deterministic_reduce_cancellation_test()22651c0b2f7Stbbdev void run_parallel_deterministic_reduce_cancellation_test() {
22751c0b2f7Stbbdev     for ( auto concurrency_level : utils::concurrency_range() ) {
22851c0b2f7Stbbdev         if (concurrency_level < 2) continue;
22951c0b2f7Stbbdev 
23051c0b2f7Stbbdev         tbb::global_control gc(tbb::global_control::max_allowed_parallelism, concurrency_level);
23151c0b2f7Stbbdev         ResetEhGlobals();
23251c0b2f7Stbbdev         RunCancellationTest<ParallelDeterministicReduceRunner<Mode>, Cancellator>();
23351c0b2f7Stbbdev     }
23451c0b2f7Stbbdev }
23551c0b2f7Stbbdev 
23651c0b2f7Stbbdev template <std::size_t Mode>
23751c0b2f7Stbbdev struct ParallelReduceTestRunner {
runtest_cancellation::ParallelReduceTestRunner23851c0b2f7Stbbdev     static void run() {
23951c0b2f7Stbbdev         run_parallel_reduce_cancellation_test<Mode>();
24051c0b2f7Stbbdev         ParallelReduceTestRunner<Mode + 1>::run();
24151c0b2f7Stbbdev     }
24251c0b2f7Stbbdev }; // struct ParallelReduceTestRunner
24351c0b2f7Stbbdev 
24451c0b2f7Stbbdev template <>
24551c0b2f7Stbbdev struct ParallelReduceTestRunner<maxParallelReduceRunnerMode> {
runtest_cancellation::ParallelReduceTestRunner24651c0b2f7Stbbdev     static void run() {
24751c0b2f7Stbbdev         run_parallel_reduce_cancellation_test<maxParallelReduceRunnerMode>();
24851c0b2f7Stbbdev     }
24951c0b2f7Stbbdev }; // struct ParallelReduceTestRunner<maxParallelReduceRunnerMode>
25051c0b2f7Stbbdev 
25151c0b2f7Stbbdev template <std::size_t Mode>
25251c0b2f7Stbbdev struct ParallelDeterministicReduceTestRunner {
runtest_cancellation::ParallelDeterministicReduceTestRunner25351c0b2f7Stbbdev     static void run() {
25451c0b2f7Stbbdev         run_parallel_deterministic_reduce_cancellation_test<Mode>();
25551c0b2f7Stbbdev         ParallelDeterministicReduceTestRunner<Mode + 1>::run();
25651c0b2f7Stbbdev     }
25751c0b2f7Stbbdev }; // struct ParallelDeterministicReduceTestRunner
25851c0b2f7Stbbdev 
25951c0b2f7Stbbdev template <>
26051c0b2f7Stbbdev struct ParallelDeterministicReduceTestRunner<maxParallelDeterministicReduceRunnerMode> {
runtest_cancellation::ParallelDeterministicReduceTestRunner26151c0b2f7Stbbdev     static void run() {
26251c0b2f7Stbbdev         run_parallel_deterministic_reduce_cancellation_test<maxParallelDeterministicReduceRunnerMode>();
26351c0b2f7Stbbdev     }
26451c0b2f7Stbbdev }; // struct ParallelDeterministicReduceTestRunner<maxParallelDeterministicReduceRunnerMode>
26551c0b2f7Stbbdev 
26651c0b2f7Stbbdev } // namespace test_cancellation
26751c0b2f7Stbbdev 
268478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
269478de5b1Stbbdev template <typename... Args>
270478de5b1Stbbdev concept can_call_parallel_reduce_basic = requires( Args&&... args ) {
271478de5b1Stbbdev     tbb::parallel_reduce(std::forward<Args>(args)...);
272478de5b1Stbbdev };
273478de5b1Stbbdev 
274478de5b1Stbbdev template <typename... Args>
275478de5b1Stbbdev concept can_call_parallel_deterministic_reduce_basic = requires ( Args&&... args ) {
276478de5b1Stbbdev     tbb::parallel_deterministic_reduce(std::forward<Args>(args)...);
277478de5b1Stbbdev };
278478de5b1Stbbdev 
279478de5b1Stbbdev template <typename... Args>
280478de5b1Stbbdev concept can_call_preduce_helper = can_call_parallel_reduce_basic<Args...> &&
281478de5b1Stbbdev                                   can_call_parallel_reduce_basic<Args..., tbb::task_group_context&>;
282478de5b1Stbbdev 
283478de5b1Stbbdev template <typename... Args>
284478de5b1Stbbdev concept can_call_pdet_reduce_helper = can_call_parallel_deterministic_reduce_basic<Args...> &&
285478de5b1Stbbdev                                       can_call_parallel_deterministic_reduce_basic<Args..., tbb::task_group_context&>;
286478de5b1Stbbdev 
287478de5b1Stbbdev template <typename... Args>
288478de5b1Stbbdev concept can_call_preduce_with_partitioner = can_call_preduce_helper<Args...> &&
289478de5b1Stbbdev                                             can_call_preduce_helper<Args..., const tbb::simple_partitioner&> &&
290478de5b1Stbbdev                                             can_call_preduce_helper<Args..., const tbb::auto_partitioner&> &&
291478de5b1Stbbdev                                             can_call_preduce_helper<Args..., const tbb::static_partitioner&> &&
292478de5b1Stbbdev                                             can_call_preduce_helper<Args..., tbb::affinity_partitioner&>;
293478de5b1Stbbdev 
294478de5b1Stbbdev template <typename... Args>
295478de5b1Stbbdev concept can_call_pdet_reduce_with_partitioner = can_call_pdet_reduce_helper<Args...> &&
296478de5b1Stbbdev                                                 can_call_pdet_reduce_helper<Args..., const tbb::simple_partitioner&> &&
297478de5b1Stbbdev                                                 can_call_pdet_reduce_helper<Args..., const tbb::static_partitioner&>;
298478de5b1Stbbdev 
299478de5b1Stbbdev template <typename Range, typename Body>
300478de5b1Stbbdev concept can_call_imperative_preduce = can_call_preduce_with_partitioner<const Range&, Body&>;
301478de5b1Stbbdev 
302478de5b1Stbbdev template <typename Range, typename Body>
303478de5b1Stbbdev concept can_call_imperative_pdet_reduce = can_call_pdet_reduce_with_partitioner<const Range&, Body&>;
304478de5b1Stbbdev 
305478de5b1Stbbdev template <typename Range, typename Value, typename RealBody, typename Reduction>
306478de5b1Stbbdev concept can_call_functional_preduce = can_call_preduce_with_partitioner<const Range&, const Value&,
307478de5b1Stbbdev                                                                         const RealBody&, const Reduction&>;
308478de5b1Stbbdev 
309478de5b1Stbbdev template <typename Range, typename Value, typename RealBody, typename Reduction>
310478de5b1Stbbdev concept can_call_functional_pdet_reduce = can_call_pdet_reduce_with_partitioner<const Range&, const Value&,
311478de5b1Stbbdev                                                                                 const RealBody&, const Reduction&>;
312478de5b1Stbbdev 
313478de5b1Stbbdev template <typename Range>
314478de5b1Stbbdev using CorrectBody = test_concepts::parallel_reduce_body::Correct<Range>;
315478de5b1Stbbdev 
316478de5b1Stbbdev template <typename Range>
317478de5b1Stbbdev using CorrectFunc = test_concepts::parallel_reduce_function::Correct<Range>;
318478de5b1Stbbdev 
319478de5b1Stbbdev using CorrectReduction = test_concepts::parallel_reduce_combine::Correct<int>;
320478de5b1Stbbdev using CorrectRange = test_concepts::range::Correct;
321478de5b1Stbbdev 
test_preduce_range_constraints()322478de5b1Stbbdev void test_preduce_range_constraints() {
323478de5b1Stbbdev     using namespace test_concepts::range;
324478de5b1Stbbdev     static_assert(can_call_imperative_preduce<Correct, CorrectBody<Correct>>);
325478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<NonCopyable, CorrectBody<NonCopyable>>);
326478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<NonDestructible, CorrectBody<NonDestructible>>);
327478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<NonSplittable, CorrectBody<NonSplittable>>);
328478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<NoEmpty, CorrectBody<NoEmpty>>);
329478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<EmptyNonConst, CorrectBody<EmptyNonConst>>);
330478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<WrongReturnEmpty, CorrectBody<WrongReturnEmpty>>);
331478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<NoIsDivisible, CorrectBody<NoIsDivisible>>);
332478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<IsDivisibleNonConst, CorrectBody<NoIsDivisible>>);
333478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<WrongReturnIsDivisible, CorrectBody<WrongReturnIsDivisible>>);
334478de5b1Stbbdev 
335478de5b1Stbbdev     static_assert(can_call_functional_preduce<Correct, int, CorrectFunc<Correct>, CorrectReduction>);
336478de5b1Stbbdev     static_assert(!can_call_functional_preduce<NonCopyable, int, CorrectFunc<NonCopyable>, CorrectReduction>);
337478de5b1Stbbdev     static_assert(!can_call_functional_preduce<NonDestructible, int, CorrectFunc<NonDestructible>, CorrectReduction>);
338478de5b1Stbbdev     static_assert(!can_call_functional_preduce<NonSplittable, int, CorrectFunc<NonSplittable>, CorrectReduction>);
339478de5b1Stbbdev     static_assert(!can_call_functional_preduce<NoEmpty, int, CorrectFunc<NoEmpty>, CorrectReduction>);
340478de5b1Stbbdev     static_assert(!can_call_functional_preduce<EmptyNonConst, int, CorrectFunc<EmptyNonConst>, CorrectReduction>);
341478de5b1Stbbdev     static_assert(!can_call_functional_preduce<WrongReturnEmpty, int, CorrectFunc<WrongReturnEmpty>, CorrectReduction>);
342478de5b1Stbbdev     static_assert(!can_call_functional_preduce<NoIsDivisible, int, CorrectFunc<NoIsDivisible>, CorrectReduction>);
343478de5b1Stbbdev     static_assert(!can_call_functional_preduce<IsDivisibleNonConst, int, CorrectFunc<IsDivisibleNonConst>, CorrectReduction>);
344478de5b1Stbbdev     static_assert(!can_call_functional_preduce<WrongReturnIsDivisible, int, CorrectFunc<WrongReturnIsDivisible>, CorrectReduction>);
345478de5b1Stbbdev }
346478de5b1Stbbdev 
test_preduce_body_constraints()347478de5b1Stbbdev void test_preduce_body_constraints() {
348478de5b1Stbbdev     using namespace test_concepts::parallel_reduce_body;
349478de5b1Stbbdev     static_assert(can_call_imperative_preduce<CorrectRange, Correct<CorrectRange>>);
350478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<CorrectRange, NonSplittable<CorrectRange>>);
351478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<CorrectRange, NonDestructible<CorrectRange>>);
352478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<CorrectRange, NoOperatorRoundBrackets<CorrectRange>>);
353478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<CorrectRange, WrongInputOperatorRoundBrackets<CorrectRange>>);
354478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<CorrectRange, NoJoin<CorrectRange>>);
355478de5b1Stbbdev     static_assert(!can_call_imperative_preduce<CorrectRange, WrongInputJoin<CorrectRange>>);
356478de5b1Stbbdev }
357478de5b1Stbbdev 
test_preduce_func_constraints()358478de5b1Stbbdev void test_preduce_func_constraints() {
359478de5b1Stbbdev     using namespace test_concepts::parallel_reduce_function;
360478de5b1Stbbdev     static_assert(can_call_functional_preduce<CorrectRange, int, Correct<CorrectRange>, CorrectReduction>);
361478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, NoOperatorRoundBrackets<CorrectRange>, CorrectReduction>);
362478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, OperatorRoundBracketsNonConst<CorrectRange>, CorrectReduction>);
363478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, WrongFirstInputOperatorRoundBrackets<CorrectRange>, CorrectReduction>);
364478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, WrongSecondInputOperatorRoundBrackets<CorrectRange>, CorrectReduction>);
365478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, WrongReturnOperatorRoundBrackets<CorrectRange>, CorrectReduction>);
366478de5b1Stbbdev }
367478de5b1Stbbdev 
test_preduce_combine_constraints()368478de5b1Stbbdev void test_preduce_combine_constraints() {
369478de5b1Stbbdev     using namespace test_concepts::parallel_reduce_combine;
370478de5b1Stbbdev     static_assert(can_call_functional_preduce<CorrectRange, int, CorrectFunc<CorrectRange>, Correct<int>>);
371478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, CorrectFunc<CorrectRange>, NoOperatorRoundBrackets<int>>);
372478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, CorrectFunc<CorrectRange>, OperatorRoundBracketsNonConst<int>>);
373478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, CorrectFunc<CorrectRange>, WrongFirstInputOperatorRoundBrackets<int>>);
374478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, CorrectFunc<CorrectRange>, WrongSecondInputOperatorRoundBrackets<int>>);
375478de5b1Stbbdev     static_assert(!can_call_functional_preduce<CorrectRange, int, CorrectFunc<CorrectRange>, WrongReturnOperatorRoundBrackets<int>>);
376478de5b1Stbbdev }
377478de5b1Stbbdev 
test_pdet_reduce_range_constraints()378478de5b1Stbbdev void test_pdet_reduce_range_constraints() {
379478de5b1Stbbdev     using namespace test_concepts::range;
380478de5b1Stbbdev     static_assert(can_call_imperative_pdet_reduce<Correct, CorrectBody<Correct>>);
381478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<NonCopyable, CorrectBody<NonCopyable>>);
382478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<NonDestructible, CorrectBody<NonDestructible>>);
383478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<NonSplittable, CorrectBody<NonSplittable>>);
384478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<NoEmpty, CorrectBody<NoEmpty>>);
385478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<EmptyNonConst, CorrectBody<EmptyNonConst>>);
386478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<WrongReturnEmpty, CorrectBody<WrongReturnEmpty>>);
387478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<NoIsDivisible, CorrectBody<NoIsDivisible>>);
388478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<IsDivisibleNonConst, CorrectBody<NoIsDivisible>>);
389478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<WrongReturnIsDivisible, CorrectBody<WrongReturnIsDivisible>>);
390478de5b1Stbbdev 
391478de5b1Stbbdev     static_assert(can_call_functional_pdet_reduce<Correct, int, CorrectFunc<Correct>, CorrectReduction>);
392478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<NonCopyable, int, CorrectFunc<NonCopyable>, CorrectReduction>);
393478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<NonDestructible, int, CorrectFunc<NonDestructible>, CorrectReduction>);
394478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<NonSplittable, int, CorrectFunc<NonSplittable>, CorrectReduction>);
395478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<NoEmpty, int, CorrectFunc<NoEmpty>, CorrectReduction>);
396478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<EmptyNonConst, int, CorrectFunc<EmptyNonConst>, CorrectReduction>);
397478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<WrongReturnEmpty, int, CorrectFunc<WrongReturnEmpty>, CorrectReduction>);
398478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<NoIsDivisible, int, CorrectFunc<NoIsDivisible>, CorrectReduction>);
399478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<IsDivisibleNonConst, int, CorrectFunc<IsDivisibleNonConst>, CorrectReduction>);
400478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<WrongReturnIsDivisible, int, CorrectFunc<WrongReturnIsDivisible>, CorrectReduction>);
401478de5b1Stbbdev }
402478de5b1Stbbdev 
test_pdet_reduce_body_constraints()403478de5b1Stbbdev void test_pdet_reduce_body_constraints() {
404478de5b1Stbbdev     using namespace test_concepts::parallel_reduce_body;
405478de5b1Stbbdev     static_assert(can_call_imperative_pdet_reduce<CorrectRange, Correct<CorrectRange>>);
406478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<CorrectRange, NonSplittable<CorrectRange>>);
407478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<CorrectRange, NonDestructible<CorrectRange>>);
408478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<CorrectRange, NoOperatorRoundBrackets<CorrectRange>>);
409478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<CorrectRange, WrongInputOperatorRoundBrackets<CorrectRange>>);
410478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<CorrectRange, NoJoin<CorrectRange>>);
411478de5b1Stbbdev     static_assert(!can_call_imperative_pdet_reduce<CorrectRange, WrongInputJoin<CorrectRange>>);
412478de5b1Stbbdev }
413478de5b1Stbbdev 
test_pdet_reduce_func_constraints()414478de5b1Stbbdev void test_pdet_reduce_func_constraints() {
415478de5b1Stbbdev     using namespace test_concepts::parallel_reduce_function;
416478de5b1Stbbdev     static_assert(can_call_functional_pdet_reduce<CorrectRange, int, Correct<CorrectRange>, CorrectReduction>);
417478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, NoOperatorRoundBrackets<CorrectRange>, CorrectReduction>);
418478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, OperatorRoundBracketsNonConst<CorrectRange>, CorrectReduction>);
419478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, WrongFirstInputOperatorRoundBrackets<CorrectRange>, CorrectReduction>);
420478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, WrongSecondInputOperatorRoundBrackets<CorrectRange>, CorrectReduction>);
421478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, WrongReturnOperatorRoundBrackets<CorrectRange>, CorrectReduction>);
422478de5b1Stbbdev }
423478de5b1Stbbdev 
test_pdet_reduce_combine_constraints()424478de5b1Stbbdev void test_pdet_reduce_combine_constraints() {
425478de5b1Stbbdev     using namespace test_concepts::parallel_reduce_combine;
426478de5b1Stbbdev     static_assert(can_call_functional_pdet_reduce<CorrectRange, int, CorrectFunc<CorrectRange>, Correct<int>>);
427478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, CorrectFunc<CorrectRange>, NoOperatorRoundBrackets<int>>);
428478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, CorrectFunc<CorrectRange>, OperatorRoundBracketsNonConst<int>>);
429478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, CorrectFunc<CorrectRange>, WrongFirstInputOperatorRoundBrackets<int>>);
430478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, CorrectFunc<CorrectRange>, WrongSecondInputOperatorRoundBrackets<int>>);
431478de5b1Stbbdev     static_assert(!can_call_functional_pdet_reduce<CorrectRange, int, CorrectFunc<CorrectRange>, WrongReturnOperatorRoundBrackets<int>>);
432478de5b1Stbbdev }
433478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
434478de5b1Stbbdev 
43551c0b2f7Stbbdev //! Test parallel summation correctness
43651c0b2f7Stbbdev //! \brief \ref stress
43751c0b2f7Stbbdev TEST_CASE("Test parallel summation correctness") {
43851c0b2f7Stbbdev     ParallelSumTester pst;
43951c0b2f7Stbbdev     pst.CheckParallelReduce<utils_default_partitioner>();
44051c0b2f7Stbbdev     pst.CheckParallelReduce<tbb::simple_partitioner>();
44151c0b2f7Stbbdev     pst.CheckParallelReduce<tbb::auto_partitioner>();
44251c0b2f7Stbbdev     pst.CheckParallelReduce<tbb::affinity_partitioner>();
44351c0b2f7Stbbdev     pst.CheckParallelReduce<tbb::static_partitioner>();
44451c0b2f7Stbbdev }
44551c0b2f7Stbbdev 
44651c0b2f7Stbbdev static std::atomic<long> ForkCount;
44751c0b2f7Stbbdev static std::atomic<long> FooBodyCount;
44851c0b2f7Stbbdev 
44951c0b2f7Stbbdev //! Class with public interface that is exactly minimal requirements for Range concept
45051c0b2f7Stbbdev class MinimalRange {
45151c0b2f7Stbbdev     size_t begin, end;
45251c0b2f7Stbbdev     friend class FooBody;
MinimalRange(size_t i)45351c0b2f7Stbbdev     explicit MinimalRange( size_t i ) : begin(0), end(i) {}
45451c0b2f7Stbbdev     template <typename Partitioner_> friend void TestSplitting( std::size_t nthread );
45551c0b2f7Stbbdev public:
MinimalRange(MinimalRange & r,tbb::split)45651c0b2f7Stbbdev     MinimalRange( MinimalRange& r, tbb::split ) : end(r.end) {
45751c0b2f7Stbbdev         begin = r.end = (r.begin+r.end)/2;
45851c0b2f7Stbbdev     }
is_divisible() const45951c0b2f7Stbbdev     bool is_divisible() const {return end-begin>=2;}
empty() const46051c0b2f7Stbbdev     bool empty() const {return begin==end;}
46151c0b2f7Stbbdev };
46251c0b2f7Stbbdev 
46351c0b2f7Stbbdev //! Class with public interface that is exactly minimal requirements for Body of a parallel_reduce
46451c0b2f7Stbbdev class FooBody {
46551c0b2f7Stbbdev private:
46651c0b2f7Stbbdev     FooBody( const FooBody& );          // Deny access
46751c0b2f7Stbbdev     void operator=( const FooBody& );   // Deny access
46851c0b2f7Stbbdev     template <typename Partitioner_> friend void TestSplitting( std::size_t nthread );
46957f524caSIlya Isaev     //! Parent that created this body via split operation.  nullptr if original body.
47051c0b2f7Stbbdev     FooBody* parent;
47151c0b2f7Stbbdev     //! Total number of index values processed by body and its children.
47251c0b2f7Stbbdev     size_t sum;
47351c0b2f7Stbbdev     //! Range that has been processed so far by this body and its children.
47451c0b2f7Stbbdev     size_t begin, end;
47551c0b2f7Stbbdev     //! True if body has not yet been processed at least once by operator().
47651c0b2f7Stbbdev     bool is_new;
47751c0b2f7Stbbdev     //! 1 if body was created by split; 0 if original body.
47851c0b2f7Stbbdev     int forked;
FooBody()47951c0b2f7Stbbdev     FooBody() {++FooBodyCount;}
48051c0b2f7Stbbdev public:
~FooBody()48151c0b2f7Stbbdev     ~FooBody() {
48251c0b2f7Stbbdev         forked = 0xDEADBEEF;
48351c0b2f7Stbbdev         sum=0xDEADBEEF;
48451c0b2f7Stbbdev         --FooBodyCount;
48551c0b2f7Stbbdev     }
FooBody(FooBody & other,tbb::split)48651c0b2f7Stbbdev     FooBody( FooBody& other, tbb::split ) {
48751c0b2f7Stbbdev         ++FooBodyCount;
48851c0b2f7Stbbdev         ++ForkCount;
48951c0b2f7Stbbdev         sum = 0;
49051c0b2f7Stbbdev         parent = &other;
49151c0b2f7Stbbdev         is_new = true;
49251c0b2f7Stbbdev         forked = 1;
49351c0b2f7Stbbdev     }
49451c0b2f7Stbbdev 
init()49551c0b2f7Stbbdev     void init() {
49651c0b2f7Stbbdev         sum = 0;
49751c0b2f7Stbbdev         parent = nullptr;
49851c0b2f7Stbbdev         is_new = true;
49951c0b2f7Stbbdev         forked = 0;
50051c0b2f7Stbbdev         begin = ~size_t(0);
50151c0b2f7Stbbdev         end = ~size_t(0);
50251c0b2f7Stbbdev     }
50351c0b2f7Stbbdev 
join(FooBody & s)50451c0b2f7Stbbdev     void join( FooBody& s ) {
50551c0b2f7Stbbdev         REQUIRE( s.forked==1 );
50651c0b2f7Stbbdev         REQUIRE( this!=&s );
50751c0b2f7Stbbdev         REQUIRE( this==s.parent );
50851c0b2f7Stbbdev         REQUIRE( end==s.begin );
50951c0b2f7Stbbdev         end = s.end;
51051c0b2f7Stbbdev         sum += s.sum;
51151c0b2f7Stbbdev         s.forked = 2;
51251c0b2f7Stbbdev     }
operator ()(const MinimalRange & r)51351c0b2f7Stbbdev     void operator()( const MinimalRange& r ) {
51451c0b2f7Stbbdev         for( size_t k=r.begin; k<r.end; ++k )
51551c0b2f7Stbbdev             ++sum;
51651c0b2f7Stbbdev         if( is_new ) {
51751c0b2f7Stbbdev             is_new = false;
51851c0b2f7Stbbdev             begin = r.begin;
51951c0b2f7Stbbdev         } else
52051c0b2f7Stbbdev             REQUIRE( end==r.begin );
52151c0b2f7Stbbdev         end = r.end;
52251c0b2f7Stbbdev     }
52351c0b2f7Stbbdev };
52451c0b2f7Stbbdev 
52551c0b2f7Stbbdev template<typename Partitioner>
TestSplitting(std::size_t nthread)52651c0b2f7Stbbdev void TestSplitting( std::size_t nthread ) {
52751c0b2f7Stbbdev     ForkCount = 0;
52851c0b2f7Stbbdev     Partitioner partitioner;
52951c0b2f7Stbbdev     for( size_t i=0; i<=1000; ++i ) {
53051c0b2f7Stbbdev         FooBody f;
53151c0b2f7Stbbdev         f.init();
53251c0b2f7Stbbdev         REQUIRE_MESSAGE( FooBodyCount==1, "Wrong initial BodyCount value" );
53351c0b2f7Stbbdev         reduce_invoker(MinimalRange(i), f, partitioner);
53451c0b2f7Stbbdev 
53551c0b2f7Stbbdev         if (nthread == 1) REQUIRE_MESSAGE(ForkCount==0, "Body was split during 1 thread execution");
53651c0b2f7Stbbdev 
53751c0b2f7Stbbdev         REQUIRE_MESSAGE( FooBodyCount==1, "Some copies of FooBody was not removed after reduction");
53851c0b2f7Stbbdev         REQUIRE_MESSAGE( f.sum==i, "Incorrect reduction" );
53951c0b2f7Stbbdev         REQUIRE_MESSAGE( f.begin==(i==0 ? ~size_t(0) : 0), "Incorrect range borders" );
54051c0b2f7Stbbdev         REQUIRE_MESSAGE( f.end==(i==0 ? ~size_t(0) : i), "Incorrect range borders" );
54151c0b2f7Stbbdev     }
54251c0b2f7Stbbdev }
54351c0b2f7Stbbdev 
54451c0b2f7Stbbdev //! Test splitting range and body during reduction, test that all workers sleep when no work
54551c0b2f7Stbbdev //! \brief \ref resource_usage \ref error_guessing
54651c0b2f7Stbbdev TEST_CASE("Test splitting range and body during reduction, test that all workers sleep when no work") {
54751c0b2f7Stbbdev     for ( auto concurrency_level : utils::concurrency_range() ) {
54851c0b2f7Stbbdev         tbb::global_control control(tbb::global_control::max_allowed_parallelism, concurrency_level);
54951c0b2f7Stbbdev 
55051c0b2f7Stbbdev         TestSplitting<tbb::simple_partitioner>(concurrency_level);
55151c0b2f7Stbbdev         TestSplitting<tbb::static_partitioner>(concurrency_level);
55251c0b2f7Stbbdev         TestSplitting<tbb::auto_partitioner>(concurrency_level);
55351c0b2f7Stbbdev         TestSplitting<tbb::affinity_partitioner>(concurrency_level);
55451c0b2f7Stbbdev         TestSplitting<utils_default_partitioner>(concurrency_level);
55551c0b2f7Stbbdev 
55651c0b2f7Stbbdev         // Test that all workers sleep when no work
55751c0b2f7Stbbdev         TestCPUUserTime(concurrency_level);
55851c0b2f7Stbbdev     }
55951c0b2f7Stbbdev }
56051c0b2f7Stbbdev 
56151c0b2f7Stbbdev //! Define overloads of parallel_deterministic_reduce that accept "undesired" types of partitioners
56251c0b2f7Stbbdev namespace unsupported {
56351c0b2f7Stbbdev     template<typename Range, typename Body>
parallel_deterministic_reduce(const Range &,Body &,const tbb::auto_partitioner &)56451c0b2f7Stbbdev     void parallel_deterministic_reduce(const Range&, Body&, const tbb::auto_partitioner&) { }
56551c0b2f7Stbbdev     template<typename Range, typename Body>
parallel_deterministic_reduce(const Range &,Body &,tbb::affinity_partitioner &)56651c0b2f7Stbbdev     void parallel_deterministic_reduce(const Range&, Body&, tbb::affinity_partitioner&) { }
56751c0b2f7Stbbdev 
56851c0b2f7Stbbdev     template<typename Range, typename Value, typename RealBody, typename Reduction>
parallel_deterministic_reduce(const Range &,const Value & identity,const RealBody &,const Reduction &,const tbb::auto_partitioner &)56951c0b2f7Stbbdev     Value parallel_deterministic_reduce(const Range& , const Value& identity, const RealBody& , const Reduction& , const tbb::auto_partitioner&) {
57051c0b2f7Stbbdev         return identity;
57151c0b2f7Stbbdev     }
57251c0b2f7Stbbdev     template<typename Range, typename Value, typename RealBody, typename Reduction>
parallel_deterministic_reduce(const Range &,const Value & identity,const RealBody &,const Reduction &,tbb::affinity_partitioner &)57351c0b2f7Stbbdev     Value parallel_deterministic_reduce(const Range& , const Value& identity, const RealBody& , const Reduction& , tbb::affinity_partitioner&) {
57451c0b2f7Stbbdev         return identity;
57551c0b2f7Stbbdev     }
57651c0b2f7Stbbdev }
57751c0b2f7Stbbdev 
57851c0b2f7Stbbdev struct Body {
57951c0b2f7Stbbdev     float value;
BodyBody58051c0b2f7Stbbdev     Body() : value(0) {}
BodyBody58151c0b2f7Stbbdev     Body(Body&, tbb::split) { value = 0; }
operator ()Body58251c0b2f7Stbbdev     void operator()(const tbb::blocked_range<int>&) {}
joinBody58351c0b2f7Stbbdev     void join(Body&) {}
58451c0b2f7Stbbdev };
58551c0b2f7Stbbdev 
58651c0b2f7Stbbdev //! Check that other types of partitioners are not supported (auto, affinity)
58751c0b2f7Stbbdev //! In the case of "unsupported" API unexpectedly sneaking into namespace tbb,
58851c0b2f7Stbbdev //! this test should result in a compilation error due to overload resolution ambiguity
58951c0b2f7Stbbdev //! \brief \ref negative \ref error_guessing
59051c0b2f7Stbbdev TEST_CASE("Test Unsupported Partitioners") {
59151c0b2f7Stbbdev     using namespace tbb;
59251c0b2f7Stbbdev     using namespace unsupported;
59351c0b2f7Stbbdev     Body body;
59451c0b2f7Stbbdev     parallel_deterministic_reduce(blocked_range<int>(0, 10), body, tbb::auto_partitioner());
59551c0b2f7Stbbdev 
59651c0b2f7Stbbdev     tbb::affinity_partitioner ap;
59751c0b2f7Stbbdev     parallel_deterministic_reduce(blocked_range<int>(0, 10), body, ap);
59851c0b2f7Stbbdev 
59951c0b2f7Stbbdev     parallel_deterministic_reduce(
60051c0b2f7Stbbdev         blocked_range<int>(0, 10),
60151c0b2f7Stbbdev         0,
__anon6ca08c0e0202(const blocked_range<int>&, int init)60251c0b2f7Stbbdev         [](const blocked_range<int>&, int init)->int {
60351c0b2f7Stbbdev             return init;
60451c0b2f7Stbbdev         },
__anon6ca08c0e0302(int x, int y)60551c0b2f7Stbbdev         [](int x, int y)->int {
60651c0b2f7Stbbdev             return x + y;
60751c0b2f7Stbbdev         },
60851c0b2f7Stbbdev         tbb::auto_partitioner()
60951c0b2f7Stbbdev     );
61051c0b2f7Stbbdev     parallel_deterministic_reduce(
61151c0b2f7Stbbdev         blocked_range<int>(0, 10),
61251c0b2f7Stbbdev         0,
__anon6ca08c0e0402(const blocked_range<int>&, int init)61351c0b2f7Stbbdev         [](const blocked_range<int>&, int init)->int {
61451c0b2f7Stbbdev             return init;
61551c0b2f7Stbbdev         },
__anon6ca08c0e0502(int x, int y)61651c0b2f7Stbbdev         [](int x, int y)->int {
61751c0b2f7Stbbdev             return x + y;
61851c0b2f7Stbbdev         },
61951c0b2f7Stbbdev         ap
62051c0b2f7Stbbdev     );
62151c0b2f7Stbbdev }
62251c0b2f7Stbbdev 
62351c0b2f7Stbbdev //! Testing tbb::parallel_reduce with tbb::task_group_context
62451c0b2f7Stbbdev //! \brief \ref interface \ref error_guessing
62551c0b2f7Stbbdev TEST_CASE("cancellation test for tbb::parallel_reduce") {
62651c0b2f7Stbbdev     test_cancellation::ParallelReduceTestRunner</*First mode = */0>::run();
62751c0b2f7Stbbdev }
62851c0b2f7Stbbdev 
62951c0b2f7Stbbdev //! Testing tbb::parallel_deterministic_reduce with tbb::task_group_context
63051c0b2f7Stbbdev //! \brief \ref interface \ref error_guessing
63151c0b2f7Stbbdev TEST_CASE("cancellation test for tbb::parallel_deterministic_reduce") {
63251c0b2f7Stbbdev     test_cancellation::ParallelDeterministicReduceTestRunner</*First mode = */0>::run();
63351c0b2f7Stbbdev }
634478de5b1Stbbdev 
635478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
636478de5b1Stbbdev //! \brief \ref error_guessing
637478de5b1Stbbdev TEST_CASE("parallel_reduce constraints") {
638478de5b1Stbbdev     test_preduce_range_constraints();
639478de5b1Stbbdev     test_preduce_body_constraints();
640478de5b1Stbbdev     test_preduce_func_constraints();
641478de5b1Stbbdev     test_preduce_combine_constraints();
642478de5b1Stbbdev }
643478de5b1Stbbdev 
644478de5b1Stbbdev //! \brief \ref error_guessing
645478de5b1Stbbdev TEST_CASE("parallel_deterministic_reduce constraints") {
646478de5b1Stbbdev     test_pdet_reduce_range_constraints();
647478de5b1Stbbdev     test_pdet_reduce_body_constraints();
648478de5b1Stbbdev     test_pdet_reduce_func_constraints();
649478de5b1Stbbdev     test_pdet_reduce_combine_constraints();
650478de5b1Stbbdev }
651478de5b1Stbbdev #endif
65255f9b178SIvan Kochin 
65355f9b178SIvan Kochin #if _MSC_VER
65455f9b178SIvan Kochin #pragma warning (pop)
65555f9b178SIvan Kochin #endif
656