151c0b2f7Stbbdev /*
2b15aabb3Stbbdev     Copyright (c) 2005-2021 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"
1851c0b2f7Stbbdev #include "common/utils.h"
1951c0b2f7Stbbdev #include "common/utils_report.h"
2051c0b2f7Stbbdev #include "common/spin_barrier.h"
2151c0b2f7Stbbdev #include "common/checktype.h"
2251c0b2f7Stbbdev 
2351c0b2f7Stbbdev #include "common/container_move_support.h"
2451c0b2f7Stbbdev 
2549e08aacStbbdev #include "oneapi/tbb/combinable.h"
2649e08aacStbbdev #include "oneapi/tbb/parallel_for.h"
2749e08aacStbbdev #include "oneapi/tbb/blocked_range.h"
2849e08aacStbbdev #include "oneapi/tbb/global_control.h"
2949e08aacStbbdev #include "oneapi/tbb/tbb_allocator.h"
3049e08aacStbbdev // INFO: #include "oneapi/tbb/tick_count.h"
3151c0b2f7Stbbdev 
3251c0b2f7Stbbdev #include <cstring>
3351c0b2f7Stbbdev #include <vector>
3451c0b2f7Stbbdev #include <utility>
3551c0b2f7Stbbdev 
3651c0b2f7Stbbdev //! \file conformance_combinable.cpp
3751c0b2f7Stbbdev //! \brief Test for [tls.combinable] specification
3851c0b2f7Stbbdev 
3951c0b2f7Stbbdev //! Minimum number of threads
4051c0b2f7Stbbdev const int MinThread = 1;
4151c0b2f7Stbbdev 
4251c0b2f7Stbbdev //! Maximum number of threads
4351c0b2f7Stbbdev const int MaxThread = 4;
4451c0b2f7Stbbdev 
4551c0b2f7Stbbdev static std::atomic<int> construction_counter;
4651c0b2f7Stbbdev static std::atomic<int> destruction_counter;
4751c0b2f7Stbbdev 
4851c0b2f7Stbbdev const int REPETITIONS = 10;
4951c0b2f7Stbbdev const int N = 100000;
5051c0b2f7Stbbdev const double EXPECTED_SUM = (REPETITIONS + 1) * N;
5151c0b2f7Stbbdev 
5251c0b2f7Stbbdev //
5351c0b2f7Stbbdev // A minimal class
5451c0b2f7Stbbdev // Define: default and copy constructor, and allow implicit operator&
5551c0b2f7Stbbdev // also operator=
5651c0b2f7Stbbdev //
5751c0b2f7Stbbdev 
58*90b0671bSAlex class minimalCombinable {
5951c0b2f7Stbbdev private:
6051c0b2f7Stbbdev     int my_value;
6151c0b2f7Stbbdev public:
minimalCombinable(int val=0)62*90b0671bSAlex     minimalCombinable(int val=0) : my_value(val) { ++construction_counter; }
minimalCombinable(const minimalCombinable & m)63*90b0671bSAlex     minimalCombinable( const minimalCombinable&m ) : my_value(m.my_value) { ++construction_counter; }
operator =(const minimalCombinable & other)64*90b0671bSAlex     minimalCombinable& operator=(const minimalCombinable& other) { my_value = other.my_value; return *this; }
operator +=(const minimalCombinable & other)65*90b0671bSAlex     minimalCombinable& operator+=(const minimalCombinable& other) { my_value += other.my_value; return *this; }
operator int() const6651c0b2f7Stbbdev     operator int() const { return my_value; }
~minimalCombinable()67*90b0671bSAlex     ~minimalCombinable() { ++destruction_counter; }
set_value(const int i)6851c0b2f7Stbbdev     void set_value( const int i ) { my_value = i; }
value() const6951c0b2f7Stbbdev     int value( ) const { return my_value; }
7051c0b2f7Stbbdev };
7151c0b2f7Stbbdev 
7251c0b2f7Stbbdev //// functors for initialization and combine
7351c0b2f7Stbbdev 
7451c0b2f7Stbbdev template <typename T>
7551c0b2f7Stbbdev struct FunctorAddFinit {
operator ()FunctorAddFinit7651c0b2f7Stbbdev     T operator()() { return 0; }
7751c0b2f7Stbbdev };
7851c0b2f7Stbbdev 
7951c0b2f7Stbbdev template <typename T>
8051c0b2f7Stbbdev struct FunctorAddFinit7 {
operator ()FunctorAddFinit78151c0b2f7Stbbdev     T operator()() { return 7; }
8251c0b2f7Stbbdev };
8351c0b2f7Stbbdev 
8451c0b2f7Stbbdev template <typename T>
8551c0b2f7Stbbdev struct FunctorAddCombine {
operator ()FunctorAddCombine8651c0b2f7Stbbdev     T operator()(T left, T right ) const {
8751c0b2f7Stbbdev         return left + right;
8851c0b2f7Stbbdev     }
8951c0b2f7Stbbdev };
9051c0b2f7Stbbdev 
9151c0b2f7Stbbdev template <typename T>
9251c0b2f7Stbbdev struct FunctorAddCombineRef {
operator ()FunctorAddCombineRef9351c0b2f7Stbbdev     T operator()(const T& left, const T& right ) const {
9451c0b2f7Stbbdev         return left + right;
9551c0b2f7Stbbdev     }
9651c0b2f7Stbbdev };
9751c0b2f7Stbbdev 
9851c0b2f7Stbbdev template <typename T>
my_combine(T left,T right)9951c0b2f7Stbbdev T my_combine( T left, T right) { return left + right; }
10051c0b2f7Stbbdev 
10151c0b2f7Stbbdev template <typename T>
my_combine_ref(const T & left,const T & right)10251c0b2f7Stbbdev T my_combine_ref( const T &left, const T &right) { return left + right; }
10351c0b2f7Stbbdev 
10451c0b2f7Stbbdev template <typename T>
10551c0b2f7Stbbdev class CombineEachHelper {
10651c0b2f7Stbbdev public:
CombineEachHelper(T & _result)10751c0b2f7Stbbdev     CombineEachHelper(T& _result) : my_result(_result) {}
operator ()(const T & new_bit)10851c0b2f7Stbbdev     void operator()(const T& new_bit) { my_result +=  new_bit; }
10951c0b2f7Stbbdev private:
11051c0b2f7Stbbdev     T& my_result;
11151c0b2f7Stbbdev };
11251c0b2f7Stbbdev 
11351c0b2f7Stbbdev template <typename T>
11451c0b2f7Stbbdev class CombineEachHelperCnt {
11551c0b2f7Stbbdev public:
CombineEachHelperCnt(T & _result,int & _nbuckets)11651c0b2f7Stbbdev     CombineEachHelperCnt(T& _result, int& _nbuckets) : my_result(_result), nBuckets(_nbuckets) {}
operator ()(const T & new_bit)11751c0b2f7Stbbdev     void operator()(const T& new_bit) { my_result +=  new_bit; ++nBuckets; }
11851c0b2f7Stbbdev private:
11951c0b2f7Stbbdev     T& my_result;
12051c0b2f7Stbbdev     int& nBuckets;
12151c0b2f7Stbbdev };
12251c0b2f7Stbbdev 
12351c0b2f7Stbbdev template <typename T>
12451c0b2f7Stbbdev class CombineEachVectorHelper {
12551c0b2f7Stbbdev public:
12649e08aacStbbdev     typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > ContainerType;
CombineEachVectorHelper(T & _result)12751c0b2f7Stbbdev     CombineEachVectorHelper(T& _result) : my_result(_result) { }
operator ()(const ContainerType & new_bit)12851c0b2f7Stbbdev     void operator()(const ContainerType& new_bit) {
12951c0b2f7Stbbdev         for(typename ContainerType::const_iterator ci = new_bit.begin(); ci != new_bit.end(); ++ci) {
13051c0b2f7Stbbdev             my_result +=  *ci;
13151c0b2f7Stbbdev         }
13251c0b2f7Stbbdev     }
13351c0b2f7Stbbdev 
13451c0b2f7Stbbdev private:
13551c0b2f7Stbbdev     T& my_result;
13651c0b2f7Stbbdev };
13751c0b2f7Stbbdev 
13851c0b2f7Stbbdev //// end functors
13951c0b2f7Stbbdev 
14051c0b2f7Stbbdev // parallel body with a test for first access
14151c0b2f7Stbbdev template <typename T>
14251c0b2f7Stbbdev class ParallelScalarBody: utils::NoAssign {
14351c0b2f7Stbbdev 
14449e08aacStbbdev     oneapi::tbb::combinable<T> &sums;
14551c0b2f7Stbbdev 
14651c0b2f7Stbbdev public:
14751c0b2f7Stbbdev 
ParallelScalarBody(oneapi::tbb::combinable<T> & _sums)14849e08aacStbbdev     ParallelScalarBody ( oneapi::tbb::combinable<T> &_sums ) : sums(_sums) { }
14951c0b2f7Stbbdev 
operator ()(const oneapi::tbb::blocked_range<int> & r) const15049e08aacStbbdev     void operator()( const oneapi::tbb::blocked_range<int> &r ) const {
15151c0b2f7Stbbdev         for (int i = r.begin(); i != r.end(); ++i) {
15251c0b2f7Stbbdev             bool was_there;
15351c0b2f7Stbbdev             T& my_local = sums.local(was_there);
15451c0b2f7Stbbdev             if(!was_there) my_local = 0;
15551c0b2f7Stbbdev              my_local +=  1 ;
15651c0b2f7Stbbdev         }
15751c0b2f7Stbbdev     }
15851c0b2f7Stbbdev 
15951c0b2f7Stbbdev };
16051c0b2f7Stbbdev 
16151c0b2f7Stbbdev // parallel body with no test for first access
16251c0b2f7Stbbdev template <typename T>
16351c0b2f7Stbbdev class ParallelScalarBodyNoInit: utils::NoAssign {
16451c0b2f7Stbbdev 
16549e08aacStbbdev     oneapi::tbb::combinable<T> &sums;
16651c0b2f7Stbbdev 
16751c0b2f7Stbbdev public:
16851c0b2f7Stbbdev 
ParallelScalarBodyNoInit(oneapi::tbb::combinable<T> & _sums)16949e08aacStbbdev     ParallelScalarBodyNoInit ( oneapi::tbb::combinable<T> &_sums ) : sums(_sums) { }
17051c0b2f7Stbbdev 
operator ()(const oneapi::tbb::blocked_range<int> & r) const17149e08aacStbbdev     void operator()( const oneapi::tbb::blocked_range<int> &r ) const {
17251c0b2f7Stbbdev         for (int i = r.begin(); i != r.end(); ++i) {
17351c0b2f7Stbbdev              sums.local() +=  1 ;
17451c0b2f7Stbbdev         }
17551c0b2f7Stbbdev     }
17651c0b2f7Stbbdev 
17751c0b2f7Stbbdev };
17851c0b2f7Stbbdev 
17951c0b2f7Stbbdev template< typename T >
RunParallelScalarTests(const char *)18051c0b2f7Stbbdev void RunParallelScalarTests(const char* /* test_name */) {
18151c0b2f7Stbbdev     for (int p = MinThread; p <= MaxThread; ++p) {
18251c0b2f7Stbbdev 
18351c0b2f7Stbbdev         if (p == 0) continue;
18451c0b2f7Stbbdev         // REMARK("  Testing parallel %s on %d thread(s)...\n", test_name, p);
18549e08aacStbbdev         oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
18651c0b2f7Stbbdev 
18749e08aacStbbdev         // INFO: oneapi::tbb::tick_count t0;
18851c0b2f7Stbbdev         T combine_sum(0);
18951c0b2f7Stbbdev         T combine_ref_sum(0);
19051c0b2f7Stbbdev         T combine_finit_sum(0);
19151c0b2f7Stbbdev         T combine_each_sum(0);
19251c0b2f7Stbbdev         T copy_construct_sum(0);
19351c0b2f7Stbbdev         T copy_assign_sum(0);
19451c0b2f7Stbbdev         T move_construct_sum(0);
19551c0b2f7Stbbdev         T move_assign_sum(0);
19651c0b2f7Stbbdev 
19751c0b2f7Stbbdev         for (int t = -1; t < REPETITIONS; ++t) {
19849e08aacStbbdev             // INFO: if (Verbose && t == 0) t0 = oneapi::tbb::tick_count::now();
19951c0b2f7Stbbdev 
20051c0b2f7Stbbdev             // test uninitialized parallel combinable
20149e08aacStbbdev             oneapi::tbb::combinable<T> sums;
20249e08aacStbbdev             oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>( 0, N, 10000 ), ParallelScalarBody<T>( sums ) );
20351c0b2f7Stbbdev             combine_sum += sums.combine(my_combine<T>);
20451c0b2f7Stbbdev             combine_ref_sum += sums.combine(my_combine_ref<T>);
20551c0b2f7Stbbdev 
20651c0b2f7Stbbdev             // test combinable::clear()
20749e08aacStbbdev             oneapi::tbb::combinable<T> sums_to_clear;
20849e08aacStbbdev             oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>(0, N, 10000), ParallelScalarBody<T>(sums_to_clear) );
20951c0b2f7Stbbdev             sums_to_clear.clear();
21051c0b2f7Stbbdev             CHECK_MESSAGE(sums_to_clear.combine(my_combine<T>) == 0, "Failed combinable::clear test");
21151c0b2f7Stbbdev 
21251c0b2f7Stbbdev             // test parallel combinable preinitialized with a functor that returns 0
21351c0b2f7Stbbdev             FunctorAddFinit<T> my_finit_decl;
21449e08aacStbbdev             oneapi::tbb::combinable<T> finit_combinable(my_finit_decl);
21549e08aacStbbdev             oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>( 0, N, 10000 ), ParallelScalarBodyNoInit<T>( finit_combinable ) );
21651c0b2f7Stbbdev             combine_finit_sum += finit_combinable.combine(my_combine<T>);
21751c0b2f7Stbbdev 
21851c0b2f7Stbbdev             // test another way of combining the elements using CombineEachHelper<T> functor
21951c0b2f7Stbbdev             CombineEachHelper<T> my_helper(combine_each_sum);
22051c0b2f7Stbbdev             sums.combine_each(my_helper);
22151c0b2f7Stbbdev 
22251c0b2f7Stbbdev             // test copy constructor for parallel combinable
22349e08aacStbbdev             oneapi::tbb::combinable<T> copy_constructed(sums);
22451c0b2f7Stbbdev             copy_construct_sum += copy_constructed.combine(my_combine<T>);
22551c0b2f7Stbbdev 
22651c0b2f7Stbbdev             // test copy assignment for uninitialized parallel combinable
22749e08aacStbbdev             oneapi::tbb::combinable<T> assigned;
22851c0b2f7Stbbdev             assigned = sums;
22951c0b2f7Stbbdev             copy_assign_sum += assigned.combine(my_combine<T>);
23051c0b2f7Stbbdev 
23151c0b2f7Stbbdev             // test move constructor for parallel combinable
23249e08aacStbbdev             oneapi::tbb::combinable<T> moved1(std::move(sums));
23351c0b2f7Stbbdev             move_construct_sum += moved1.combine(my_combine<T>);
23451c0b2f7Stbbdev 
23551c0b2f7Stbbdev             // test move assignment for uninitialized parallel combinable
23649e08aacStbbdev             oneapi::tbb::combinable<T> moved2;
23751c0b2f7Stbbdev             moved2=std::move(finit_combinable);
23851c0b2f7Stbbdev             move_assign_sum += moved2.combine(my_combine<T>);
23951c0b2f7Stbbdev         }
24051c0b2f7Stbbdev         // Here and below comparison for equality of float numbers succeeds
24151c0b2f7Stbbdev         // as the rounding error doesn't accumulate and doesn't affect the comparison
24251c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == combine_sum );
24351c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == combine_ref_sum );
24451c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == combine_finit_sum );
24551c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == combine_each_sum );
24651c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == copy_construct_sum );
24751c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == copy_assign_sum );
24851c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == move_construct_sum );
24951c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == move_assign_sum );
25049e08aacStbbdev         // REMARK("  done parallel %s, %d, %g, %g\n", test_name, p, static_cast<double>(combine_sum), ( oneapi::tbb::tick_count::now() - t0).seconds());
25151c0b2f7Stbbdev     }
25251c0b2f7Stbbdev }
25351c0b2f7Stbbdev 
25451c0b2f7Stbbdev template <typename T>
25551c0b2f7Stbbdev class ParallelVectorForBody: utils::NoAssign {
25651c0b2f7Stbbdev 
25749e08aacStbbdev     oneapi::tbb::combinable< std::vector<T, oneapi::tbb::tbb_allocator<T> > > &locals;
25851c0b2f7Stbbdev 
25951c0b2f7Stbbdev public:
26051c0b2f7Stbbdev 
ParallelVectorForBody(oneapi::tbb::combinable<std::vector<T,oneapi::tbb::tbb_allocator<T>>> & _locals)26149e08aacStbbdev     ParallelVectorForBody ( oneapi::tbb::combinable< std::vector<T, oneapi::tbb::tbb_allocator<T> > > &_locals ) : locals(_locals) { }
26251c0b2f7Stbbdev 
operator ()(const oneapi::tbb::blocked_range<int> & r) const26349e08aacStbbdev     void operator()( const oneapi::tbb::blocked_range<int> &r ) const {
26451c0b2f7Stbbdev         T one = 1;
26551c0b2f7Stbbdev 
26651c0b2f7Stbbdev         for (int i = r.begin(); i < r.end(); ++i) {
26751c0b2f7Stbbdev             locals.local().push_back( one );
26851c0b2f7Stbbdev         }
26951c0b2f7Stbbdev     }
27051c0b2f7Stbbdev 
27151c0b2f7Stbbdev };
27251c0b2f7Stbbdev 
27351c0b2f7Stbbdev template< typename T >
RunParallelVectorTests(const char *)27451c0b2f7Stbbdev void RunParallelVectorTests(const char* /* test_name */) {
27549e08aacStbbdev     typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > ContainerType;
27651c0b2f7Stbbdev 
27751c0b2f7Stbbdev     for (int p = MinThread; p <= MaxThread; ++p) {
27851c0b2f7Stbbdev 
27951c0b2f7Stbbdev         if (p == 0) continue;
28051c0b2f7Stbbdev         // REMARK("  Testing parallel %s on %d thread(s)... \n", test_name, p);
28149e08aacStbbdev         oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
28251c0b2f7Stbbdev 
28349e08aacStbbdev         // INFO: oneapi::tbb::tick_count t0;
28451c0b2f7Stbbdev         T defaultConstructed_sum(0);
28551c0b2f7Stbbdev         T copyConstructed_sum(0);
28651c0b2f7Stbbdev         T copyAssigned_sum(0);
28751c0b2f7Stbbdev         T moveConstructed_sum(0);
28851c0b2f7Stbbdev         T moveAssigned_sum(0);
28951c0b2f7Stbbdev 
29051c0b2f7Stbbdev         for (int t = -1; t < REPETITIONS; ++t) {
29149e08aacStbbdev             // if (Verbose && t == 0) t0 = oneapi::tbb::tick_count::now();
29251c0b2f7Stbbdev 
29349e08aacStbbdev             typedef typename oneapi::tbb::combinable< ContainerType > CombinableType;
29451c0b2f7Stbbdev 
29551c0b2f7Stbbdev             // test uninitialized parallel combinable
29651c0b2f7Stbbdev             CombinableType vs;
29749e08aacStbbdev             oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int> (0, N, 10000), ParallelVectorForBody<T>( vs ) );
29851c0b2f7Stbbdev             CombineEachVectorHelper<T> MyCombineEach(defaultConstructed_sum);
29951c0b2f7Stbbdev             vs.combine_each(MyCombineEach); // combine_each sums all elements of each vector into the result
30051c0b2f7Stbbdev 
30151c0b2f7Stbbdev             // test copy constructor for parallel combinable with vectors
30251c0b2f7Stbbdev             CombinableType vs2(vs);
30351c0b2f7Stbbdev             CombineEachVectorHelper<T> MyCombineEach2(copyConstructed_sum);
30451c0b2f7Stbbdev             vs2.combine_each(MyCombineEach2);
30551c0b2f7Stbbdev 
30651c0b2f7Stbbdev             // test copy assignment for uninitialized parallel combinable with vectors
30751c0b2f7Stbbdev             CombinableType vs3;
30851c0b2f7Stbbdev             vs3 = vs;
30951c0b2f7Stbbdev             CombineEachVectorHelper<T> MyCombineEach3(copyAssigned_sum);
31051c0b2f7Stbbdev             vs3.combine_each(MyCombineEach3);
31151c0b2f7Stbbdev 
31251c0b2f7Stbbdev             // test move constructor for parallel combinable with vectors
31351c0b2f7Stbbdev             CombinableType vs4(std::move(vs2));
31451c0b2f7Stbbdev             CombineEachVectorHelper<T> MyCombineEach4(moveConstructed_sum);
31551c0b2f7Stbbdev             vs4.combine_each(MyCombineEach4);
31651c0b2f7Stbbdev 
31751c0b2f7Stbbdev             // test move assignment for uninitialized parallel combinable with vectors
31851c0b2f7Stbbdev             vs4=std::move(vs3);
31951c0b2f7Stbbdev             CombineEachVectorHelper<T> MyCombineEach5(moveAssigned_sum);
32051c0b2f7Stbbdev             vs4.combine_each(MyCombineEach5);
32151c0b2f7Stbbdev         }
32251c0b2f7Stbbdev 
32351c0b2f7Stbbdev         double ResultValue = defaultConstructed_sum;
32451c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == ResultValue );
32551c0b2f7Stbbdev         ResultValue = copyConstructed_sum;
32651c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == ResultValue );
32751c0b2f7Stbbdev         ResultValue = copyAssigned_sum;
32851c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == ResultValue );
32951c0b2f7Stbbdev         ResultValue = moveConstructed_sum;
33051c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == ResultValue );
33151c0b2f7Stbbdev         ResultValue = moveAssigned_sum;
33251c0b2f7Stbbdev         REQUIRE( EXPECTED_SUM == ResultValue );
33351c0b2f7Stbbdev 
33449e08aacStbbdev         // REMARK("  done parallel %s, %d, %g, %g\n", test_name, p, ResultValue, ( oneapi::tbb::tick_count::now() - t0).seconds());
33551c0b2f7Stbbdev     }
33651c0b2f7Stbbdev }
33751c0b2f7Stbbdev 
33851c0b2f7Stbbdev void
RunParallelTests()33951c0b2f7Stbbdev RunParallelTests() {
34051c0b2f7Stbbdev     // REMARK("Running RunParallelTests\n");
34151c0b2f7Stbbdev     RunParallelScalarTests<int>("int");
34251c0b2f7Stbbdev     RunParallelScalarTests<double>("double");
343*90b0671bSAlex     RunParallelScalarTests<minimalCombinable>("minimalCombinable");
34449e08aacStbbdev     RunParallelVectorTests<int>("std::vector<int, oneapi::tbb::tbb_allocator<int> >");
34549e08aacStbbdev     RunParallelVectorTests<double>("std::vector<double, oneapi::tbb::tbb_allocator<double> >");
34651c0b2f7Stbbdev }
34751c0b2f7Stbbdev 
34851c0b2f7Stbbdev template <typename T>
34951c0b2f7Stbbdev void
RunAssignmentAndCopyConstructorTest(const char *)35051c0b2f7Stbbdev RunAssignmentAndCopyConstructorTest(const char* /* test_name */) {
35151c0b2f7Stbbdev     // REMARK("  Testing assignment and copy construction for combinable<%s>...\n", test_name);
35251c0b2f7Stbbdev 
35351c0b2f7Stbbdev     // test creation with finit function (combine returns finit return value if no threads have created locals)
35451c0b2f7Stbbdev     FunctorAddFinit7<T> my_finit7_decl;
35549e08aacStbbdev     oneapi::tbb::combinable<T> create1(my_finit7_decl);
35651c0b2f7Stbbdev     REQUIRE_MESSAGE(7 == create1.combine(my_combine<T>), "Unexpected combine result for combinable object preinitialized with functor");
35751c0b2f7Stbbdev 
35851c0b2f7Stbbdev     // test copy construction with function initializer
35949e08aacStbbdev     oneapi::tbb::combinable<T> copy1(create1);
36051c0b2f7Stbbdev     REQUIRE_MESSAGE(7 == copy1.combine(my_combine<T>), "Unexpected combine result for copy-constructed combinable object");
36151c0b2f7Stbbdev 
36251c0b2f7Stbbdev     // test copy assignment with function initializer
36351c0b2f7Stbbdev     FunctorAddFinit<T> my_finit_decl;
36449e08aacStbbdev     oneapi::tbb::combinable<T> assign1(my_finit_decl);
36551c0b2f7Stbbdev     assign1 = create1;
36651c0b2f7Stbbdev     REQUIRE_MESSAGE(7 == assign1.combine(my_combine<T>), "Unexpected combine result for copy-assigned combinable object");
36751c0b2f7Stbbdev 
36851c0b2f7Stbbdev     // test move construction with function initializer
36949e08aacStbbdev     oneapi::tbb::combinable<T> move1(std::move(create1));
37051c0b2f7Stbbdev     REQUIRE_MESSAGE(7 == move1.combine(my_combine<T>), "Unexpected combine result for move-constructed combinable object");
37151c0b2f7Stbbdev 
37251c0b2f7Stbbdev     // test move assignment with function initializer
37349e08aacStbbdev     oneapi::tbb::combinable<T> move2;
37451c0b2f7Stbbdev     move2=std::move(copy1);
37551c0b2f7Stbbdev     REQUIRE_MESSAGE(7 == move2.combine(my_combine<T>), "Unexpected combine result for move-assigned combinable object");
37651c0b2f7Stbbdev 
37751c0b2f7Stbbdev     // REMARK("  done\n");
37851c0b2f7Stbbdev 
37951c0b2f7Stbbdev }
38051c0b2f7Stbbdev 
RunAssignmentAndCopyConstructorTests()38151c0b2f7Stbbdev void RunAssignmentAndCopyConstructorTests() {
38251c0b2f7Stbbdev     // REMARK("Running assignment and copy constructor tests:\n");
38351c0b2f7Stbbdev     RunAssignmentAndCopyConstructorTest<int>("int");
38451c0b2f7Stbbdev     RunAssignmentAndCopyConstructorTest<double>("double");
385*90b0671bSAlex     RunAssignmentAndCopyConstructorTest<minimalCombinable>("minimalCombinable");
38651c0b2f7Stbbdev }
38751c0b2f7Stbbdev 
RunMoveSemanticsForStateTrackableObjectTest()38851c0b2f7Stbbdev void RunMoveSemanticsForStateTrackableObjectTest() {
38951c0b2f7Stbbdev     // REMARK("Testing move assignment and move construction for combinable<Harness::StateTrackable>...\n");
39051c0b2f7Stbbdev 
39149e08aacStbbdev     oneapi::tbb::combinable< StateTrackable<true> > create1;
39251c0b2f7Stbbdev     REQUIRE_MESSAGE(create1.local().state == StateTrackable<true>::DefaultInitialized,
39351c0b2f7Stbbdev            "Unexpected value in default combinable object");
39451c0b2f7Stbbdev 
39551c0b2f7Stbbdev     // Copy constructing of the new combinable causes copying of stored values
39649e08aacStbbdev     oneapi::tbb::combinable< StateTrackable<true> > copy1(create1);
39751c0b2f7Stbbdev     REQUIRE_MESSAGE(copy1.local().state == StateTrackable<true>::CopyInitialized,
39851c0b2f7Stbbdev            "Unexpected value in copy-constructed combinable object");
39951c0b2f7Stbbdev 
40051c0b2f7Stbbdev     // Copy assignment also causes copying of stored values
40149e08aacStbbdev     oneapi::tbb::combinable< StateTrackable<true> > copy2;
40251c0b2f7Stbbdev     REQUIRE_MESSAGE(copy2.local().state == StateTrackable<true>::DefaultInitialized,
40351c0b2f7Stbbdev            "Unexpected value in default combinable object");
40451c0b2f7Stbbdev     copy2=create1;
40551c0b2f7Stbbdev     REQUIRE_MESSAGE(copy2.local().state == StateTrackable<true>::CopyInitialized,
40651c0b2f7Stbbdev            "Unexpected value in copy-assigned combinable object");
40751c0b2f7Stbbdev 
40851c0b2f7Stbbdev     // Store some marked values in the initial combinable object
40951c0b2f7Stbbdev     create1.local().state = StateTrackableBase::Unspecified;
41051c0b2f7Stbbdev 
41151c0b2f7Stbbdev     // Move constructing of the new combinable must not cause copying of stored values
41249e08aacStbbdev     oneapi::tbb::combinable< StateTrackable<true> > move1(std::move(create1));
41351c0b2f7Stbbdev     REQUIRE_MESSAGE(move1.local().state == StateTrackableBase::Unspecified, "Unexpected value in move-constructed combinable object");
41451c0b2f7Stbbdev 
41551c0b2f7Stbbdev     // Move assignment must not cause copying of stored values
41651c0b2f7Stbbdev     copy1=std::move(move1);
41751c0b2f7Stbbdev     REQUIRE_MESSAGE(copy1.local().state == StateTrackableBase::Unspecified, "Unexpected value in move-assigned combinable object");
41851c0b2f7Stbbdev 
41951c0b2f7Stbbdev     // Make the stored values valid again in order to delete StateTrackable object correctly
42051c0b2f7Stbbdev     copy1.local().state = StateTrackable<true>::MoveAssigned;
42151c0b2f7Stbbdev 
42251c0b2f7Stbbdev     // REMARK("done\n");
42351c0b2f7Stbbdev }
42451c0b2f7Stbbdev 
42551c0b2f7Stbbdev utils::SpinBarrier sBarrier;
42651c0b2f7Stbbdev 
42751c0b2f7Stbbdev struct Body : utils::NoAssign {
42849e08aacStbbdev     oneapi::tbb::combinable<int>* locals;
42951c0b2f7Stbbdev     const int nthread;
43051c0b2f7Stbbdev     const int nIters;
BodyBody43151c0b2f7Stbbdev     Body( int nthread_, int niters_ ) : nthread(nthread_), nIters(niters_) { sBarrier.initialize(nthread_); }
43251c0b2f7Stbbdev 
operator ()Body43351c0b2f7Stbbdev     void operator()(int thread_id ) const {
43451c0b2f7Stbbdev         bool existed;
43551c0b2f7Stbbdev         sBarrier.wait();
43651c0b2f7Stbbdev         for(int i = 0; i < nIters; ++i ) {
43751c0b2f7Stbbdev             existed = thread_id & 1;
43851c0b2f7Stbbdev             int oldval = locals->local(existed);
43951c0b2f7Stbbdev             REQUIRE_MESSAGE(existed == (i > 0), "Error on first reference");
44051c0b2f7Stbbdev             REQUIRE_MESSAGE((!existed || (oldval == thread_id)), "Error on fetched value");
44151c0b2f7Stbbdev             existed = thread_id & 1;
44251c0b2f7Stbbdev             locals->local(existed) = thread_id;
44351c0b2f7Stbbdev             REQUIRE_MESSAGE(existed, "Error on assignment");
44451c0b2f7Stbbdev         }
44551c0b2f7Stbbdev     }
44651c0b2f7Stbbdev };
44751c0b2f7Stbbdev 
TestLocalAllocations(int nthread)44851c0b2f7Stbbdev void TestLocalAllocations( int nthread ) {
44951c0b2f7Stbbdev     REQUIRE_MESSAGE(nthread > 0, "nthread must be positive");
45051c0b2f7Stbbdev #define NITERATIONS 1000
45151c0b2f7Stbbdev     Body myBody(nthread, NITERATIONS);
45249e08aacStbbdev     oneapi::tbb::combinable<int> myCombinable;
45351c0b2f7Stbbdev     myBody.locals = &myCombinable;
45451c0b2f7Stbbdev 
45551c0b2f7Stbbdev     NativeParallelFor( nthread, myBody );
45651c0b2f7Stbbdev 
45751c0b2f7Stbbdev     int mySum = 0;
45851c0b2f7Stbbdev     int mySlots = 0;
45951c0b2f7Stbbdev     CombineEachHelperCnt<int> myCountCombine(mySum, mySlots);
46051c0b2f7Stbbdev     myCombinable.combine_each(myCountCombine);
46151c0b2f7Stbbdev 
46251c0b2f7Stbbdev     REQUIRE_MESSAGE(nthread == mySlots, "Incorrect number of slots");
46351c0b2f7Stbbdev     REQUIRE_MESSAGE(mySum == (nthread - 1) * nthread / 2, "Incorrect values in result");
46451c0b2f7Stbbdev }
46551c0b2f7Stbbdev 
RunLocalAllocationsTests()46651c0b2f7Stbbdev void RunLocalAllocationsTests() {
46751c0b2f7Stbbdev     // REMARK("Testing local() allocations\n");
46851c0b2f7Stbbdev     for(int i = 1 <= MinThread ? MinThread : 1; i <= MaxThread; ++i) {
46951c0b2f7Stbbdev         // REMARK("  Testing local() allocation with nthreads=%d...\n", i);
47051c0b2f7Stbbdev         for(int j = 0; j < 100; ++j) {
47151c0b2f7Stbbdev             TestLocalAllocations(i);
47251c0b2f7Stbbdev         }
47351c0b2f7Stbbdev         // REMARK("  done\n");
47451c0b2f7Stbbdev     }
47551c0b2f7Stbbdev }
47651c0b2f7Stbbdev 
47751c0b2f7Stbbdev //! Test combinable in parallel algorithms
47851c0b2f7Stbbdev //! \brief \ref interface \ref requirement
47951c0b2f7Stbbdev TEST_CASE("Parallel scenario") {
48051c0b2f7Stbbdev     RunParallelTests();
48151c0b2f7Stbbdev     RunLocalAllocationsTests();
48251c0b2f7Stbbdev }
48351c0b2f7Stbbdev 
48451c0b2f7Stbbdev //! Test assignment and copy construction
48551c0b2f7Stbbdev //! \brief \ref interface \ref requirement
48651c0b2f7Stbbdev TEST_CASE("Assignment and copy constructor test") {
48751c0b2f7Stbbdev     RunAssignmentAndCopyConstructorTests();
48851c0b2f7Stbbdev }
48951c0b2f7Stbbdev 
49051c0b2f7Stbbdev //! Test move support
49151c0b2f7Stbbdev //! \brief \ref interface \ref requirement
49251c0b2f7Stbbdev TEST_CASE("Move semantics") {
49351c0b2f7Stbbdev     RunMoveSemanticsForStateTrackableObjectTest();
49451c0b2f7Stbbdev }
49551c0b2f7Stbbdev 
496