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 "common/test.h"
18478de5b1Stbbdev
19478de5b1Stbbdev #include "tbb/parallel_for.h"
20478de5b1Stbbdev
2151c0b2f7Stbbdev #include "common/config.h"
2251c0b2f7Stbbdev #include "common/utils.h"
2351c0b2f7Stbbdev #include "common/utils_concurrency_limit.h"
2451c0b2f7Stbbdev #include "common/utils_report.h"
2551c0b2f7Stbbdev #include "common/vector_types.h"
2651c0b2f7Stbbdev #include "common/cpu_usertime.h"
2751c0b2f7Stbbdev #include "common/spin_barrier.h"
2851c0b2f7Stbbdev #include "common/exception_handling.h"
29478de5b1Stbbdev #include "common/concepts_common.h"
30478de5b1Stbbdev #include "test_partitioner.h"
3151c0b2f7Stbbdev
32478de5b1Stbbdev #include <cstddef>
3351c0b2f7Stbbdev #include <vector>
3451c0b2f7Stbbdev
3551c0b2f7Stbbdev //! \file test_parallel_for.cpp
3651c0b2f7Stbbdev //! \brief Test for [algorithms.parallel_for] specification
3751c0b2f7Stbbdev
3851c0b2f7Stbbdev #if _MSC_VER
3951c0b2f7Stbbdev #pragma warning (push)
4055f9b178SIvan Kochin // Suppress conditional expression is constant
4155f9b178SIvan Kochin #pragma warning (disable: 4127)
4251c0b2f7Stbbdev #if __TBB_MSVC_UNREACHABLE_CODE_IGNORED
4351c0b2f7Stbbdev // Suppress pointless "unreachable code" warning.
4451c0b2f7Stbbdev #pragma warning (disable: 4702)
4551c0b2f7Stbbdev #endif
4651c0b2f7Stbbdev #if defined(_Wp64)
4751c0b2f7Stbbdev // Workaround for overzealous compiler warnings in /Wp64 mode
4851c0b2f7Stbbdev #pragma warning (disable: 4267)
4951c0b2f7Stbbdev #endif
5051c0b2f7Stbbdev #define _SCL_SECURE_NO_WARNINGS
5151c0b2f7Stbbdev #endif //#if _MSC_VER
5251c0b2f7Stbbdev
5351c0b2f7Stbbdev
5451c0b2f7Stbbdev #if (HAVE_m128 || HAVE_m256)
5551c0b2f7Stbbdev template<typename ClassWithVectorType>
5651c0b2f7Stbbdev struct SSE_Functor {
5751c0b2f7Stbbdev ClassWithVectorType* Src, * Dst;
SSE_FunctorSSE_Functor5851c0b2f7Stbbdev SSE_Functor( ClassWithVectorType* src, ClassWithVectorType* dst ) : Src(src), Dst(dst) {}
5951c0b2f7Stbbdev
operator ()SSE_Functor6051c0b2f7Stbbdev void operator()( tbb::blocked_range<int>& r ) const {
6151c0b2f7Stbbdev for( int i=r.begin(); i!=r.end(); ++i )
6251c0b2f7Stbbdev Dst[i] = Src[i];
6351c0b2f7Stbbdev }
6451c0b2f7Stbbdev };
6551c0b2f7Stbbdev
6651c0b2f7Stbbdev //! Test that parallel_for works with stack-allocated __m128
6751c0b2f7Stbbdev template<typename ClassWithVectorType>
TestVectorTypes()6851c0b2f7Stbbdev void TestVectorTypes() {
6951c0b2f7Stbbdev const int aSize = 300;
7051c0b2f7Stbbdev ClassWithVectorType Array1[aSize], Array2[aSize];
7151c0b2f7Stbbdev for( int i=0; i<aSize; ++i ) {
7251c0b2f7Stbbdev // VC8 does not properly align a temporary value; to work around, use explicit variable
7351c0b2f7Stbbdev ClassWithVectorType foo(i);
7451c0b2f7Stbbdev Array1[i] = foo;
7551c0b2f7Stbbdev }
7651c0b2f7Stbbdev tbb::parallel_for( tbb::blocked_range<int>(0,aSize), SSE_Functor<ClassWithVectorType>(Array1, Array2) );
7751c0b2f7Stbbdev for( int i=0; i<aSize; ++i ) {
7851c0b2f7Stbbdev ClassWithVectorType foo(i);
7951c0b2f7Stbbdev CHECK( Array2[i]==foo ) ;
8051c0b2f7Stbbdev }
8151c0b2f7Stbbdev }
8251c0b2f7Stbbdev #endif /* HAVE_m128 || HAVE_m256 */
8351c0b2f7Stbbdev
8451c0b2f7Stbbdev struct TestSimplePartitionerStabilityFunctor {
8551c0b2f7Stbbdev std::vector<int> & ranges;
TestSimplePartitionerStabilityFunctorTestSimplePartitionerStabilityFunctor8651c0b2f7Stbbdev TestSimplePartitionerStabilityFunctor(std::vector<int> & theRanges):ranges(theRanges){}
operator ()TestSimplePartitionerStabilityFunctor8751c0b2f7Stbbdev void operator()(tbb::blocked_range<size_t>& r)const{
8851c0b2f7Stbbdev ranges.at(r.begin()) = 1;
8951c0b2f7Stbbdev }
9051c0b2f7Stbbdev };
TestSimplePartitionerStability()9151c0b2f7Stbbdev void TestSimplePartitionerStability(){
9251c0b2f7Stbbdev const std::size_t repeat_count= 10;
9351c0b2f7Stbbdev const std::size_t rangeToSplitSize=1000000;
9451c0b2f7Stbbdev const std::size_t grainsizeStep=rangeToSplitSize/repeat_count;
9551c0b2f7Stbbdev typedef TestSimplePartitionerStabilityFunctor FunctorType;
9651c0b2f7Stbbdev
9751c0b2f7Stbbdev for (std::size_t i=0 , grainsize=grainsizeStep; i<repeat_count;i++, grainsize+=grainsizeStep){
9851c0b2f7Stbbdev std::vector<int> firstSeries(rangeToSplitSize,0);
9951c0b2f7Stbbdev std::vector<int> secondSeries(rangeToSplitSize,0);
10051c0b2f7Stbbdev
10151c0b2f7Stbbdev tbb::parallel_for(tbb::blocked_range<size_t>(0,rangeToSplitSize,grainsize),FunctorType(firstSeries),tbb::simple_partitioner());
10251c0b2f7Stbbdev tbb::parallel_for(tbb::blocked_range<size_t>(0,rangeToSplitSize,grainsize),FunctorType(secondSeries),tbb::simple_partitioner());
103478de5b1Stbbdev
104478de5b1Stbbdev CHECK_MESSAGE(
105478de5b1Stbbdev firstSeries == secondSeries,
106478de5b1Stbbdev "Splitting range with tbb::simple_partitioner must be reproducible; i = " << i
107478de5b1Stbbdev );
10851c0b2f7Stbbdev }
10951c0b2f7Stbbdev }
11051c0b2f7Stbbdev
11151c0b2f7Stbbdev namespace various_range_implementations {
11251c0b2f7Stbbdev
11351c0b2f7Stbbdev using namespace test_partitioner_utils;
11451c0b2f7Stbbdev using namespace test_partitioner_utils::TestRanges;
11551c0b2f7Stbbdev
11651c0b2f7Stbbdev // Body ensures that initial work distribution is done uniformly through affinity mechanism and not through work stealing
11751c0b2f7Stbbdev class Body {
11851c0b2f7Stbbdev utils::SpinBarrier &m_sb;
11951c0b2f7Stbbdev public:
Body(utils::SpinBarrier & sb)12051c0b2f7Stbbdev Body(utils::SpinBarrier& sb) : m_sb(sb) { }
Body(Body & b,tbb::split)12151c0b2f7Stbbdev Body(Body& b, tbb::split) : m_sb(b.m_sb) { }
12251c0b2f7Stbbdev
12351c0b2f7Stbbdev template <typename Range>
operator ()(Range & r) const12451c0b2f7Stbbdev void operator()(Range& r) const {
12551c0b2f7Stbbdev INFO("Executing range [" << r.begin() << ", " << r.end() << "]");
126b15aabb3Stbbdev m_sb.wait(); // waiting for all threads
12751c0b2f7Stbbdev }
12851c0b2f7Stbbdev };
12951c0b2f7Stbbdev
13051c0b2f7Stbbdev namespace correctness {
13151c0b2f7Stbbdev
13251c0b2f7Stbbdev /* Testing only correctness (that is parallel_for does not hang) */
13351c0b2f7Stbbdev template <typename RangeType, bool /* feedback */, bool ensure_non_emptiness>
test()13451c0b2f7Stbbdev void test() {
13557f524caSIlya Isaev RangeType range( 0, utils::get_platform_max_threads(), nullptr, false, ensure_non_emptiness );
13651c0b2f7Stbbdev tbb::affinity_partitioner ap;
13751c0b2f7Stbbdev tbb::parallel_for( range, SimpleBody(), ap );
13851c0b2f7Stbbdev }
13951c0b2f7Stbbdev
14051c0b2f7Stbbdev } // namespace correctness
14151c0b2f7Stbbdev
14251c0b2f7Stbbdev namespace uniform_distribution {
14351c0b2f7Stbbdev
14451c0b2f7Stbbdev /* Body of parallel_for algorithm would hang if non-uniform work distribution happened */
14551c0b2f7Stbbdev template <typename RangeType, bool feedback, bool ensure_non_emptiness>
test()14651c0b2f7Stbbdev void test() {
14751c0b2f7Stbbdev static const std::size_t thread_num = utils::get_platform_max_threads();
14851c0b2f7Stbbdev utils::SpinBarrier sb( thread_num );
14957f524caSIlya Isaev RangeType range(0, thread_num, nullptr, feedback, ensure_non_emptiness);
15051c0b2f7Stbbdev const Body sync_body( sb );
15151c0b2f7Stbbdev tbb::affinity_partitioner ap;
15251c0b2f7Stbbdev tbb::parallel_for( range, sync_body, ap );
15351c0b2f7Stbbdev tbb::parallel_for( range, sync_body, tbb::static_partitioner() );
15451c0b2f7Stbbdev }
15551c0b2f7Stbbdev
15651c0b2f7Stbbdev } // namespace uniform_distribution
15751c0b2f7Stbbdev
test()15851c0b2f7Stbbdev void test() {
15951c0b2f7Stbbdev const bool provide_feedback = false;
16051c0b2f7Stbbdev const bool ensure_non_empty_range = true;
16151c0b2f7Stbbdev
16251c0b2f7Stbbdev // BlockedRange does not take into account feedback and non-emptiness settings but uses the
16351c0b2f7Stbbdev // tbb::blocked_range implementation
16451c0b2f7Stbbdev uniform_distribution::test<BlockedRange, !provide_feedback, !ensure_non_empty_range>();
16551c0b2f7Stbbdev using correctness::test;
16651c0b2f7Stbbdev
16751c0b2f7Stbbdev {
16851c0b2f7Stbbdev test<RoundedDownRange, provide_feedback, ensure_non_empty_range>();
16951c0b2f7Stbbdev test<RoundedDownRange, provide_feedback, !ensure_non_empty_range>();
17051c0b2f7Stbbdev }
17151c0b2f7Stbbdev
17251c0b2f7Stbbdev {
17351c0b2f7Stbbdev test<RoundedUpRange, provide_feedback, ensure_non_empty_range>();
17451c0b2f7Stbbdev test<RoundedUpRange, provide_feedback, !ensure_non_empty_range>();
17551c0b2f7Stbbdev }
17651c0b2f7Stbbdev
17751c0b2f7Stbbdev // Testing that parallel_for algorithm works with such weird ranges
17851c0b2f7Stbbdev correctness::test<Range1_2, /* provide_feedback= */ false, !ensure_non_empty_range>();
17951c0b2f7Stbbdev correctness::test<Range1_999, /* provide_feedback= */ false, !ensure_non_empty_range>();
18051c0b2f7Stbbdev correctness::test<Range999_1, /* provide_feedback= */ false, !ensure_non_empty_range>();
18151c0b2f7Stbbdev
18251c0b2f7Stbbdev // The following ranges do not comply with the proportion suggested by partitioner. Therefore
18351c0b2f7Stbbdev // they have to provide the proportion in which they were actually split back to partitioner and
18451c0b2f7Stbbdev // ensure theirs non-emptiness
18551c0b2f7Stbbdev test<Range1_2, provide_feedback, ensure_non_empty_range>();
18651c0b2f7Stbbdev test<Range1_999, provide_feedback, ensure_non_empty_range>();
18751c0b2f7Stbbdev test<Range999_1, provide_feedback, ensure_non_empty_range>();
18851c0b2f7Stbbdev }
18951c0b2f7Stbbdev
19051c0b2f7Stbbdev } // namespace various_range_implementations
19151c0b2f7Stbbdev
19251c0b2f7Stbbdev namespace test_cancellation {
19351c0b2f7Stbbdev
19451c0b2f7Stbbdev struct FunctorToCancel {
19551c0b2f7Stbbdev static std::atomic<bool> need_to_wait;
19651c0b2f7Stbbdev
operator ()test_cancellation::FunctorToCancel19751c0b2f7Stbbdev void operator()( std::size_t ) const {
19851c0b2f7Stbbdev ++g_CurExecuted;
19951c0b2f7Stbbdev if (need_to_wait) {
20051c0b2f7Stbbdev need_to_wait = Cancellator::WaitUntilReady();
20151c0b2f7Stbbdev }
20251c0b2f7Stbbdev }
20351c0b2f7Stbbdev
operator ()test_cancellation::FunctorToCancel20451c0b2f7Stbbdev void operator()( const tbb::blocked_range<std::size_t>& ) const {
20551c0b2f7Stbbdev ++g_CurExecuted;
20651c0b2f7Stbbdev Cancellator::WaitUntilReady();
20751c0b2f7Stbbdev }
20851c0b2f7Stbbdev
resettest_cancellation::FunctorToCancel20951c0b2f7Stbbdev static void reset() { need_to_wait = true; }
21051c0b2f7Stbbdev }; // struct FunctorToCancel
21151c0b2f7Stbbdev
21251c0b2f7Stbbdev std::atomic<bool> FunctorToCancel::need_to_wait(true);
21351c0b2f7Stbbdev
21451c0b2f7Stbbdev static constexpr std::size_t buffer_test_size = 1024;
21551c0b2f7Stbbdev static constexpr std::size_t maxParallelForRunnerMode = 14;
21651c0b2f7Stbbdev
21751c0b2f7Stbbdev template <std::size_t Mode>
21851c0b2f7Stbbdev class ParallelForRunner {
21951c0b2f7Stbbdev tbb::task_group_context& my_ctx;
22051c0b2f7Stbbdev const std::size_t worker_task_step = 1;
22151c0b2f7Stbbdev
22251c0b2f7Stbbdev static_assert(Mode >= 0 && Mode <= maxParallelForRunnerMode, "Incorrect mode for ParallelForRunner");
22351c0b2f7Stbbdev
22451c0b2f7Stbbdev template <typename Partitioner, typename... Args>
run_parallel_for(Args &&...args) const22551c0b2f7Stbbdev void run_parallel_for( Args&&... args ) const {
22651c0b2f7Stbbdev Partitioner part;
22751c0b2f7Stbbdev tbb::parallel_for(std::forward<Args>(args)..., part, my_ctx);
22851c0b2f7Stbbdev }
22951c0b2f7Stbbdev
23051c0b2f7Stbbdev template <typename... Args>
run_overload(Args &&...args) const23151c0b2f7Stbbdev void run_overload( Args&&... args ) const {
23251c0b2f7Stbbdev
23351c0b2f7Stbbdev switch(Mode % 5) {
23451c0b2f7Stbbdev case 0 : {
23551c0b2f7Stbbdev tbb::parallel_for(std::forward<Args>(args)..., my_ctx);
23651c0b2f7Stbbdev break;
23751c0b2f7Stbbdev }
23851c0b2f7Stbbdev case 1 : {
23951c0b2f7Stbbdev run_parallel_for<tbb::simple_partitioner>(std::forward<Args>(args)...);
24051c0b2f7Stbbdev break;
24151c0b2f7Stbbdev }
24251c0b2f7Stbbdev case 2 : {
24351c0b2f7Stbbdev run_parallel_for<tbb::auto_partitioner>(std::forward<Args>(args)...);
24451c0b2f7Stbbdev break;
24551c0b2f7Stbbdev }
24651c0b2f7Stbbdev case 3 : {
24751c0b2f7Stbbdev run_parallel_for<tbb::static_partitioner>(std::forward<Args>(args)...);
24851c0b2f7Stbbdev break;
24951c0b2f7Stbbdev }
25051c0b2f7Stbbdev case 4 : {
25151c0b2f7Stbbdev run_parallel_for<tbb::affinity_partitioner>(std::forward<Args>(args)...);
25251c0b2f7Stbbdev break;
25351c0b2f7Stbbdev }
25451c0b2f7Stbbdev }
25551c0b2f7Stbbdev }
25651c0b2f7Stbbdev
25751c0b2f7Stbbdev public:
ParallelForRunner(tbb::task_group_context & ctx)25851c0b2f7Stbbdev ParallelForRunner( tbb::task_group_context& ctx )
25951c0b2f7Stbbdev : my_ctx(ctx) {}
26051c0b2f7Stbbdev
~ParallelForRunner()26151c0b2f7Stbbdev ~ParallelForRunner() { FunctorToCancel::reset(); }
26251c0b2f7Stbbdev
operator ()() const26351c0b2f7Stbbdev void operator()() const {
26451c0b2f7Stbbdev if (Mode < 5) {
26551c0b2f7Stbbdev // Overload with blocked range
26651c0b2f7Stbbdev tbb::blocked_range<std::size_t> br(0, buffer_test_size);
26751c0b2f7Stbbdev run_overload(br, FunctorToCancel{});
26851c0b2f7Stbbdev } else if (Mode < 10) {
26951c0b2f7Stbbdev // Overload with two indexes
27051c0b2f7Stbbdev run_overload(std::size_t(0), buffer_test_size, FunctorToCancel{});
27151c0b2f7Stbbdev } else {
27251c0b2f7Stbbdev // Overload with two indexes and step
27351c0b2f7Stbbdev run_overload(std::size_t(0), buffer_test_size, worker_task_step, FunctorToCancel{});
27451c0b2f7Stbbdev }
27551c0b2f7Stbbdev }
27651c0b2f7Stbbdev }; // class ParallelForRunner
27751c0b2f7Stbbdev
27851c0b2f7Stbbdev template <std::size_t Mode>
run_parallel_for_cancellation_test()27951c0b2f7Stbbdev void run_parallel_for_cancellation_test() {
28051c0b2f7Stbbdev // TODO: enable concurrency_range
281478de5b1Stbbdev if (utils::get_platform_max_threads() < 2) {
282478de5b1Stbbdev // The test requires at least one worker thread to request cancellation
283478de5b1Stbbdev return;
284478de5b1Stbbdev }
28551c0b2f7Stbbdev ResetEhGlobals();
28651c0b2f7Stbbdev RunCancellationTest<ParallelForRunner<Mode>, Cancellator>();
28751c0b2f7Stbbdev }
28851c0b2f7Stbbdev
28951c0b2f7Stbbdev template <std::size_t Mode>
29051c0b2f7Stbbdev struct ParallelForTestRunner {
runtest_cancellation::ParallelForTestRunner29151c0b2f7Stbbdev static void run() {
29251c0b2f7Stbbdev run_parallel_for_cancellation_test<Mode>();
29351c0b2f7Stbbdev ParallelForTestRunner<Mode + 1>::run();
29451c0b2f7Stbbdev }
29551c0b2f7Stbbdev }; // struct ParallelForTestRunner
29651c0b2f7Stbbdev
29751c0b2f7Stbbdev template <>
29851c0b2f7Stbbdev struct ParallelForTestRunner<maxParallelForRunnerMode> {
runtest_cancellation::ParallelForTestRunner29951c0b2f7Stbbdev static void run() {
30051c0b2f7Stbbdev run_parallel_for_cancellation_test<maxParallelForRunnerMode>();
30151c0b2f7Stbbdev }
30251c0b2f7Stbbdev }; // struct ParallelForTestRunner<maxParallelForRunnerMode>
30351c0b2f7Stbbdev
30451c0b2f7Stbbdev } // namespace test_cancellation
30551c0b2f7Stbbdev
306478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
307478de5b1Stbbdev template <typename... Args>
308478de5b1Stbbdev concept can_call_parallel_for_basic = requires( Args&&... args ) {
309478de5b1Stbbdev tbb::parallel_for(std::forward<Args>(args)...);
310478de5b1Stbbdev };
311478de5b1Stbbdev
312478de5b1Stbbdev template <typename... Args>
313478de5b1Stbbdev concept can_call_parallel_for_helper = can_call_parallel_for_basic<Args...> &&
314478de5b1Stbbdev can_call_parallel_for_basic<Args..., tbb::task_group_context&>;
315478de5b1Stbbdev
316478de5b1Stbbdev template <typename... Args>
317478de5b1Stbbdev concept can_call_parallel_for_with_partitioner = can_call_parallel_for_helper<Args...> &&
318478de5b1Stbbdev can_call_parallel_for_helper<Args..., const tbb::simple_partitioner&> &&
319478de5b1Stbbdev can_call_parallel_for_helper<Args..., const tbb::auto_partitioner&> &&
320478de5b1Stbbdev can_call_parallel_for_helper<Args..., const tbb::static_partitioner> &&
321478de5b1Stbbdev can_call_parallel_for_helper<Args..., tbb::affinity_partitioner&>;
322478de5b1Stbbdev
323478de5b1Stbbdev template <typename Range, typename Body>
324478de5b1Stbbdev concept can_call_range_pfor = can_call_parallel_for_with_partitioner<const Range&, const Body&>;
325478de5b1Stbbdev
326478de5b1Stbbdev template <typename Index, typename Function>
327478de5b1Stbbdev concept can_call_index_pfor = can_call_parallel_for_with_partitioner<Index, Index, const Function&> &&
328478de5b1Stbbdev can_call_parallel_for_with_partitioner<Index, Index, Index, const Function&>;
329478de5b1Stbbdev
330478de5b1Stbbdev
331478de5b1Stbbdev template <typename Range>
332478de5b1Stbbdev using CorrectBody = test_concepts::parallel_for_body::Correct<Range>;
333478de5b1Stbbdev template <typename Index>
334478de5b1Stbbdev using CorrectFunc = test_concepts::parallel_for_function::Correct<Index>;
335478de5b1Stbbdev
test_pfor_range_constraints()336478de5b1Stbbdev void test_pfor_range_constraints() {
337478de5b1Stbbdev using namespace test_concepts::range;
338478de5b1Stbbdev
339478de5b1Stbbdev static_assert(can_call_range_pfor<Correct, CorrectBody<Correct>>);
340478de5b1Stbbdev static_assert(!can_call_range_pfor<NonCopyable, CorrectBody<NonCopyable>>);
341478de5b1Stbbdev static_assert(!can_call_range_pfor<NonSplittable, CorrectBody<NonSplittable>>);
342478de5b1Stbbdev static_assert(!can_call_range_pfor<NonDestructible, CorrectBody<NonDestructible>>);
343478de5b1Stbbdev static_assert(!can_call_range_pfor<NoEmpty, CorrectBody<NoEmpty>>);
344478de5b1Stbbdev static_assert(!can_call_range_pfor<EmptyNonConst, CorrectBody<EmptyNonConst>>);
345478de5b1Stbbdev static_assert(!can_call_range_pfor<WrongReturnEmpty, CorrectBody<WrongReturnEmpty>>);
346478de5b1Stbbdev static_assert(!can_call_range_pfor<NoIsDivisible, CorrectBody<NoIsDivisible>>);
347478de5b1Stbbdev static_assert(!can_call_range_pfor<IsDivisibleNonConst, CorrectBody<IsDivisibleNonConst>>);
348478de5b1Stbbdev static_assert(!can_call_range_pfor<WrongReturnIsDivisible, CorrectBody<WrongReturnIsDivisible>>);
349478de5b1Stbbdev }
350478de5b1Stbbdev
test_pfor_body_constraints()351478de5b1Stbbdev void test_pfor_body_constraints() {
352478de5b1Stbbdev using namespace test_concepts::parallel_for_body;
353478de5b1Stbbdev using CorrectRange = test_concepts::range::Correct;
354478de5b1Stbbdev
355478de5b1Stbbdev static_assert(can_call_range_pfor<CorrectRange, Correct<CorrectRange>>);
356478de5b1Stbbdev static_assert(!can_call_range_pfor<CorrectRange, NonCopyable<CorrectRange>>);
357478de5b1Stbbdev static_assert(!can_call_range_pfor<CorrectRange, NonDestructible<CorrectRange>>);
358478de5b1Stbbdev static_assert(!can_call_range_pfor<CorrectRange, NoOperatorRoundBrackets<CorrectRange>>);
359478de5b1Stbbdev static_assert(!can_call_range_pfor<CorrectRange, OperatorRoundBracketsNonConst<CorrectRange>>);
360478de5b1Stbbdev static_assert(!can_call_range_pfor<CorrectRange, WrongInputOperatorRoundBrackets<CorrectRange>>);
361478de5b1Stbbdev }
362478de5b1Stbbdev
test_pfor_func_constraints()363478de5b1Stbbdev void test_pfor_func_constraints() {
364478de5b1Stbbdev using namespace test_concepts::parallel_for_function;
365478de5b1Stbbdev using CorrectIndex = test_concepts::parallel_for_index::Correct;
366478de5b1Stbbdev
367478de5b1Stbbdev static_assert(can_call_index_pfor<CorrectIndex, Correct<CorrectIndex>>);
368478de5b1Stbbdev static_assert(!can_call_index_pfor<CorrectIndex, NoOperatorRoundBrackets<CorrectIndex>>);
369478de5b1Stbbdev static_assert(!can_call_index_pfor<CorrectIndex, OperatorRoundBracketsNonConst<CorrectIndex>>);
370478de5b1Stbbdev static_assert(!can_call_index_pfor<CorrectIndex, WrongInputOperatorRoundBrackets<CorrectIndex>>);
371478de5b1Stbbdev }
372478de5b1Stbbdev
test_pfor_index_constraints()373478de5b1Stbbdev void test_pfor_index_constraints() {
374478de5b1Stbbdev using namespace test_concepts::parallel_for_index;
375478de5b1Stbbdev static_assert(can_call_index_pfor<Correct, CorrectFunc<Correct>>);
376478de5b1Stbbdev static_assert(!can_call_index_pfor<NoIntCtor, CorrectFunc<NoIntCtor>>);
377478de5b1Stbbdev static_assert(!can_call_index_pfor<NonCopyable, CorrectFunc<NonCopyable>>);
378478de5b1Stbbdev static_assert(!can_call_index_pfor<NonCopyAssignable, CorrectFunc<NonCopyAssignable>>);
379478de5b1Stbbdev static_assert(!can_call_index_pfor<NonDestructible, CorrectFunc<NonDestructible>>);
380478de5b1Stbbdev static_assert(!can_call_index_pfor<NoOperatorLess, CorrectFunc<NoOperatorLess>>);
381478de5b1Stbbdev static_assert(!can_call_index_pfor<OperatorLessNonConst, CorrectFunc<OperatorLessNonConst>>);
382478de5b1Stbbdev static_assert(!can_call_index_pfor<WrongInputOperatorLess, CorrectFunc<WrongInputOperatorLess>>);
383478de5b1Stbbdev static_assert(!can_call_index_pfor<WrongReturnOperatorLess, CorrectFunc<WrongReturnOperatorLess>>);
384478de5b1Stbbdev static_assert(!can_call_index_pfor<NoOperatorMinus, CorrectFunc<NoOperatorMinus>>);
385478de5b1Stbbdev static_assert(!can_call_index_pfor<OperatorMinusNonConst, CorrectFunc<OperatorMinusNonConst>>);
386478de5b1Stbbdev static_assert(!can_call_index_pfor<WrongInputOperatorMinus, CorrectFunc<WrongInputOperatorMinus>>);
387478de5b1Stbbdev static_assert(!can_call_index_pfor<WrongReturnOperatorMinus, CorrectFunc<WrongReturnOperatorMinus>>);
388478de5b1Stbbdev static_assert(!can_call_index_pfor<NoOperatorPlus, CorrectFunc<NoOperatorPlus>>);
389478de5b1Stbbdev static_assert(!can_call_index_pfor<OperatorPlusNonConst, CorrectFunc<OperatorPlusNonConst>>);
390478de5b1Stbbdev static_assert(!can_call_index_pfor<WrongInputOperatorPlus, CorrectFunc<WrongInputOperatorPlus>>);
391478de5b1Stbbdev static_assert(!can_call_index_pfor<WrongReturnOperatorPlus, CorrectFunc<WrongReturnOperatorPlus>>);
392478de5b1Stbbdev }
393478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
394478de5b1Stbbdev
39551c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN && TBB_REVAMP_TODO
396478de5b1Stbbdev #include "tbb/global_control.h"
39751c0b2f7Stbbdev //! Testing exceptions
39851c0b2f7Stbbdev //! \brief \ref requirement
39951c0b2f7Stbbdev TEST_CASE("Exceptions support") {
40051c0b2f7Stbbdev for ( int p = MinThread; p <= MaxThread; ++p ) {
40151c0b2f7Stbbdev if ( p > 0 ) {
40251c0b2f7Stbbdev tbb::global_control control(tbb::global_control::max_allowed_parallelism, p);
40351c0b2f7Stbbdev TestExceptionsSupport();
40451c0b2f7Stbbdev }
40551c0b2f7Stbbdev }
40651c0b2f7Stbbdev }
40751c0b2f7Stbbdev #endif /* TBB_USE_EXCEPTIONS && !__TBB_THROW_ACROSS_MODULE_BOUNDARY_BROKEN */
40851c0b2f7Stbbdev
40951c0b2f7Stbbdev //! Testing cancellation
41051c0b2f7Stbbdev //! \brief \ref error_guessing
41151c0b2f7Stbbdev TEST_CASE("Vector types") {
41251c0b2f7Stbbdev #if HAVE_m128
41351c0b2f7Stbbdev TestVectorTypes<ClassWithSSE>();
41451c0b2f7Stbbdev #endif
41551c0b2f7Stbbdev #if HAVE_m256
41651c0b2f7Stbbdev if (have_AVX()) TestVectorTypes<ClassWithAVX>();
41751c0b2f7Stbbdev #endif
41851c0b2f7Stbbdev }
41951c0b2f7Stbbdev
42051c0b2f7Stbbdev //! Testing workers going to sleep
42151c0b2f7Stbbdev //! \brief \ref resource_usage
42251c0b2f7Stbbdev TEST_CASE("That all workers sleep when no work") {
42351c0b2f7Stbbdev const std::size_t N = 100000;
42451c0b2f7Stbbdev std::atomic<int> counter{};
42551c0b2f7Stbbdev
__anon95d702fd0102(std::size_t) 42651c0b2f7Stbbdev tbb::parallel_for(std::size_t(0), N, [&](std::size_t) {
427b15aabb3Stbbdev for (int i = 0; i < 1000; ++i) {
42851c0b2f7Stbbdev ++counter;
42951c0b2f7Stbbdev }
43051c0b2f7Stbbdev }, tbb::simple_partitioner());
43151c0b2f7Stbbdev TestCPUUserTime(utils::get_platform_max_threads());
43251c0b2f7Stbbdev }
43351c0b2f7Stbbdev
43451c0b2f7Stbbdev //! Testing simple partitioner stability
43551c0b2f7Stbbdev //! \brief \ref error_guessing
43651c0b2f7Stbbdev TEST_CASE("Simple partitioner stability") {
43751c0b2f7Stbbdev TestSimplePartitionerStability();
43851c0b2f7Stbbdev }
43951c0b2f7Stbbdev
44051c0b2f7Stbbdev //! Testing various range implementations
44151c0b2f7Stbbdev //! \brief \ref requirement
44251c0b2f7Stbbdev TEST_CASE("Various range implementations") {
44351c0b2f7Stbbdev various_range_implementations::test();
44451c0b2f7Stbbdev }
44551c0b2f7Stbbdev
44651c0b2f7Stbbdev //! Testing parallel_for with explicit task_group_context
44751c0b2f7Stbbdev //! \brief \ref interface \ref error_guessing
44851c0b2f7Stbbdev TEST_CASE("Сancellation test for tbb::parallel_for") {
44951c0b2f7Stbbdev test_cancellation::ParallelForTestRunner</*FirstMode = */0>::run();
45051c0b2f7Stbbdev }
45151c0b2f7Stbbdev
452478de5b1Stbbdev #if __TBB_CPP20_CONCEPTS_PRESENT
453478de5b1Stbbdev //! \brief \ref error_guessing
454478de5b1Stbbdev TEST_CASE("parallel_for constraints") {
455478de5b1Stbbdev test_pfor_range_constraints();
456478de5b1Stbbdev test_pfor_body_constraints();
457478de5b1Stbbdev test_pfor_func_constraints();
458478de5b1Stbbdev test_pfor_index_constraints();
459478de5b1Stbbdev }
460478de5b1Stbbdev #endif // __TBB_CPP20_CONCEPTS_PRESENT
461478de5b1Stbbdev
46251c0b2f7Stbbdev #if _MSC_VER
46351c0b2f7Stbbdev #pragma warning (pop)
46451c0b2f7Stbbdev #endif
465