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
17b15aabb3Stbbdev #if _MSC_VER
18b15aabb3Stbbdev #if __INTEL_COMPILER
19b15aabb3Stbbdev #pragma warning(disable : 2586) // decorated name length exceeded, name was truncated
20b15aabb3Stbbdev #else
2151c0b2f7Stbbdev // Workaround for vs2015 and warning name was longer than the compiler limit (4096).
2251c0b2f7Stbbdev #pragma warning (disable: 4503)
2351c0b2f7Stbbdev #endif
24b15aabb3Stbbdev #endif
2551c0b2f7Stbbdev
2651c0b2f7Stbbdev #include "common/test.h"
2751c0b2f7Stbbdev #include "common/utils.h"
2851c0b2f7Stbbdev #include "common/utils_report.h"
2951c0b2f7Stbbdev #include "common/utils_concurrency_limit.h"
3051c0b2f7Stbbdev #include "common/spin_barrier.h"
3151c0b2f7Stbbdev #include "common/checktype.h"
32b15aabb3Stbbdev #include "common/test_comparisons.h"
3351c0b2f7Stbbdev
3449e08aacStbbdev #include "oneapi/tbb/detail/_utils.h"
3549e08aacStbbdev #include "oneapi/tbb/enumerable_thread_specific.h"
3649e08aacStbbdev #include "oneapi/tbb/parallel_for.h"
3749e08aacStbbdev #include "oneapi/tbb/parallel_reduce.h"
3849e08aacStbbdev #include "oneapi/tbb/parallel_invoke.h"
3949e08aacStbbdev #include "oneapi/tbb/blocked_range.h"
4049e08aacStbbdev #include "oneapi/tbb/tbb_allocator.h"
4149e08aacStbbdev #include "oneapi/tbb/global_control.h"
4249e08aacStbbdev #include "oneapi/tbb/cache_aligned_allocator.h"
4351c0b2f7Stbbdev
4451c0b2f7Stbbdev #include <cstring>
4551c0b2f7Stbbdev #include <cstdio>
4651c0b2f7Stbbdev #include <vector>
4751c0b2f7Stbbdev #include <numeric>
4851c0b2f7Stbbdev #include <utility>
4951c0b2f7Stbbdev #include <atomic>
5051c0b2f7Stbbdev
5151c0b2f7Stbbdev //! \file conformance_enumerable_thread_specific.cpp
5251c0b2f7Stbbdev //! \brief Test for [tls.enumerable_thread_specific tls.flattened2d] specification
5351c0b2f7Stbbdev
5451c0b2f7Stbbdev //------------------------------------------------------------------------------------------------------
5551c0b2f7Stbbdev // Utility types/classes/functions
5651c0b2f7Stbbdev //------------------------------------------------------------------------------------------------------
5751c0b2f7Stbbdev
5851c0b2f7Stbbdev //! Minimum number of threads
5951c0b2f7Stbbdev static int MinThread = 1;
6051c0b2f7Stbbdev
6151c0b2f7Stbbdev //! Maximum number of threads
6251c0b2f7Stbbdev static int MaxThread = 4;
6351c0b2f7Stbbdev
6451c0b2f7Stbbdev static std::atomic<int> construction_counter;
6551c0b2f7Stbbdev static std::atomic<int> destruction_counter;
6651c0b2f7Stbbdev
67b15aabb3Stbbdev const int REPETITIONS = 5;
68b15aabb3Stbbdev const int N = 25000;
69b15aabb3Stbbdev const int RANGE_MIN = 5000;
7051c0b2f7Stbbdev const double EXPECTED_SUM = (REPETITIONS + 1) * N;
7151c0b2f7Stbbdev
7251c0b2f7Stbbdev //! A minimal class that occupies N bytes.
7351c0b2f7Stbbdev /** Defines default and copy constructor, and allows implicit operator&. Hides operator=. */
7449e08aacStbbdev template<size_t N = oneapi::tbb::detail::max_nfs_size>
75*90b0671bSAlex class minimalNComparable: utils::NoAssign {
7651c0b2f7Stbbdev private:
7751c0b2f7Stbbdev int my_value;
7851c0b2f7Stbbdev bool is_constructed;
7951c0b2f7Stbbdev char pad[N-sizeof(int) - sizeof(bool)];
8051c0b2f7Stbbdev public:
minimalNComparable()81*90b0671bSAlex minimalNComparable() : utils::NoAssign(), my_value(0) { ++construction_counter; is_constructed = true; }
minimalNComparable(const minimalNComparable & m)82*90b0671bSAlex minimalNComparable( const minimalNComparable &m ) : utils::NoAssign(), my_value(m.my_value) { ++construction_counter; is_constructed = true; }
~minimalNComparable()83*90b0671bSAlex ~minimalNComparable() { ++destruction_counter; CHECK_FAST(is_constructed); is_constructed = false; }
set_value(const int i)84b15aabb3Stbbdev void set_value( const int i ) { CHECK_FAST(is_constructed); my_value = i; }
value() const85b15aabb3Stbbdev int value( ) const { CHECK_FAST(is_constructed); return my_value; }
8651c0b2f7Stbbdev
operator ==(const minimalNComparable & other) const87*90b0671bSAlex bool operator==( const minimalNComparable& other ) const { return my_value == other.my_value; }
8851c0b2f7Stbbdev };
8951c0b2f7Stbbdev
9051c0b2f7Stbbdev static size_t AlignMask = 0; // set to cache-line-size - 1
9151c0b2f7Stbbdev
9251c0b2f7Stbbdev template<typename T>
check_alignment(T & t,const char * aname)9351c0b2f7Stbbdev T& check_alignment(T& t, const char *aname) {
9449e08aacStbbdev if( !oneapi::tbb::detail::is_aligned(&t, AlignMask)) {
9551c0b2f7Stbbdev // TBB_REVAMP_TODO: previously was REPORT_ONCE
9651c0b2f7Stbbdev REPORT("alignment error with %s allocator (%x)\n", aname, (int)size_t(&t) & (AlignMask-1));
9751c0b2f7Stbbdev }
9851c0b2f7Stbbdev return t;
9951c0b2f7Stbbdev }
10051c0b2f7Stbbdev
10151c0b2f7Stbbdev template<typename T>
check_alignment(const T & t,const char * aname)10251c0b2f7Stbbdev const T& check_alignment(const T& t, const char *aname) {
10349e08aacStbbdev if( !oneapi::tbb::detail::is_aligned(&t, AlignMask)) {
10451c0b2f7Stbbdev // TBB_REVAMP_TODO: previously was REPORT_ONCE
10551c0b2f7Stbbdev REPORT("alignment error with %s allocator (%x)\n", aname, (int)size_t(&t) & (AlignMask-1));
10651c0b2f7Stbbdev }
10751c0b2f7Stbbdev return t;
10851c0b2f7Stbbdev }
10951c0b2f7Stbbdev
11051c0b2f7Stbbdev // Test constructors which throw. If an ETS constructor throws before completion,
11151c0b2f7Stbbdev // the already-built objects are un-constructed. Do not call the destructor if
11251c0b2f7Stbbdev // this occurs.
11351c0b2f7Stbbdev
11451c0b2f7Stbbdev static std::atomic<int> gThrowValue;
11551c0b2f7Stbbdev static int targetThrowValue = 3;
11651c0b2f7Stbbdev
11751c0b2f7Stbbdev class Thrower {
11851c0b2f7Stbbdev public:
Thrower()11951c0b2f7Stbbdev Thrower() {
12051c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
12151c0b2f7Stbbdev if(++gThrowValue == targetThrowValue) {
12251c0b2f7Stbbdev throw std::bad_alloc();
12351c0b2f7Stbbdev }
12451c0b2f7Stbbdev #endif
12551c0b2f7Stbbdev }
12651c0b2f7Stbbdev };
12751c0b2f7Stbbdev
12851c0b2f7Stbbdev // MyThrower field of ThrowingConstructor will throw after a certain number of
12951c0b2f7Stbbdev // construction calls. The constructor unwinder wshould unconstruct the instance
13051c0b2f7Stbbdev // of check_type<int> that was constructed just before.
13151c0b2f7Stbbdev class ThrowingConstructor {
13251c0b2f7Stbbdev CheckType<int> m_checktype;
13351c0b2f7Stbbdev Thrower m_throwing_field;
13451c0b2f7Stbbdev public:
13551c0b2f7Stbbdev int m_cnt;
ThrowingConstructor()13651c0b2f7Stbbdev ThrowingConstructor() : m_checktype(), m_throwing_field() { m_cnt = 0;}
13751c0b2f7Stbbdev
operator ==(const ThrowingConstructor & other) const138b15aabb3Stbbdev bool operator==( const ThrowingConstructor& other ) const { return m_cnt == other.m_cnt; }
13951c0b2f7Stbbdev private:
14051c0b2f7Stbbdev };
14151c0b2f7Stbbdev
14251c0b2f7Stbbdev //
143*90b0671bSAlex // A helper class that simplifies writing the tests since minimalNComparable does not
14451c0b2f7Stbbdev // define = or + operators.
14551c0b2f7Stbbdev //
14651c0b2f7Stbbdev
14751c0b2f7Stbbdev template< typename T >
14851c0b2f7Stbbdev struct test_helper {
inittest_helper14951c0b2f7Stbbdev static inline void init(T &e) { e = static_cast<T>(0); }
sumtest_helper15051c0b2f7Stbbdev static inline void sum(T &e, const int addend ) { e += static_cast<T>(addend); }
sumtest_helper15151c0b2f7Stbbdev static inline void sum(T &e, const double addend ) { e += static_cast<T>(addend); }
settest_helper15251c0b2f7Stbbdev static inline void set(T &e, const int value ) { e = static_cast<T>(value); }
gettest_helper15351c0b2f7Stbbdev static inline double get(const T &e ) { return static_cast<double>(e); }
15451c0b2f7Stbbdev };
15551c0b2f7Stbbdev
15651c0b2f7Stbbdev template<size_t N>
157*90b0671bSAlex struct test_helper<minimalNComparable<N> > {
inittest_helper158*90b0671bSAlex static inline void init(minimalNComparable<N> &sum) { sum.set_value( 0 ); }
sumtest_helper159*90b0671bSAlex static inline void sum(minimalNComparable<N> &sum, const int addend ) { sum.set_value( sum.value() + addend); }
sumtest_helper160*90b0671bSAlex static inline void sum(minimalNComparable<N> &sum, const double addend ) { sum.set_value( sum.value() + static_cast<int>(addend)); }
sumtest_helper161*90b0671bSAlex static inline void sum(minimalNComparable<N> &sum, const minimalNComparable<N> &addend ) { sum.set_value( sum.value() + addend.value()); }
settest_helper162*90b0671bSAlex static inline void set(minimalNComparable<N> &v, const int value ) { v.set_value( static_cast<int>(value) ); }
gettest_helper163*90b0671bSAlex static inline double get(const minimalNComparable<N> &sum ) { return static_cast<double>(sum.value()); }
16451c0b2f7Stbbdev };
16551c0b2f7Stbbdev
16651c0b2f7Stbbdev template<>
16751c0b2f7Stbbdev struct test_helper<ThrowingConstructor> {
inittest_helper16851c0b2f7Stbbdev static inline void init(ThrowingConstructor &sum) { sum.m_cnt = 0; }
sumtest_helper16951c0b2f7Stbbdev static inline void sum(ThrowingConstructor &sum, const int addend ) { sum.m_cnt += addend; }
sumtest_helper17051c0b2f7Stbbdev static inline void sum(ThrowingConstructor &sum, const double addend ) { sum.m_cnt += static_cast<int>(addend); }
sumtest_helper17151c0b2f7Stbbdev static inline void sum(ThrowingConstructor &sum, const ThrowingConstructor &addend ) { sum.m_cnt += addend.m_cnt; }
settest_helper17251c0b2f7Stbbdev static inline void set(ThrowingConstructor &v, const int value ) { v.m_cnt = static_cast<int>(value); }
gettest_helper17351c0b2f7Stbbdev static inline double get(const ThrowingConstructor &sum ) { return static_cast<double>(sum.m_cnt); }
17451c0b2f7Stbbdev };
17551c0b2f7Stbbdev
17651c0b2f7Stbbdev //! Tag class used to make certain constructors hard to invoke accidentally.
17751c0b2f7Stbbdev struct SecretTagType {} SecretTag;
17851c0b2f7Stbbdev
17951c0b2f7Stbbdev //// functors and routines for initialization and combine
18051c0b2f7Stbbdev
18151c0b2f7Stbbdev //! Counts instances of FunctorFinit
18251c0b2f7Stbbdev static std::atomic<int> FinitCounter;
18351c0b2f7Stbbdev
18451c0b2f7Stbbdev template <typename T, int Value>
18551c0b2f7Stbbdev struct FunctorFinit {
FunctorFinitFunctorFinit18651c0b2f7Stbbdev FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
FunctorFinitFunctorFinit18751c0b2f7Stbbdev FunctorFinit( SecretTagType ) {++FinitCounter;}
~FunctorFinitFunctorFinit18851c0b2f7Stbbdev ~FunctorFinit() {--FinitCounter;}
operator ()FunctorFinit18951c0b2f7Stbbdev T operator()() { return Value; }
19051c0b2f7Stbbdev };
19151c0b2f7Stbbdev
19251c0b2f7Stbbdev template <int Value>
19351c0b2f7Stbbdev struct FunctorFinit<ThrowingConstructor,Value> {
FunctorFinitFunctorFinit19451c0b2f7Stbbdev FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
FunctorFinitFunctorFinit19551c0b2f7Stbbdev FunctorFinit( SecretTagType ) {++FinitCounter;}
~FunctorFinitFunctorFinit19651c0b2f7Stbbdev ~FunctorFinit() {--FinitCounter;}
operator ()FunctorFinit19751c0b2f7Stbbdev ThrowingConstructor operator()() { ThrowingConstructor temp; temp.m_cnt = Value; return temp; }
19851c0b2f7Stbbdev };
19951c0b2f7Stbbdev
20051c0b2f7Stbbdev template <size_t N, int Value>
201*90b0671bSAlex struct FunctorFinit<minimalNComparable<N>,Value> {
FunctorFinitFunctorFinit20251c0b2f7Stbbdev FunctorFinit( const FunctorFinit& ) {++FinitCounter;}
FunctorFinitFunctorFinit20351c0b2f7Stbbdev FunctorFinit( SecretTagType ) {++FinitCounter;}
~FunctorFinitFunctorFinit20451c0b2f7Stbbdev ~FunctorFinit() {--FinitCounter;}
operator ()FunctorFinit205*90b0671bSAlex minimalNComparable<N> operator()() {
206*90b0671bSAlex minimalNComparable<N> result;
20751c0b2f7Stbbdev result.set_value( Value );
20851c0b2f7Stbbdev return result;
20951c0b2f7Stbbdev }
21051c0b2f7Stbbdev };
21151c0b2f7Stbbdev
21251c0b2f7Stbbdev // Addition
21351c0b2f7Stbbdev
21451c0b2f7Stbbdev template <typename T>
21551c0b2f7Stbbdev struct FunctorAddCombineRef {
operator ()FunctorAddCombineRef21651c0b2f7Stbbdev T operator()(const T& left, const T& right) const {
21751c0b2f7Stbbdev return left+right;
21851c0b2f7Stbbdev }
21951c0b2f7Stbbdev };
22051c0b2f7Stbbdev
22151c0b2f7Stbbdev template <size_t N>
222*90b0671bSAlex struct FunctorAddCombineRef<minimalNComparable<N> > {
operator ()FunctorAddCombineRef223*90b0671bSAlex minimalNComparable<N> operator()(const minimalNComparable<N>& left, const minimalNComparable<N>& right) const {
224*90b0671bSAlex minimalNComparable<N> result;
22551c0b2f7Stbbdev result.set_value( left.value() + right.value() );
22651c0b2f7Stbbdev return result;
22751c0b2f7Stbbdev }
22851c0b2f7Stbbdev };
22951c0b2f7Stbbdev
23051c0b2f7Stbbdev template <>
23151c0b2f7Stbbdev struct FunctorAddCombineRef<ThrowingConstructor> {
operator ()FunctorAddCombineRef23251c0b2f7Stbbdev ThrowingConstructor operator()(const ThrowingConstructor& left, const ThrowingConstructor& right) const {
23351c0b2f7Stbbdev ThrowingConstructor result;
23451c0b2f7Stbbdev result.m_cnt = ( left.m_cnt + right.m_cnt );
23551c0b2f7Stbbdev return result;
23651c0b2f7Stbbdev }
23751c0b2f7Stbbdev };
23851c0b2f7Stbbdev
23951c0b2f7Stbbdev template <typename T>
24051c0b2f7Stbbdev struct FunctorAddCombine {
operator ()FunctorAddCombine24151c0b2f7Stbbdev T operator()(T left, T right ) const {
24251c0b2f7Stbbdev return FunctorAddCombineRef<T>()( left, right );
24351c0b2f7Stbbdev }
24451c0b2f7Stbbdev };
24551c0b2f7Stbbdev
24651c0b2f7Stbbdev template <typename T>
FunctionAddByRef(const T & left,const T & right)24751c0b2f7Stbbdev T FunctionAddByRef( const T &left, const T &right) {
24851c0b2f7Stbbdev return FunctorAddCombineRef<T>()( left, right );
24951c0b2f7Stbbdev }
25051c0b2f7Stbbdev
25151c0b2f7Stbbdev template <typename T>
FunctionAdd(T left,T right)25251c0b2f7Stbbdev T FunctionAdd( T left, T right) { return FunctionAddByRef(left,right); }
25351c0b2f7Stbbdev
25451c0b2f7Stbbdev template <typename T>
25551c0b2f7Stbbdev class Accumulator {
25651c0b2f7Stbbdev public:
Accumulator(T & result)25751c0b2f7Stbbdev Accumulator(T& result) : my_result(result) {}
Accumulator(const Accumulator & other)25851c0b2f7Stbbdev Accumulator(const Accumulator& other) : my_result(other.my_result) {}
operator =(const Accumulator & other)25951c0b2f7Stbbdev Accumulator& operator=(const Accumulator& other) {
26051c0b2f7Stbbdev test_helper<T>::set(my_result, test_helper<T>::get(other));
26151c0b2f7Stbbdev return *this;
26251c0b2f7Stbbdev }
operator ()(const T & new_bit)26351c0b2f7Stbbdev void operator()(const T& new_bit) { test_helper<T>::sum(my_result, new_bit); }
26451c0b2f7Stbbdev private:
26551c0b2f7Stbbdev T& my_result;
26651c0b2f7Stbbdev };
26751c0b2f7Stbbdev
26851c0b2f7Stbbdev template <typename T>
26951c0b2f7Stbbdev class ClearingAccumulator {
27051c0b2f7Stbbdev public:
ClearingAccumulator(T & result)27151c0b2f7Stbbdev ClearingAccumulator(T& result) : my_result(result) {}
ClearingAccumulator(const ClearingAccumulator & other)27251c0b2f7Stbbdev ClearingAccumulator(const ClearingAccumulator& other) : my_result(other.my_result) {}
operator =(const ClearingAccumulator & other)27351c0b2f7Stbbdev ClearingAccumulator& operator=(const ClearingAccumulator& other) {
27451c0b2f7Stbbdev test_helper<T>::set(my_result, test_helper<T>::get(other));
27551c0b2f7Stbbdev return *this;
27651c0b2f7Stbbdev }
operator ()(T & new_bit)27751c0b2f7Stbbdev void operator()(T& new_bit) {
27851c0b2f7Stbbdev test_helper<T>::sum(my_result, new_bit);
27951c0b2f7Stbbdev test_helper<T>::init(new_bit);
28051c0b2f7Stbbdev }
AssertClean(const T & thread_local_value)28151c0b2f7Stbbdev static void AssertClean(const T& thread_local_value) {
28251c0b2f7Stbbdev T zero;
28351c0b2f7Stbbdev test_helper<T>::init(zero);
28451c0b2f7Stbbdev REQUIRE_MESSAGE(test_helper<T>::get(thread_local_value)==test_helper<T>::get(zero),
28551c0b2f7Stbbdev "combine_each does not allow to modify thread local values?");
28651c0b2f7Stbbdev }
28751c0b2f7Stbbdev private:
28851c0b2f7Stbbdev T& my_result;
28951c0b2f7Stbbdev };
29051c0b2f7Stbbdev
29151c0b2f7Stbbdev //// end functors and routines
29251c0b2f7Stbbdev
29351c0b2f7Stbbdev //------------------------------------------------------------------------------------------------------
29451c0b2f7Stbbdev // Tests for tests cases
29551c0b2f7Stbbdev //------------------------------------------------------------------------------------------------------
29651c0b2f7Stbbdev
29751c0b2f7Stbbdev template <typename T, template<class> class Allocator>
29851c0b2f7Stbbdev class parallel_scalar_body: utils::NoAssign {
29949e08aacStbbdev typedef oneapi::tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
30051c0b2f7Stbbdev ets_type &sums;
30151c0b2f7Stbbdev const char* allocator_name;
30251c0b2f7Stbbdev
30351c0b2f7Stbbdev public:
30451c0b2f7Stbbdev
parallel_scalar_body(ets_type & _sums,const char * alloc_name)30551c0b2f7Stbbdev parallel_scalar_body ( ets_type &_sums, const char *alloc_name ) : sums(_sums), allocator_name(alloc_name) { }
30651c0b2f7Stbbdev
operator ()(const oneapi::tbb::blocked_range<int> & r) const30749e08aacStbbdev void operator()( const oneapi::tbb::blocked_range<int> &r ) const {
30851c0b2f7Stbbdev for (int i = r.begin(); i != r.end(); ++i)
30951c0b2f7Stbbdev test_helper<T>::sum( check_alignment(sums.local(),allocator_name), 1 );
31051c0b2f7Stbbdev }
31151c0b2f7Stbbdev
31251c0b2f7Stbbdev };
31351c0b2f7Stbbdev
31451c0b2f7Stbbdev template< typename T, template<class> class Allocator>
run_parallel_scalar_tests_nocombine(const char *,const char * allocator_name)31551c0b2f7Stbbdev void run_parallel_scalar_tests_nocombine(const char* /* test_name */, const char *allocator_name) {
31651c0b2f7Stbbdev
31749e08aacStbbdev typedef oneapi::tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
31851c0b2f7Stbbdev
31951c0b2f7Stbbdev Checker<T> my_check;
320b15aabb3Stbbdev
321b15aabb3Stbbdev gThrowValue = 0;
322b15aabb3Stbbdev struct fail_on_exception_guard {
323b15aabb3Stbbdev bool dismiss = false;
324b15aabb3Stbbdev ~fail_on_exception_guard() {
325b15aabb3Stbbdev if (!dismiss) {
326b15aabb3Stbbdev FAIL("The exception is not expected");
327b15aabb3Stbbdev }
328b15aabb3Stbbdev }
329b15aabb3Stbbdev } guard;
330b15aabb3Stbbdev T default_value{};
331b15aabb3Stbbdev guard.dismiss = true;
332b15aabb3Stbbdev
33351c0b2f7Stbbdev gThrowValue = 0;
33451c0b2f7Stbbdev {
33551c0b2f7Stbbdev // We assume that static_sums zero-initialized or has a default constructor that zeros it.
33651c0b2f7Stbbdev ets_type static_sums = ets_type( T() );
33751c0b2f7Stbbdev
33851c0b2f7Stbbdev T exemplar;
33951c0b2f7Stbbdev test_helper<T>::init(exemplar);
34051c0b2f7Stbbdev
341b15aabb3Stbbdev for (int p = std::max(MinThread, 2); p <= MaxThread; ++p) {
34249e08aacStbbdev oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
34351c0b2f7Stbbdev
34451c0b2f7Stbbdev T iterator_sum;
34551c0b2f7Stbbdev test_helper<T>::init(iterator_sum);
34651c0b2f7Stbbdev
34751c0b2f7Stbbdev T finit_ets_sum;
34851c0b2f7Stbbdev test_helper<T>::init(finit_ets_sum);
34951c0b2f7Stbbdev
35051c0b2f7Stbbdev T const_iterator_sum;
35151c0b2f7Stbbdev test_helper<T>::init(const_iterator_sum);
35251c0b2f7Stbbdev
35351c0b2f7Stbbdev T range_sum;
35451c0b2f7Stbbdev test_helper<T>::init(range_sum);
35551c0b2f7Stbbdev
35651c0b2f7Stbbdev T const_range_sum;
35751c0b2f7Stbbdev test_helper<T>::init(const_range_sum);
35851c0b2f7Stbbdev
35951c0b2f7Stbbdev T cconst_sum;
36051c0b2f7Stbbdev test_helper<T>::init(cconst_sum);
36151c0b2f7Stbbdev
36251c0b2f7Stbbdev T assign_sum;
36351c0b2f7Stbbdev test_helper<T>::init(assign_sum);
36451c0b2f7Stbbdev
36551c0b2f7Stbbdev T cassgn_sum;
36651c0b2f7Stbbdev test_helper<T>::init(cassgn_sum);
36751c0b2f7Stbbdev T non_cassgn_sum;
36851c0b2f7Stbbdev test_helper<T>::init(non_cassgn_sum);
36951c0b2f7Stbbdev
37051c0b2f7Stbbdev T static_sum;
37151c0b2f7Stbbdev test_helper<T>::init(static_sum);
37251c0b2f7Stbbdev
37351c0b2f7Stbbdev for (int t = -1; t < REPETITIONS; ++t) {
37451c0b2f7Stbbdev static_sums.clear();
37551c0b2f7Stbbdev
37651c0b2f7Stbbdev ets_type sums(exemplar);
37751c0b2f7Stbbdev FunctorFinit<T,0> my_finit(SecretTag);
37851c0b2f7Stbbdev ets_type finit_ets(my_finit);
37951c0b2f7Stbbdev
38051c0b2f7Stbbdev REQUIRE( sums.empty());
381b15aabb3Stbbdev oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>( 0, N*p, RANGE_MIN ), parallel_scalar_body<T,Allocator>( sums, allocator_name ) );
38251c0b2f7Stbbdev REQUIRE( !sums.empty());
38351c0b2f7Stbbdev
38451c0b2f7Stbbdev REQUIRE( finit_ets.empty());
385b15aabb3Stbbdev oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>( 0, N*p, RANGE_MIN ), parallel_scalar_body<T,Allocator>( finit_ets, allocator_name ) );
38651c0b2f7Stbbdev REQUIRE( !finit_ets.empty());
38751c0b2f7Stbbdev
38851c0b2f7Stbbdev REQUIRE(static_sums.empty());
389b15aabb3Stbbdev oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>( 0, N*p, RANGE_MIN ), parallel_scalar_body<T,Allocator>( static_sums, allocator_name ) );
39051c0b2f7Stbbdev REQUIRE( !static_sums.empty());
39151c0b2f7Stbbdev
39251c0b2f7Stbbdev // use iterator
39351c0b2f7Stbbdev typename ets_type::size_type size = 0;
39451c0b2f7Stbbdev for ( typename ets_type::iterator i = sums.begin(); i != sums.end(); ++i ) {
39551c0b2f7Stbbdev ++size;
39651c0b2f7Stbbdev test_helper<T>::sum(iterator_sum, *i);
39751c0b2f7Stbbdev }
39851c0b2f7Stbbdev REQUIRE( sums.size() == size);
39951c0b2f7Stbbdev
40051c0b2f7Stbbdev // use const_iterator
40151c0b2f7Stbbdev for ( typename ets_type::const_iterator i = sums.begin(); i != sums.end(); ++i ) {
40251c0b2f7Stbbdev test_helper<T>::sum(const_iterator_sum, *i);
40351c0b2f7Stbbdev }
40451c0b2f7Stbbdev
40551c0b2f7Stbbdev // use range_type
40651c0b2f7Stbbdev typename ets_type::range_type r = sums.range();
40751c0b2f7Stbbdev for ( typename ets_type::range_type::const_iterator i = r.begin(); i != r.end(); ++i ) {
40851c0b2f7Stbbdev test_helper<T>::sum(range_sum, *i);
40951c0b2f7Stbbdev }
41051c0b2f7Stbbdev
41151c0b2f7Stbbdev // use const_range_type
41251c0b2f7Stbbdev const ets_type& csums = sums;
41351c0b2f7Stbbdev typename ets_type::const_range_type cr = csums.range();
41451c0b2f7Stbbdev for ( typename ets_type::const_range_type::iterator i = cr.begin(); i != cr.end(); ++i ) {
41551c0b2f7Stbbdev test_helper<T>::sum(const_range_sum, *i);
41651c0b2f7Stbbdev }
41751c0b2f7Stbbdev
41851c0b2f7Stbbdev // test copy constructor, with TLS-cached locals
41949e08aacStbbdev typedef typename oneapi::tbb::enumerable_thread_specific<T, Allocator<T>, oneapi::tbb::ets_key_per_instance> cached_ets_type;
42051c0b2f7Stbbdev
42151c0b2f7Stbbdev cached_ets_type cconst(sums);
422b15aabb3Stbbdev oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int>(0, N*p, RANGE_MIN), [&]( const oneapi::tbb::blocked_range<int>& ) {
42351c0b2f7Stbbdev bool exists = false;
42451c0b2f7Stbbdev T& ref = cconst.local(exists);
425b15aabb3Stbbdev CHECK( (exists || ref == default_value) );
42651c0b2f7Stbbdev } );
42751c0b2f7Stbbdev cached_ets_type cconst_to_assign1 = cconst;
42851c0b2f7Stbbdev cached_ets_type cconst_to_assign2;
42951c0b2f7Stbbdev cconst_to_assign2 = std::move(cconst_to_assign1);
43051c0b2f7Stbbdev REQUIRE(cconst_to_assign2.size() == cconst.size());
43151c0b2f7Stbbdev
43251c0b2f7Stbbdev for ( typename cached_ets_type::const_iterator i = cconst.begin(); i != cconst.end(); ++i ) {
43351c0b2f7Stbbdev test_helper<T>::sum(cconst_sum, *i);
43451c0b2f7Stbbdev }
43551c0b2f7Stbbdev
43651c0b2f7Stbbdev // test assignment
43751c0b2f7Stbbdev ets_type assigned;
43851c0b2f7Stbbdev assigned = sums;
43951c0b2f7Stbbdev
44051c0b2f7Stbbdev for ( typename ets_type::const_iterator i = assigned.begin(); i != assigned.end(); ++i ) {
44151c0b2f7Stbbdev test_helper<T>::sum(assign_sum, *i);
44251c0b2f7Stbbdev }
44351c0b2f7Stbbdev
44451c0b2f7Stbbdev // test assign to and from cached locals
44551c0b2f7Stbbdev cached_ets_type cassgn;
44651c0b2f7Stbbdev cassgn = sums;
44751c0b2f7Stbbdev for ( typename cached_ets_type::const_iterator i = cassgn.begin(); i != cassgn.end(); ++i ) {
44851c0b2f7Stbbdev test_helper<T>::sum(cassgn_sum, *i);
44951c0b2f7Stbbdev }
45051c0b2f7Stbbdev
45151c0b2f7Stbbdev ets_type non_cassgn;
45251c0b2f7Stbbdev non_cassgn = cassgn;
45351c0b2f7Stbbdev for ( typename ets_type::const_iterator i = non_cassgn.begin(); i != non_cassgn.end(); ++i ) {
45451c0b2f7Stbbdev test_helper<T>::sum(non_cassgn_sum, *i);
45551c0b2f7Stbbdev }
45651c0b2f7Stbbdev
45751c0b2f7Stbbdev // test finit-initialized ets
45851c0b2f7Stbbdev for(typename ets_type::const_iterator i = finit_ets.begin(); i != finit_ets.end(); ++i) {
45951c0b2f7Stbbdev test_helper<T>::sum(finit_ets_sum, *i);
46051c0b2f7Stbbdev }
46151c0b2f7Stbbdev
46251c0b2f7Stbbdev // test static ets
46351c0b2f7Stbbdev for(typename ets_type::const_iterator i = static_sums.begin(); i != static_sums.end(); ++i) {
46451c0b2f7Stbbdev test_helper<T>::sum(static_sum, *i);
46551c0b2f7Stbbdev }
46651c0b2f7Stbbdev
46751c0b2f7Stbbdev }
46851c0b2f7Stbbdev
469b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(iterator_sum));
470b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(const_iterator_sum));
471b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(range_sum));
472b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(const_range_sum));
47351c0b2f7Stbbdev
474b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(cconst_sum));
475b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(assign_sum));
476b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(cassgn_sum));
477b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(non_cassgn_sum));
478b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(finit_ets_sum));
479b15aabb3Stbbdev REQUIRE(EXPECTED_SUM*p == test_helper<T>::get(static_sum));
48051c0b2f7Stbbdev }
48151c0b2f7Stbbdev } // Checker block
48251c0b2f7Stbbdev }
48351c0b2f7Stbbdev
48451c0b2f7Stbbdev template< typename T, template<class> class Allocator>
run_parallel_scalar_tests(const char * test_name,const char * allocator_name)48551c0b2f7Stbbdev void run_parallel_scalar_tests(const char* test_name, const char* allocator_name) {
48651c0b2f7Stbbdev
48749e08aacStbbdev typedef oneapi::tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
48851c0b2f7Stbbdev bool exception_caught = false;
48951c0b2f7Stbbdev
49051c0b2f7Stbbdev // We assume that static_sums zero-initialized or has a default constructor that zeros it.
49151c0b2f7Stbbdev ets_type static_sums = ets_type( T() );
49251c0b2f7Stbbdev
49351c0b2f7Stbbdev T exemplar;
49451c0b2f7Stbbdev test_helper<T>::init(exemplar);
49551c0b2f7Stbbdev
49651c0b2f7Stbbdev int test_throw_count = 10;
49751c0b2f7Stbbdev // the test will be performed repeatedly until it does not throw. For non-throwing types
49851c0b2f7Stbbdev // this means once; for the throwing type test it may loop two or three times. The
49951c0b2f7Stbbdev // value of targetThrowValue will determine when and if the test will throw.
50051c0b2f7Stbbdev do {
50151c0b2f7Stbbdev targetThrowValue = test_throw_count; // keep testing until we get no exception
50251c0b2f7Stbbdev exception_caught = false;
50351c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
50451c0b2f7Stbbdev try {
50551c0b2f7Stbbdev #endif
50651c0b2f7Stbbdev run_parallel_scalar_tests_nocombine<T,Allocator>(test_name, allocator_name);
50751c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
50851c0b2f7Stbbdev }
509b15aabb3Stbbdev catch(...) {}
51051c0b2f7Stbbdev #endif
511b15aabb3Stbbdev for (int p = std::max(MinThread, 2); p <= MaxThread; ++p) {
51249e08aacStbbdev oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
51351c0b2f7Stbbdev
51451c0b2f7Stbbdev gThrowValue = 0;
51551c0b2f7Stbbdev
51651c0b2f7Stbbdev T combine_sum;
51751c0b2f7Stbbdev test_helper<T>::init(combine_sum);
51851c0b2f7Stbbdev
51951c0b2f7Stbbdev T combine_ref_sum;
52051c0b2f7Stbbdev test_helper<T>::init(combine_ref_sum);
52151c0b2f7Stbbdev
52251c0b2f7Stbbdev T accumulator_sum;
52351c0b2f7Stbbdev test_helper<T>::init(accumulator_sum);
52451c0b2f7Stbbdev
52551c0b2f7Stbbdev T static_sum;
52651c0b2f7Stbbdev test_helper<T>::init(static_sum);
52751c0b2f7Stbbdev
52851c0b2f7Stbbdev T clearing_accumulator_sum;
52951c0b2f7Stbbdev test_helper<T>::init(clearing_accumulator_sum);
53051c0b2f7Stbbdev
53151c0b2f7Stbbdev {
53251c0b2f7Stbbdev Checker<T> my_check;
53351c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
53451c0b2f7Stbbdev try
53551c0b2f7Stbbdev #endif
53651c0b2f7Stbbdev {
53751c0b2f7Stbbdev for (int t = -1; t < REPETITIONS; ++t) {
53851c0b2f7Stbbdev static_sums.clear();
53951c0b2f7Stbbdev
54051c0b2f7Stbbdev ets_type sums(exemplar);
54151c0b2f7Stbbdev
54251c0b2f7Stbbdev REQUIRE(sums.empty());
543b15aabb3Stbbdev oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<int>(0, N * p, RANGE_MIN),
54451c0b2f7Stbbdev parallel_scalar_body<T, Allocator>(sums, allocator_name));
54551c0b2f7Stbbdev REQUIRE(!sums.empty());
54651c0b2f7Stbbdev
54751c0b2f7Stbbdev REQUIRE(static_sums.empty());
548b15aabb3Stbbdev oneapi::tbb::parallel_for(oneapi::tbb::blocked_range<int>(0, N * p, RANGE_MIN),
54951c0b2f7Stbbdev parallel_scalar_body<T, Allocator>(static_sums, allocator_name));
55051c0b2f7Stbbdev REQUIRE(!static_sums.empty());
55151c0b2f7Stbbdev
55251c0b2f7Stbbdev // Use combine
55351c0b2f7Stbbdev test_helper<T>::sum(combine_sum, sums.combine(FunctionAdd<T>));
55451c0b2f7Stbbdev test_helper<T>::sum(combine_ref_sum, sums.combine(FunctionAddByRef<T>));
55551c0b2f7Stbbdev test_helper<T>::sum(static_sum, static_sums.combine(FunctionAdd<T>));
55651c0b2f7Stbbdev
55751c0b2f7Stbbdev // Accumulate with combine_each
55851c0b2f7Stbbdev sums.combine_each(Accumulator<T>(accumulator_sum));
55951c0b2f7Stbbdev // Accumulate and clear thread-local values
56051c0b2f7Stbbdev sums.combine_each(ClearingAccumulator<T>(clearing_accumulator_sum));
56151c0b2f7Stbbdev // Check that the values were cleared
56251c0b2f7Stbbdev sums.combine_each(ClearingAccumulator<T>::AssertClean);
56351c0b2f7Stbbdev }
56451c0b2f7Stbbdev }
56551c0b2f7Stbbdev #if TBB_USE_EXCEPTIONS
56651c0b2f7Stbbdev catch (...) {
56751c0b2f7Stbbdev exception_caught = true;
56851c0b2f7Stbbdev }
56951c0b2f7Stbbdev #endif
57051c0b2f7Stbbdev }
57151c0b2f7Stbbdev
572b15aabb3Stbbdev if (!exception_caught) {
573b15aabb3Stbbdev REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(combine_sum));
574b15aabb3Stbbdev REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(combine_ref_sum));
575b15aabb3Stbbdev REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(static_sum));
576b15aabb3Stbbdev REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(accumulator_sum));
577b15aabb3Stbbdev REQUIRE(EXPECTED_SUM * p == test_helper<T>::get(clearing_accumulator_sum));
578b15aabb3Stbbdev }
57951c0b2f7Stbbdev
58051c0b2f7Stbbdev } // MinThread .. MaxThread
58151c0b2f7Stbbdev test_throw_count += 10; // keep testing until we don't get an exception
58251c0b2f7Stbbdev } while (exception_caught && test_throw_count < 200);
58351c0b2f7Stbbdev REQUIRE_MESSAGE(!exception_caught, "No non-exception test completed");
58451c0b2f7Stbbdev }
58551c0b2f7Stbbdev
58651c0b2f7Stbbdev template <typename T, template<class> class Allocator>
58751c0b2f7Stbbdev class parallel_vector_for_body: utils::NoAssign {
58849e08aacStbbdev typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > container_type;
58949e08aacStbbdev typedef oneapi::tbb::enumerable_thread_specific< container_type, Allocator<container_type> > ets_type;
59051c0b2f7Stbbdev ets_type &locals;
59151c0b2f7Stbbdev const char *allocator_name;
59251c0b2f7Stbbdev
59351c0b2f7Stbbdev public:
59451c0b2f7Stbbdev
parallel_vector_for_body(ets_type & _locals,const char * aname)59551c0b2f7Stbbdev parallel_vector_for_body ( ets_type &_locals, const char *aname ) : locals(_locals), allocator_name(aname) { }
59651c0b2f7Stbbdev
operator ()(const oneapi::tbb::blocked_range<int> & r) const59749e08aacStbbdev void operator()( const oneapi::tbb::blocked_range<int> &r ) const {
59851c0b2f7Stbbdev T one;
59951c0b2f7Stbbdev test_helper<T>::set(one, 1);
60051c0b2f7Stbbdev
60151c0b2f7Stbbdev for (int i = r.begin(); i < r.end(); ++i) {
60251c0b2f7Stbbdev check_alignment(locals.local(),allocator_name).push_back( one );
60351c0b2f7Stbbdev }
60451c0b2f7Stbbdev }
60551c0b2f7Stbbdev
60651c0b2f7Stbbdev };
60751c0b2f7Stbbdev
60851c0b2f7Stbbdev template <typename R, typename T>
60951c0b2f7Stbbdev struct parallel_vector_reduce_body {
61051c0b2f7Stbbdev
61151c0b2f7Stbbdev T sum;
61251c0b2f7Stbbdev size_t count;
61349e08aacStbbdev typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > container_type;
61451c0b2f7Stbbdev
parallel_vector_reduce_bodyparallel_vector_reduce_body61551c0b2f7Stbbdev parallel_vector_reduce_body ( ) : count(0) { test_helper<T>::init(sum); }
parallel_vector_reduce_bodyparallel_vector_reduce_body61649e08aacStbbdev parallel_vector_reduce_body ( parallel_vector_reduce_body<R, T> &, oneapi::tbb::split ) : count(0) { test_helper<T>::init(sum); }
61751c0b2f7Stbbdev
operator ()parallel_vector_reduce_body61851c0b2f7Stbbdev void operator()( const R &r ) {
61951c0b2f7Stbbdev for (typename R::iterator ri = r.begin(); ri != r.end(); ++ri) {
62051c0b2f7Stbbdev const container_type &v = *ri;
62151c0b2f7Stbbdev ++count;
62251c0b2f7Stbbdev for (typename container_type::const_iterator vi = v.begin(); vi != v.end(); ++vi) {
62351c0b2f7Stbbdev test_helper<T>::sum(sum, *vi);
62451c0b2f7Stbbdev }
62551c0b2f7Stbbdev }
62651c0b2f7Stbbdev }
62751c0b2f7Stbbdev
joinparallel_vector_reduce_body62851c0b2f7Stbbdev void join( const parallel_vector_reduce_body &b ) {
62951c0b2f7Stbbdev test_helper<T>::sum(sum,b.sum);
63051c0b2f7Stbbdev count += b.count;
63151c0b2f7Stbbdev }
63251c0b2f7Stbbdev
63351c0b2f7Stbbdev };
63451c0b2f7Stbbdev
63551c0b2f7Stbbdev template< typename T, template<class> class Allocator>
run_parallel_vector_tests(const char *,const char * allocator_name)63651c0b2f7Stbbdev void run_parallel_vector_tests(const char* /* test_name */, const char *allocator_name) {
63749e08aacStbbdev typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > container_type;
63849e08aacStbbdev typedef oneapi::tbb::enumerable_thread_specific< container_type, Allocator<container_type> > ets_type;
63951c0b2f7Stbbdev
640b15aabb3Stbbdev for (int p = std::max(MinThread, 2); p <= MaxThread; ++p) {
64149e08aacStbbdev oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
64251c0b2f7Stbbdev
64351c0b2f7Stbbdev T sum;
64451c0b2f7Stbbdev test_helper<T>::init(sum);
64551c0b2f7Stbbdev
64651c0b2f7Stbbdev for (int t = -1; t < REPETITIONS; ++t) {
64751c0b2f7Stbbdev ets_type vs;
64851c0b2f7Stbbdev
64951c0b2f7Stbbdev REQUIRE( vs.empty() );
650b15aabb3Stbbdev oneapi::tbb::parallel_for( oneapi::tbb::blocked_range<int> (0, N*p, RANGE_MIN),
65151c0b2f7Stbbdev parallel_vector_for_body<T,Allocator>( vs, allocator_name ) );
65251c0b2f7Stbbdev REQUIRE( !vs.empty() );
65351c0b2f7Stbbdev
65451c0b2f7Stbbdev // copy construct
65551c0b2f7Stbbdev ets_type vs2(vs); // this causes an assertion failure, related to allocators...
65651c0b2f7Stbbdev
65751c0b2f7Stbbdev // assign
65851c0b2f7Stbbdev ets_type vs3;
65951c0b2f7Stbbdev vs3 = vs;
66051c0b2f7Stbbdev
66151c0b2f7Stbbdev parallel_vector_reduce_body< typename ets_type::const_range_type, T > pvrb;
66249e08aacStbbdev oneapi::tbb::parallel_reduce ( vs.range(1), pvrb );
66351c0b2f7Stbbdev
66451c0b2f7Stbbdev test_helper<T>::sum(sum, pvrb.sum);
66551c0b2f7Stbbdev
66651c0b2f7Stbbdev REQUIRE( vs.size() == pvrb.count );
66751c0b2f7Stbbdev REQUIRE( vs2.size() == pvrb.count );
66851c0b2f7Stbbdev REQUIRE( vs3.size() == pvrb.count );
66951c0b2f7Stbbdev
67049e08aacStbbdev oneapi::tbb::flattened2d<ets_type> fvs = flatten2d(vs);
67151c0b2f7Stbbdev size_t ccount = fvs.size();
672b15aabb3Stbbdev REQUIRE( ccount == size_t(N*p) );
67351c0b2f7Stbbdev size_t elem_cnt = 0;
67449e08aacStbbdev typename oneapi::tbb::flattened2d<ets_type>::iterator it;
67551c0b2f7Stbbdev auto it2(it);
67651c0b2f7Stbbdev it = fvs.begin();
67751c0b2f7Stbbdev REQUIRE(it != it2);
67849e08aacStbbdev typename oneapi::tbb::flattened2d<ets_type>::iterator it3;
67949e08aacStbbdev typename oneapi::tbb::flattened2d<ets_type>::const_iterator cit = fvs.begin();
68051c0b2f7Stbbdev it3 = cit;
68151c0b2f7Stbbdev REQUIRE(it3 == cit);
68251c0b2f7Stbbdev REQUIRE(it3.operator->() == &(*it3));
68351c0b2f7Stbbdev
68449e08aacStbbdev for(typename oneapi::tbb::flattened2d<ets_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) {
68551c0b2f7Stbbdev ++elem_cnt;
68651c0b2f7Stbbdev };
68751c0b2f7Stbbdev REQUIRE( ccount == elem_cnt );
68851c0b2f7Stbbdev
68951c0b2f7Stbbdev elem_cnt = 0;
69049e08aacStbbdev for(typename oneapi::tbb::flattened2d<ets_type>::iterator i = fvs.begin(); i != fvs.end(); i++) {
69151c0b2f7Stbbdev ++elem_cnt;
69251c0b2f7Stbbdev };
69351c0b2f7Stbbdev REQUIRE( ccount == elem_cnt );
69451c0b2f7Stbbdev
69551c0b2f7Stbbdev // Test the ETS constructor with multiple args
69651c0b2f7Stbbdev T minus_one;
69751c0b2f7Stbbdev test_helper<T>::set(minus_one, -1);
69851c0b2f7Stbbdev // Set ETS to construct "local" vectors pre-occupied with 25 "minus_one"s
69951c0b2f7Stbbdev // Cast 25 to size_type to prevent Intel Compiler SFINAE compilation issues with gcc 5.
70049e08aacStbbdev ets_type vvs( typename container_type::size_type(25), minus_one, oneapi::tbb::tbb_allocator<T>() );
70151c0b2f7Stbbdev REQUIRE( vvs.empty() );
702b15aabb3Stbbdev oneapi::tbb::parallel_for ( oneapi::tbb::blocked_range<int> (0, N*p, RANGE_MIN), parallel_vector_for_body<T,Allocator>( vvs, allocator_name ) );
70351c0b2f7Stbbdev REQUIRE( !vvs.empty() );
70451c0b2f7Stbbdev
70551c0b2f7Stbbdev parallel_vector_reduce_body< typename ets_type::const_range_type, T > pvrb2;
70649e08aacStbbdev oneapi::tbb::parallel_reduce ( vvs.range(1), pvrb2 );
70751c0b2f7Stbbdev REQUIRE( pvrb2.count == vvs.size() );
708b15aabb3Stbbdev REQUIRE( test_helper<T>::get(pvrb2.sum) == N*p-pvrb2.count*25 );
70951c0b2f7Stbbdev
71049e08aacStbbdev oneapi::tbb::flattened2d<ets_type> fvvs = flatten2d(vvs);
71151c0b2f7Stbbdev ccount = fvvs.size();
712b15aabb3Stbbdev REQUIRE( ccount == N*p+pvrb2.count*25 );
71351c0b2f7Stbbdev }
71451c0b2f7Stbbdev
71551c0b2f7Stbbdev double result_value = test_helper<T>::get(sum);
716b15aabb3Stbbdev REQUIRE( EXPECTED_SUM*p == result_value);
71751c0b2f7Stbbdev }
71851c0b2f7Stbbdev }
71951c0b2f7Stbbdev
72051c0b2f7Stbbdev template<typename T, template<class> class Allocator>
run_cross_type_vector_tests(const char *)72151c0b2f7Stbbdev void run_cross_type_vector_tests(const char* /* test_name */) {
72251c0b2f7Stbbdev const char* allocator_name = "default";
72349e08aacStbbdev typedef std::vector<T, oneapi::tbb::tbb_allocator<T> > container_type;
72451c0b2f7Stbbdev
725b15aabb3Stbbdev for (int p = std::max(MinThread, 2); p <= MaxThread; ++p) {
72649e08aacStbbdev oneapi::tbb::global_control gc(oneapi::tbb::global_control::max_allowed_parallelism, p);
72751c0b2f7Stbbdev
72851c0b2f7Stbbdev T sum;
72951c0b2f7Stbbdev test_helper<T>::init(sum);
73051c0b2f7Stbbdev
73151c0b2f7Stbbdev for (int t = -1; t < REPETITIONS; ++t) {
73249e08aacStbbdev typedef typename oneapi::tbb::enumerable_thread_specific< container_type, Allocator<container_type>, oneapi::tbb::ets_no_key > ets_nokey_type;
73349e08aacStbbdev typedef typename oneapi::tbb::enumerable_thread_specific< container_type, Allocator<container_type>, oneapi::tbb::ets_key_per_instance > ets_tlskey_type;
73451c0b2f7Stbbdev ets_nokey_type vs;
73551c0b2f7Stbbdev
73651c0b2f7Stbbdev REQUIRE( vs.empty());
737b15aabb3Stbbdev oneapi::tbb::parallel_for ( oneapi::tbb::blocked_range<int> (0, N*p, RANGE_MIN), parallel_vector_for_body<T, Allocator>( vs, allocator_name ) );
73851c0b2f7Stbbdev REQUIRE( !vs.empty());
73951c0b2f7Stbbdev
74051c0b2f7Stbbdev // copy construct
74151c0b2f7Stbbdev ets_tlskey_type vs2(vs);
74251c0b2f7Stbbdev
74351c0b2f7Stbbdev // assign
74451c0b2f7Stbbdev ets_nokey_type vs3;
74551c0b2f7Stbbdev vs3 = vs2;
74651c0b2f7Stbbdev
74751c0b2f7Stbbdev parallel_vector_reduce_body< typename ets_nokey_type::const_range_type, T > pvrb;
74849e08aacStbbdev oneapi::tbb::parallel_reduce ( vs3.range(1), pvrb );
74951c0b2f7Stbbdev
75051c0b2f7Stbbdev test_helper<T>::sum(sum, pvrb.sum);
75151c0b2f7Stbbdev
75251c0b2f7Stbbdev REQUIRE( vs3.size() == pvrb.count);
75351c0b2f7Stbbdev
75449e08aacStbbdev oneapi::tbb::flattened2d<ets_nokey_type> fvs = flatten2d(vs3);
75551c0b2f7Stbbdev size_t ccount = fvs.size();
75651c0b2f7Stbbdev size_t elem_cnt = 0;
75749e08aacStbbdev for(typename oneapi::tbb::flattened2d<ets_nokey_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) {
75851c0b2f7Stbbdev ++elem_cnt;
75951c0b2f7Stbbdev };
76051c0b2f7Stbbdev REQUIRE(ccount == elem_cnt);
76151c0b2f7Stbbdev
76251c0b2f7Stbbdev elem_cnt = 0;
76349e08aacStbbdev for(typename oneapi::tbb::flattened2d<ets_nokey_type>::iterator i = fvs.begin(); i != fvs.end(); ++i) {
76451c0b2f7Stbbdev ++elem_cnt;
76551c0b2f7Stbbdev };
76651c0b2f7Stbbdev REQUIRE(ccount == elem_cnt);
76751c0b2f7Stbbdev
76849e08aacStbbdev oneapi::tbb::flattened2d<ets_nokey_type> fvs2 = flatten2d(vs3, vs3.begin(), std::next(vs3.begin()));
76951c0b2f7Stbbdev REQUIRE(std::distance(fvs2.begin(), fvs2.end()) == vs3.begin()->size());
77049e08aacStbbdev const oneapi::tbb::flattened2d<ets_nokey_type>& cfvs2(fvs2);
77151c0b2f7Stbbdev REQUIRE(std::distance(cfvs2.begin(), cfvs2.end()) == vs3.begin()->size());
77251c0b2f7Stbbdev }
77351c0b2f7Stbbdev
77451c0b2f7Stbbdev double result_value = test_helper<T>::get(sum);
775b15aabb3Stbbdev REQUIRE( EXPECTED_SUM*p == result_value);
77651c0b2f7Stbbdev }
77751c0b2f7Stbbdev }
77851c0b2f7Stbbdev
77951c0b2f7Stbbdev template< typename T >
run_serial_scalar_tests(const char *)78051c0b2f7Stbbdev void run_serial_scalar_tests(const char* /* test_name */) {
78151c0b2f7Stbbdev T sum;
78251c0b2f7Stbbdev test_helper<T>::init(sum);
78351c0b2f7Stbbdev
78451c0b2f7Stbbdev for (int t = -1; t < REPETITIONS; ++t) {
78551c0b2f7Stbbdev for (int i = 0; i < N; ++i) {
78651c0b2f7Stbbdev test_helper<T>::sum(sum,1);
78751c0b2f7Stbbdev }
78851c0b2f7Stbbdev }
78951c0b2f7Stbbdev
79051c0b2f7Stbbdev double result_value = test_helper<T>::get(sum);
79151c0b2f7Stbbdev REQUIRE( EXPECTED_SUM == result_value);
79251c0b2f7Stbbdev }
79351c0b2f7Stbbdev
79451c0b2f7Stbbdev template< typename T >
run_serial_vector_tests(const char *)79551c0b2f7Stbbdev void run_serial_vector_tests(const char* /* test_name */) {
79651c0b2f7Stbbdev T sum;
79751c0b2f7Stbbdev test_helper<T>::init(sum);
79851c0b2f7Stbbdev T one;
79951c0b2f7Stbbdev test_helper<T>::set(one, 1);
80051c0b2f7Stbbdev
80151c0b2f7Stbbdev for (int t = -1; t < REPETITIONS; ++t) {
80249e08aacStbbdev std::vector<T, oneapi::tbb::tbb_allocator<T> > v;
80351c0b2f7Stbbdev for (int i = 0; i < N; ++i) {
80451c0b2f7Stbbdev v.push_back( one );
80551c0b2f7Stbbdev }
80649e08aacStbbdev for (typename std::vector<T, oneapi::tbb::tbb_allocator<T> >::const_iterator i = v.begin(); i != v.end(); ++i)
80751c0b2f7Stbbdev test_helper<T>::sum(sum, *i);
80851c0b2f7Stbbdev }
80951c0b2f7Stbbdev
81051c0b2f7Stbbdev double result_value = test_helper<T>::get(sum);
81151c0b2f7Stbbdev REQUIRE( EXPECTED_SUM == result_value);
81251c0b2f7Stbbdev }
81351c0b2f7Stbbdev
81449e08aacStbbdev const size_t line_size = oneapi::tbb::detail::max_nfs_size;
81551c0b2f7Stbbdev
run_reference_check()81651c0b2f7Stbbdev void run_reference_check() {
81751c0b2f7Stbbdev run_serial_scalar_tests<int>("int");
81851c0b2f7Stbbdev run_serial_scalar_tests<double>("double");
819*90b0671bSAlex run_serial_scalar_tests<minimalNComparable<> >("minimalNComparable<>");
82049e08aacStbbdev run_serial_vector_tests<int>("std::vector<int, oneapi::tbb::tbb_allocator<int> >");
82149e08aacStbbdev run_serial_vector_tests<double>("std::vector<double, oneapi::tbb::tbb_allocator<double> >");
82251c0b2f7Stbbdev }
82351c0b2f7Stbbdev
82451c0b2f7Stbbdev template<template<class>class Allocator>
run_parallel_tests(const char * allocator_name)82551c0b2f7Stbbdev void run_parallel_tests(const char *allocator_name) {
82651c0b2f7Stbbdev run_parallel_scalar_tests<int, Allocator>("int",allocator_name);
82751c0b2f7Stbbdev run_parallel_scalar_tests<double, Allocator>("double",allocator_name);
828*90b0671bSAlex run_parallel_scalar_tests_nocombine<minimalNComparable<>,Allocator>("minimalNComparable<>",allocator_name);
82951c0b2f7Stbbdev run_parallel_scalar_tests<ThrowingConstructor, Allocator>("ThrowingConstructor", allocator_name);
83049e08aacStbbdev run_parallel_vector_tests<int, Allocator>("std::vector<int, oneapi::tbb::tbb_allocator<int> >",allocator_name);
83149e08aacStbbdev run_parallel_vector_tests<double, Allocator>("std::vector<double, oneapi::tbb::tbb_allocator<double> >",allocator_name);
83251c0b2f7Stbbdev }
83351c0b2f7Stbbdev
run_cross_type_tests()83451c0b2f7Stbbdev void run_cross_type_tests() {
83551c0b2f7Stbbdev // cross-type scalar tests are part of run_parallel_scalar_tests_nocombine
83649e08aacStbbdev run_cross_type_vector_tests<int, oneapi::tbb::tbb_allocator>("std::vector<int, oneapi::tbb::tbb_allocator<int> >");
83749e08aacStbbdev run_cross_type_vector_tests<double, oneapi::tbb::tbb_allocator>("std::vector<double, oneapi::tbb::tbb_allocator<double> >");
83851c0b2f7Stbbdev }
83951c0b2f7Stbbdev
84051c0b2f7Stbbdev template<typename T, template<class> class Allocator, typename Init>
MakeETS(Init init)84149e08aacStbbdev oneapi::tbb::enumerable_thread_specific<T,Allocator<T> > MakeETS( Init init ) {
84249e08aacStbbdev return oneapi::tbb::enumerable_thread_specific<T,Allocator<T> >(init);
84351c0b2f7Stbbdev }
84451c0b2f7Stbbdev // In some GCC versions, parameter packs in lambdas might cause compile errors
84551c0b2f7Stbbdev template<typename ETS, typename... P>
84651c0b2f7Stbbdev struct MakeETS_Functor {
operator ()MakeETS_Functor84751c0b2f7Stbbdev ETS operator()( typename std::decay<P>::type&&... params ) {
84851c0b2f7Stbbdev return ETS(std::move(params)...);
84951c0b2f7Stbbdev }
85051c0b2f7Stbbdev };
85151c0b2f7Stbbdev template<typename T, template<class> class Allocator, typename... P>
MakeETS(oneapi::tbb::detail::stored_pack<P...> pack)85249e08aacStbbdev oneapi::tbb::enumerable_thread_specific<T,Allocator<T> > MakeETS( oneapi::tbb::detail::stored_pack<P...> pack ) {
85349e08aacStbbdev typedef oneapi::tbb::enumerable_thread_specific<T,Allocator<T> > result_type;
85449e08aacStbbdev return oneapi::tbb::detail::call_and_return< result_type >(
85551c0b2f7Stbbdev MakeETS_Functor<result_type,P...>(), std::move(pack)
85651c0b2f7Stbbdev );
85751c0b2f7Stbbdev }
85851c0b2f7Stbbdev
85951c0b2f7Stbbdev template<typename T, template<class> class Allocator, typename InitSrc, typename InitDst, typename Validator>
ets_copy_assign_test(InitSrc init1,InitDst init2,Validator check,const char * allocator_name)86051c0b2f7Stbbdev void ets_copy_assign_test( InitSrc init1, InitDst init2, Validator check, const char *allocator_name ) {
86149e08aacStbbdev typedef oneapi::tbb::enumerable_thread_specific<T, Allocator<T> > ets_type;
86251c0b2f7Stbbdev
86351c0b2f7Stbbdev // Create the source instance
86451c0b2f7Stbbdev const ets_type& cref_binder = MakeETS<T, Allocator>(init1);
86551c0b2f7Stbbdev ets_type& source = const_cast<ets_type&>(cref_binder);
86651c0b2f7Stbbdev check(check_alignment(source.local(),allocator_name));
86751c0b2f7Stbbdev
86851c0b2f7Stbbdev // Test copy construction
86951c0b2f7Stbbdev bool existed = false;
87051c0b2f7Stbbdev ets_type copy(source);
87151c0b2f7Stbbdev check(check_alignment(copy.local(existed),allocator_name));
87251c0b2f7Stbbdev REQUIRE_MESSAGE(existed, "Local data not created by ETS copy constructor");
87351c0b2f7Stbbdev copy.clear();
87451c0b2f7Stbbdev check(check_alignment(copy.local(),allocator_name));
87551c0b2f7Stbbdev
87651c0b2f7Stbbdev // Test assignment
87751c0b2f7Stbbdev existed = false;
87851c0b2f7Stbbdev ets_type assign(init2);
87951c0b2f7Stbbdev assign = source;
88051c0b2f7Stbbdev check(check_alignment(assign.local(existed),allocator_name));
88151c0b2f7Stbbdev REQUIRE_MESSAGE(existed, "Local data not created by ETS assignment");
88251c0b2f7Stbbdev assign.clear();
88351c0b2f7Stbbdev check(check_alignment(assign.local(),allocator_name));
88451c0b2f7Stbbdev
88551c0b2f7Stbbdev // Create the source instance
88651c0b2f7Stbbdev ets_type&& rvref_binder = MakeETS<T, Allocator>(init1);
88751c0b2f7Stbbdev check(check_alignment(rvref_binder.local(),allocator_name));
88851c0b2f7Stbbdev
88951c0b2f7Stbbdev // Test move construction
89051c0b2f7Stbbdev existed = false;
89151c0b2f7Stbbdev ets_type moved(rvref_binder);
89251c0b2f7Stbbdev check(check_alignment(moved.local(existed),allocator_name));
89351c0b2f7Stbbdev REQUIRE_MESSAGE(existed, "Local data not created by ETS move constructor");
89451c0b2f7Stbbdev moved.clear();
89551c0b2f7Stbbdev check(check_alignment(moved.local(),allocator_name));
89651c0b2f7Stbbdev
89751c0b2f7Stbbdev // Test assignment
89851c0b2f7Stbbdev existed = false;
89951c0b2f7Stbbdev ets_type move_assign(init2);
90051c0b2f7Stbbdev move_assign = std::move(moved);
90151c0b2f7Stbbdev check(check_alignment(move_assign.local(existed),allocator_name));
90251c0b2f7Stbbdev REQUIRE_MESSAGE(existed, "Local data not created by ETS move assignment");
90351c0b2f7Stbbdev move_assign.clear();
90451c0b2f7Stbbdev check(check_alignment(move_assign.local(),allocator_name));
90551c0b2f7Stbbdev }
90651c0b2f7Stbbdev
90751c0b2f7Stbbdev template<typename T, int Expected>
90851c0b2f7Stbbdev struct Validator {
operator ()Validator90951c0b2f7Stbbdev void operator()( const T& value ) {
91051c0b2f7Stbbdev REQUIRE(test_helper<T>::get(value) == Expected);
91151c0b2f7Stbbdev }
operator ()Validator91251c0b2f7Stbbdev void operator()( const std::pair<int,T>& value ) {
91351c0b2f7Stbbdev REQUIRE(value.first > 0);
91451c0b2f7Stbbdev REQUIRE(test_helper<T>::get(value.second) == Expected*value.first);
91551c0b2f7Stbbdev }
91651c0b2f7Stbbdev };
91751c0b2f7Stbbdev
91851c0b2f7Stbbdev template <typename T, template<class> class Allocator>
run_assign_and_copy_constructor_test(const char *,const char * allocator_name)91951c0b2f7Stbbdev void run_assign_and_copy_constructor_test(const char* /* test_name */, const char *allocator_name) {
92051c0b2f7Stbbdev #define EXPECTED 3142
92151c0b2f7Stbbdev
92251c0b2f7Stbbdev // test with exemplar initializer
92351c0b2f7Stbbdev T src_init;
92451c0b2f7Stbbdev test_helper<T>::set(src_init,EXPECTED);
92551c0b2f7Stbbdev T other_init;
92651c0b2f7Stbbdev test_helper<T>::init(other_init);
92751c0b2f7Stbbdev ets_copy_assign_test<T, Allocator>(src_init, other_init, Validator<T,EXPECTED>(), allocator_name);
92851c0b2f7Stbbdev
92951c0b2f7Stbbdev // test with function initializer
93051c0b2f7Stbbdev FunctorFinit<T,EXPECTED> src_finit(SecretTag);
93151c0b2f7Stbbdev FunctorFinit<T,0> other_finit(SecretTag);
93251c0b2f7Stbbdev ets_copy_assign_test<T, Allocator>(src_finit, other_finit, Validator<T,EXPECTED>(), allocator_name);
93351c0b2f7Stbbdev
93451c0b2f7Stbbdev // test with multi-argument "emplace" initializer
93549e08aacStbbdev // The arguments are wrapped into oneapi::tbb::internal::stored_pack to avoid variadic templates in ets_copy_assign_test.
93651c0b2f7Stbbdev test_helper<T>::set(src_init,EXPECTED*17);
93749e08aacStbbdev ets_copy_assign_test< std::pair<int,T>, Allocator>(oneapi::tbb::detail::save_pack(17,src_init), std::make_pair(-1,T()), Validator<T,EXPECTED>(), allocator_name);
93851c0b2f7Stbbdev #undef EXPECTED
93951c0b2f7Stbbdev }
94051c0b2f7Stbbdev
94151c0b2f7Stbbdev template< template<class> class Allocator>
run_assignment_and_copy_constructor_tests(const char * allocator_name)94251c0b2f7Stbbdev void run_assignment_and_copy_constructor_tests(const char* allocator_name) {
94351c0b2f7Stbbdev run_assign_and_copy_constructor_test<int, Allocator>("int", allocator_name);
94451c0b2f7Stbbdev run_assign_and_copy_constructor_test<double, Allocator>("double", allocator_name);
94551c0b2f7Stbbdev // Try class sizes that are close to a cache line in size, in order to check padding calculations.
946*90b0671bSAlex run_assign_and_copy_constructor_test<minimalNComparable<line_size-1>, Allocator >("minimalNComparable<line_size-1>", allocator_name);
947*90b0671bSAlex run_assign_and_copy_constructor_test<minimalNComparable<line_size>, Allocator >("minimalNComparable<line_size>", allocator_name);
948*90b0671bSAlex run_assign_and_copy_constructor_test<minimalNComparable<line_size+1>, Allocator >("minimalNComparable<line_size+1>", allocator_name);
94951c0b2f7Stbbdev REQUIRE(FinitCounter==0);
95051c0b2f7Stbbdev }
95151c0b2f7Stbbdev
95251c0b2f7Stbbdev // Class with no default constructor
95351c0b2f7Stbbdev class HasNoDefaultConstructor {
95451c0b2f7Stbbdev HasNoDefaultConstructor();
95551c0b2f7Stbbdev public:
HasNoDefaultConstructor(SecretTagType)95651c0b2f7Stbbdev HasNoDefaultConstructor( SecretTagType ) {}
95751c0b2f7Stbbdev };
95851c0b2f7Stbbdev // Initialization functor for HasNoDefaultConstructor
95951c0b2f7Stbbdev struct HasNoDefaultConstructorFinit {
operator ()HasNoDefaultConstructorFinit96051c0b2f7Stbbdev HasNoDefaultConstructor operator()() {
96151c0b2f7Stbbdev return HasNoDefaultConstructor(SecretTag);
96251c0b2f7Stbbdev }
96351c0b2f7Stbbdev };
96451c0b2f7Stbbdev // Combine functor for HasNoDefaultConstructor
96551c0b2f7Stbbdev struct HasNoDefaultConstructorCombine {
operator ()HasNoDefaultConstructorCombine96651c0b2f7Stbbdev HasNoDefaultConstructor operator()( HasNoDefaultConstructor, HasNoDefaultConstructor ) {
96751c0b2f7Stbbdev return HasNoDefaultConstructor(SecretTag);
96851c0b2f7Stbbdev }
96951c0b2f7Stbbdev };
97051c0b2f7Stbbdev
97151c0b2f7Stbbdev // Class that only has a constructor with multiple parameters and a move constructor
97251c0b2f7Stbbdev class HasSpecialAndMoveCtor : utils::NoCopy {
97351c0b2f7Stbbdev HasSpecialAndMoveCtor();
97451c0b2f7Stbbdev public:
HasSpecialAndMoveCtor(SecretTagType,size_t=size_t (0),const char * ="")97551c0b2f7Stbbdev HasSpecialAndMoveCtor( SecretTagType, size_t = size_t(0), const char* = "" ) {}
HasSpecialAndMoveCtor(HasSpecialAndMoveCtor &&)97651c0b2f7Stbbdev HasSpecialAndMoveCtor( HasSpecialAndMoveCtor&& ) {}
97751c0b2f7Stbbdev };
97851c0b2f7Stbbdev
97951c0b2f7Stbbdev // No-op combine-each functor
98051c0b2f7Stbbdev template<typename V>
98151c0b2f7Stbbdev struct EmptyCombineEach {
operator ()EmptyCombineEach98251c0b2f7Stbbdev void operator()( const V& ) { }
98351c0b2f7Stbbdev };
98451c0b2f7Stbbdev
98551c0b2f7Stbbdev //! Test situations where only default constructor or copy constructor is required.
98651c0b2f7Stbbdev template<template<class> class Allocator>
TestInstantiation(const char *)98751c0b2f7Stbbdev void TestInstantiation(const char* /* allocator_name */) {
98851c0b2f7Stbbdev // Test instantiation is possible when copy constructor is not required.
98949e08aacStbbdev oneapi::tbb::enumerable_thread_specific<utils::NoCopy, Allocator<utils::NoCopy> > ets1;
99051c0b2f7Stbbdev ets1.local();
99151c0b2f7Stbbdev ets1.combine_each(EmptyCombineEach<utils::NoCopy>());
99251c0b2f7Stbbdev
99351c0b2f7Stbbdev // Test instantiation when default constructor is not required, because exemplar is provided.
99451c0b2f7Stbbdev HasNoDefaultConstructor x(SecretTag);
99549e08aacStbbdev oneapi::tbb::enumerable_thread_specific<HasNoDefaultConstructor, Allocator<HasNoDefaultConstructor> > ets2(x);
99651c0b2f7Stbbdev ets2.local();
99751c0b2f7Stbbdev ets2.combine(HasNoDefaultConstructorCombine());
99851c0b2f7Stbbdev
99951c0b2f7Stbbdev // Test instantiation when default constructor is not required, because init function is provided.
100051c0b2f7Stbbdev HasNoDefaultConstructorFinit f;
100149e08aacStbbdev oneapi::tbb::enumerable_thread_specific<HasNoDefaultConstructor, Allocator<HasNoDefaultConstructor> > ets3(f);
100251c0b2f7Stbbdev ets3.local();
100351c0b2f7Stbbdev ets3.combine(HasNoDefaultConstructorCombine());
100451c0b2f7Stbbdev
100551c0b2f7Stbbdev // Test instantiation with multiple arguments
100649e08aacStbbdev oneapi::tbb::enumerable_thread_specific<HasSpecialAndMoveCtor, Allocator<HasSpecialAndMoveCtor> > ets4(SecretTag, 0x42, "meaningless");
100751c0b2f7Stbbdev ets4.local();
100851c0b2f7Stbbdev ets4.combine_each(EmptyCombineEach<HasSpecialAndMoveCtor>());
100951c0b2f7Stbbdev // Test instantiation with one argument that should however use the variadic constructor
101049e08aacStbbdev oneapi::tbb::enumerable_thread_specific<HasSpecialAndMoveCtor, Allocator<HasSpecialAndMoveCtor> > ets5(SecretTag);
101151c0b2f7Stbbdev ets5.local();
101251c0b2f7Stbbdev ets5.combine_each(EmptyCombineEach<HasSpecialAndMoveCtor>());
101351c0b2f7Stbbdev // Test that move operations do not impose extra requirements
101451c0b2f7Stbbdev // Default allocator is used. If it does not match Allocator, there will be elementwise move
101549e08aacStbbdev oneapi::tbb::enumerable_thread_specific<HasSpecialAndMoveCtor> ets6( std::move(ets4) );
101651c0b2f7Stbbdev ets6.combine_each(EmptyCombineEach<HasSpecialAndMoveCtor>());
101751c0b2f7Stbbdev ets6 = std::move(ets5);
101851c0b2f7Stbbdev }
101951c0b2f7Stbbdev
TestMemberTypes()102051c0b2f7Stbbdev void TestMemberTypes() {
102149e08aacStbbdev using default_container_type = oneapi::tbb::enumerable_thread_specific<int>;
102249e08aacStbbdev static_assert(std::is_same<typename default_container_type::allocator_type, oneapi::tbb::cache_aligned_allocator<int>>::value,
102351c0b2f7Stbbdev "Incorrect default template allocator");
102451c0b2f7Stbbdev
102551c0b2f7Stbbdev using test_allocator_type = std::allocator<int>;
102649e08aacStbbdev using ets_container_type = oneapi::tbb::enumerable_thread_specific<int, test_allocator_type>;
102751c0b2f7Stbbdev
102851c0b2f7Stbbdev static_assert(std::is_same<typename ets_container_type::allocator_type, test_allocator_type>::value,
102951c0b2f7Stbbdev "Incorrect container allocator_type member type");
103051c0b2f7Stbbdev
103151c0b2f7Stbbdev using value_type = typename ets_container_type::value_type;
103251c0b2f7Stbbdev
103351c0b2f7Stbbdev static_assert(std::is_same<typename ets_container_type::value_type, int>::value,
103451c0b2f7Stbbdev "Incorrect container value_type member type");
103551c0b2f7Stbbdev static_assert(std::is_same<typename ets_container_type::reference, value_type&>::value,
103651c0b2f7Stbbdev "Incorrect container reference member type");
103751c0b2f7Stbbdev static_assert(std::is_same<typename ets_container_type::const_reference, const value_type&>::value,
103851c0b2f7Stbbdev "Incorrect container const_reference member type");
103951c0b2f7Stbbdev
104051c0b2f7Stbbdev using allocator_type = typename ets_container_type::allocator_type;
104151c0b2f7Stbbdev static_assert(std::is_same<typename ets_container_type::pointer, typename std::allocator_traits<allocator_type>::pointer>::value,
104251c0b2f7Stbbdev "Incorrect container pointer member type");
104351c0b2f7Stbbdev static_assert(std::is_same<typename ets_container_type::const_pointer, typename std::allocator_traits<allocator_type>::const_pointer>::value,
104451c0b2f7Stbbdev "Incorrect container const_pointer member type");
104551c0b2f7Stbbdev
104651c0b2f7Stbbdev static_assert(std::is_unsigned<typename ets_container_type::size_type>::value,
104751c0b2f7Stbbdev "Incorrect container size_type member type");
104851c0b2f7Stbbdev static_assert(std::is_signed<typename ets_container_type::difference_type>::value,
104951c0b2f7Stbbdev "Incorrect container difference_type member type");
105051c0b2f7Stbbdev
105151c0b2f7Stbbdev static_assert(utils::is_random_access_iterator<typename ets_container_type::iterator>::value,
105251c0b2f7Stbbdev "Incorrect container iterator member type");
105351c0b2f7Stbbdev static_assert(!std::is_const<typename ets_container_type::iterator::value_type>::value,
105451c0b2f7Stbbdev "Incorrect container iterator member type");
105551c0b2f7Stbbdev static_assert(utils::is_random_access_iterator<typename ets_container_type::const_iterator>::value,
105651c0b2f7Stbbdev "Incorrect container const_iterator member type");
105751c0b2f7Stbbdev static_assert(std::is_const<typename ets_container_type::const_iterator::value_type>::value,
105851c0b2f7Stbbdev "Incorrect container iterator member type");
105951c0b2f7Stbbdev }
106051c0b2f7Stbbdev
init_tbb_alloc_mask()106151c0b2f7Stbbdev size_t init_tbb_alloc_mask() {
106251c0b2f7Stbbdev // TODO: use __TBB_alignof(T) to check for local() results instead of using internal knowledges of ets element padding
106349e08aacStbbdev if(oneapi::tbb::tbb_allocator<int>::allocator_type() == oneapi::tbb::tbb_allocator<int>::standard) {
106451c0b2f7Stbbdev // scalable allocator is not available.
106551c0b2f7Stbbdev return 1;
106651c0b2f7Stbbdev }
106751c0b2f7Stbbdev else {
106851c0b2f7Stbbdev // this value is for large objects, but will be correct for small.
106951c0b2f7Stbbdev return 64; // TBB_REVAMP_TODO: enable as estimatedCacheLineSize when tbbmalloc is available;
107051c0b2f7Stbbdev }
107151c0b2f7Stbbdev }
107251c0b2f7Stbbdev
107351c0b2f7Stbbdev // TODO: rework the test not to depend on oneTBB internals
107449e08aacStbbdev static const size_t cache_allocator_mask = oneapi::tbb::detail::r1::cache_line_size();
107551c0b2f7Stbbdev static const size_t tbb_allocator_mask = init_tbb_alloc_mask();
107651c0b2f7Stbbdev
TestETSIterator()107751c0b2f7Stbbdev void TestETSIterator() {
107849e08aacStbbdev using ets_type = oneapi::tbb::enumerable_thread_specific<int>;
107951c0b2f7Stbbdev if (utils::get_platform_max_threads() == 1) {
108051c0b2f7Stbbdev ets_type ets;
108151c0b2f7Stbbdev ets.local() = 1;
108251c0b2f7Stbbdev REQUIRE_MESSAGE(std::next(ets.begin()) == ets.end(), "Incorrect begin or end of the ETS");
108351c0b2f7Stbbdev REQUIRE_MESSAGE(std::prev(ets.end()) == ets.begin(), "Incorrect begin or end of the ETS");
108451c0b2f7Stbbdev } else {
108551c0b2f7Stbbdev std::atomic<std::size_t> sync_counter(0);
108651c0b2f7Stbbdev
108751c0b2f7Stbbdev const std::size_t expected_ets_size = 2;
108851c0b2f7Stbbdev ets_type ets;
108951c0b2f7Stbbdev const ets_type& cets(ets);
109051c0b2f7Stbbdev
109151c0b2f7Stbbdev auto fill_ets_body = [&](){
109251c0b2f7Stbbdev ets.local() = 42;
109351c0b2f7Stbbdev ++sync_counter;
109451c0b2f7Stbbdev while(sync_counter != expected_ets_size)
1095b15aabb3Stbbdev utils::yield();
109651c0b2f7Stbbdev };
109751c0b2f7Stbbdev
109849e08aacStbbdev oneapi::tbb::parallel_invoke(fill_ets_body, fill_ets_body);
109951c0b2f7Stbbdev REQUIRE_MESSAGE(ets.size() == expected_ets_size, "Incorrect ETS size");
110051c0b2f7Stbbdev
110151c0b2f7Stbbdev std::size_t counter = 0;
110251c0b2f7Stbbdev auto iter = ets.begin();
110351c0b2f7Stbbdev while(iter != ets.end()) {
110451c0b2f7Stbbdev ++counter % 2 == 0 ? ++iter : iter++;
110551c0b2f7Stbbdev }
110651c0b2f7Stbbdev REQUIRE(counter == expected_ets_size);
110751c0b2f7Stbbdev while(iter != ets.begin()) {
110851c0b2f7Stbbdev --counter % 2 == 0 ? --iter : iter--;
110951c0b2f7Stbbdev }
111051c0b2f7Stbbdev REQUIRE(counter == 0);
111151c0b2f7Stbbdev auto citer = cets.begin();
111251c0b2f7Stbbdev while(citer != cets.end()) {
111351c0b2f7Stbbdev ++counter % 2 == 0 ? ++citer : citer++;
111451c0b2f7Stbbdev }
111551c0b2f7Stbbdev REQUIRE(counter == expected_ets_size);
111651c0b2f7Stbbdev while(citer != cets.begin()) {
111751c0b2f7Stbbdev --counter % 2 == 0 ? --citer : citer--;
111851c0b2f7Stbbdev }
111951c0b2f7Stbbdev REQUIRE(counter == 0);
112051c0b2f7Stbbdev REQUIRE(ets.begin() + expected_ets_size == ets.end());
112151c0b2f7Stbbdev REQUIRE(expected_ets_size + ets.begin() == ets.end());
112251c0b2f7Stbbdev REQUIRE(ets.end() - expected_ets_size == ets.begin());
112351c0b2f7Stbbdev
112451c0b2f7Stbbdev typename ets_type::iterator it;
112551c0b2f7Stbbdev it = ets.begin();
112651c0b2f7Stbbdev
112751c0b2f7Stbbdev auto it_bkp = it;
112851c0b2f7Stbbdev auto it2 = it++;
112951c0b2f7Stbbdev REQUIRE(it2 == it_bkp);
113051c0b2f7Stbbdev
113151c0b2f7Stbbdev it = ets.begin();
113251c0b2f7Stbbdev it += expected_ets_size;
113351c0b2f7Stbbdev REQUIRE(it == ets.end());
113451c0b2f7Stbbdev it -= expected_ets_size;
113551c0b2f7Stbbdev REQUIRE(it == ets.begin());
113651c0b2f7Stbbdev
113751c0b2f7Stbbdev for (int i = 0; i < int(expected_ets_size - 1); ++i) {
113851c0b2f7Stbbdev REQUIRE(ets.begin()[i] == 42);
113951c0b2f7Stbbdev REQUIRE(std::prev(ets.end())[-i] == 42);
114051c0b2f7Stbbdev }
114151c0b2f7Stbbdev
114251c0b2f7Stbbdev auto iter1 = ets.begin();
114351c0b2f7Stbbdev auto iter2 = ets.end();
114451c0b2f7Stbbdev REQUIRE(iter1 < iter2);
114551c0b2f7Stbbdev REQUIRE(iter1 <= iter2);
114651c0b2f7Stbbdev REQUIRE(!(iter1 > iter2));
114751c0b2f7Stbbdev REQUIRE(!(iter1 >= iter2));
114851c0b2f7Stbbdev }
114951c0b2f7Stbbdev }
115051c0b2f7Stbbdev
1151b15aabb3Stbbdev template <bool ExpectEqual, bool ExpectLess, typename Iterator>
DoETSIteratorComparisons(const Iterator & lhs,const Iterator & rhs)1152b15aabb3Stbbdev void DoETSIteratorComparisons( const Iterator& lhs, const Iterator& rhs ) {
1153b15aabb3Stbbdev // TODO: replace with testEqualityAndLessComparisons after adding <=> operator for ETS iterator
1154b15aabb3Stbbdev using namespace comparisons_testing;
1155b15aabb3Stbbdev testEqualityComparisons<ExpectEqual>(lhs, rhs);
1156b15aabb3Stbbdev testTwoWayComparisons<ExpectEqual, ExpectLess>(lhs, rhs);
1157b15aabb3Stbbdev }
1158b15aabb3Stbbdev
1159b15aabb3Stbbdev template <typename Iterator, typename ETS>
TestETSIteratorComparisonsBasic(ETS & ets)1160b15aabb3Stbbdev void TestETSIteratorComparisonsBasic( ETS& ets ) {
1161b15aabb3Stbbdev REQUIRE_MESSAGE(!ets.empty(), "Incorrect test setup");
1162b15aabb3Stbbdev Iterator it1, it2;
1163b15aabb3Stbbdev DoETSIteratorComparisons</*ExpectEqual = */true, /*ExpectLess = */false>(it1, it2);
1164b15aabb3Stbbdev it1 = ets.begin();
1165b15aabb3Stbbdev it2 = ets.begin();
1166b15aabb3Stbbdev DoETSIteratorComparisons</*ExpectEqual = */true, /*ExpectLess = */false>(it1, it2);
1167b15aabb3Stbbdev it2 = std::prev(ets.end());
1168b15aabb3Stbbdev DoETSIteratorComparisons</*ExpectEqual = */false, /*ExpectLess = */true>(it1, it2);
1169b15aabb3Stbbdev }
1170b15aabb3Stbbdev
TestETSIteratorComparisons()1171b15aabb3Stbbdev void TestETSIteratorComparisons() {
1172b15aabb3Stbbdev using ets_type = oneapi::tbb::enumerable_thread_specific<int>;
1173b15aabb3Stbbdev ets_type ets;
1174b15aabb3Stbbdev
1175b15aabb3Stbbdev // Fill the ets
1176b15aabb3Stbbdev const std::size_t expected_ets_size = 2;
1177b15aabb3Stbbdev std::atomic<std::size_t> sync_counter(0);
11788b6f831cStbbdev auto fill_ets_body = [&](int){
1179b15aabb3Stbbdev ets.local() = 42;
1180b15aabb3Stbbdev ++sync_counter;
1181b15aabb3Stbbdev while(sync_counter != expected_ets_size)
1182b15aabb3Stbbdev std::this_thread::yield();
1183b15aabb3Stbbdev };
1184b15aabb3Stbbdev
11858b6f831cStbbdev utils::NativeParallelFor(2, fill_ets_body);
1186b15aabb3Stbbdev
1187b15aabb3Stbbdev TestETSIteratorComparisonsBasic<typename ets_type::iterator>(ets);
1188b15aabb3Stbbdev const ets_type& cets = ets;
1189b15aabb3Stbbdev TestETSIteratorComparisonsBasic<typename ets_type::const_iterator>(cets);
1190b15aabb3Stbbdev }
1191b15aabb3Stbbdev
119251c0b2f7Stbbdev //! Test container instantiation
119351c0b2f7Stbbdev //! \brief \ref interface \ref requirement
119451c0b2f7Stbbdev TEST_CASE("Instantiation") {
119551c0b2f7Stbbdev AlignMask = cache_allocator_mask;
119649e08aacStbbdev TestInstantiation<oneapi::tbb::cache_aligned_allocator>("oneapi::tbb::cache_aligned_allocator");
119751c0b2f7Stbbdev AlignMask = tbb_allocator_mask;
119849e08aacStbbdev TestInstantiation<oneapi::tbb::tbb_allocator>("oneapi::tbb::tbb_allocator");
119951c0b2f7Stbbdev }
120051c0b2f7Stbbdev
120151c0b2f7Stbbdev //! Test assignment and copy constructor
120251c0b2f7Stbbdev //! \brief \ref interface \ref requirement
120351c0b2f7Stbbdev TEST_CASE("Assignment and copy constructor") {
120451c0b2f7Stbbdev AlignMask = cache_allocator_mask;
120549e08aacStbbdev run_assignment_and_copy_constructor_tests<oneapi::tbb::cache_aligned_allocator>("oneapi::tbb::cache_aligned_allocator");
120651c0b2f7Stbbdev AlignMask = tbb_allocator_mask;
120749e08aacStbbdev run_assignment_and_copy_constructor_tests<oneapi::tbb::tbb_allocator>("oneapi::tbb::tbb_allocator");
120851c0b2f7Stbbdev }
120951c0b2f7Stbbdev
121051c0b2f7Stbbdev //! Test for basic ETS functionality and requirements
121151c0b2f7Stbbdev //! \brief \ref interface \ref requirement
121251c0b2f7Stbbdev TEST_CASE("Basic ETS functionality") {
121351c0b2f7Stbbdev const int LOCALS = 10;
121451c0b2f7Stbbdev
121549e08aacStbbdev oneapi::tbb::enumerable_thread_specific<int> ets;
121651c0b2f7Stbbdev ets.local() = 42;
121751c0b2f7Stbbdev
121851c0b2f7Stbbdev utils::SpinBarrier barrier(LOCALS);
__anonba7ca1840402(int i) 121951c0b2f7Stbbdev utils::NativeParallelFor(LOCALS, [&](int i) {
122051c0b2f7Stbbdev barrier.wait();
122151c0b2f7Stbbdev ets.local() = i;
122251c0b2f7Stbbdev CHECK(ets.local() == i);
122351c0b2f7Stbbdev });
122451c0b2f7Stbbdev CHECK(ets.local() == 42);
122551c0b2f7Stbbdev
122651c0b2f7Stbbdev int ref_combined{0};
122751c0b2f7Stbbdev std::vector<int> sequence(LOCALS);
122851c0b2f7Stbbdev std::iota(sequence.begin(), sequence.end(), 0);
122951c0b2f7Stbbdev for (int i : sequence) {
123051c0b2f7Stbbdev ref_combined += i;
123151c0b2f7Stbbdev }
123251c0b2f7Stbbdev ref_combined += 42;
__anonba7ca1840502(int x, int y) 123351c0b2f7Stbbdev int ets_combined = ets.combine([](int x, int y) {
123451c0b2f7Stbbdev return x + y;
123551c0b2f7Stbbdev });
123651c0b2f7Stbbdev CHECK(ref_combined == ets_combined);
123751c0b2f7Stbbdev }
123851c0b2f7Stbbdev
123951c0b2f7Stbbdev //! Test ETS usage in parallel algorithms.
124051c0b2f7Stbbdev //! Also tests flattened2d and flattend2d
124151c0b2f7Stbbdev //! \brief \ref interface \ref requirement \ref stress
124251c0b2f7Stbbdev TEST_CASE("Parallel test") {
124351c0b2f7Stbbdev run_reference_check();
124451c0b2f7Stbbdev AlignMask = cache_allocator_mask;
124549e08aacStbbdev run_parallel_tests<oneapi::tbb::cache_aligned_allocator>("oneapi::tbb::cache_aligned_allocator");
124651c0b2f7Stbbdev AlignMask = tbb_allocator_mask;
124749e08aacStbbdev run_parallel_tests<oneapi::tbb::tbb_allocator>("oneapi::tbb::tbb_allocator");
124851c0b2f7Stbbdev run_cross_type_tests();
124951c0b2f7Stbbdev }
125051c0b2f7Stbbdev
125151c0b2f7Stbbdev //! \brief \ref interface \ref requirement
125251c0b2f7Stbbdev TEST_CASE("Member types") {
125351c0b2f7Stbbdev TestMemberTypes();
125451c0b2f7Stbbdev }
125551c0b2f7Stbbdev
125651c0b2f7Stbbdev //! \brief \ref interface \ref requirement
125751c0b2f7Stbbdev TEST_CASE("enumerable_thread_specific iterator") {
125851c0b2f7Stbbdev TestETSIterator();
125951c0b2f7Stbbdev }
126051c0b2f7Stbbdev
1261b15aabb3Stbbdev //! \brief \ref interface \ref requirement
1262b15aabb3Stbbdev TEST_CASE("enumerable_thread_specific iterator comparisons") {
1263b15aabb3Stbbdev TestETSIteratorComparisons();
1264b15aabb3Stbbdev }
1265