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