| /* |
| Copyright 2005-2010 Intel Corporation. All Rights Reserved. |
| |
| This file is part of Threading Building Blocks. |
| |
| Threading Building Blocks is free software; you can redistribute it |
| and/or modify it under the terms of the GNU General Public License |
| version 2 as published by the Free Software Foundation. |
| |
| Threading Building Blocks is distributed in the hope that it will be |
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
| of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with Threading Building Blocks; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| |
| As a special exception, you may use this file as part of a free software |
| library without restriction. Specifically, if other files instantiate |
| templates or use macros or inline functions from this file, or you compile |
| this file and link it with other files to produce an executable, this |
| file does not by itself cause the resulting executable to be covered by |
| the GNU General Public License. This exception does not however |
| invalidate any other reasons why the executable file might be covered by |
| the GNU General Public License. |
| */ |
| |
| #include "tbb/enumerable_thread_specific.h" |
| #include "tbb/task_scheduler_init.h" |
| #include "tbb/parallel_for.h" |
| #include "tbb/parallel_reduce.h" |
| #include "tbb/blocked_range.h" |
| #include "tbb/tick_count.h" |
| #include "tbb/tbb_allocator.h" |
| #include "tbb/tbb_thread.h" |
| |
| #if !TBB_USE_EXCEPTIONS && _MSC_VER |
| // Suppress "C++ exception handler used, but unwind semantics are not enabled" warning in STL headers |
| #pragma warning (push) |
| #pragma warning (disable: 4530) |
| #endif |
| |
| #include <cstring> |
| #include <vector> |
| #include <deque> |
| #include <list> |
| #include <map> |
| #include <utility> |
| |
| #if !TBB_USE_EXCEPTIONS && _MSC_VER |
| #pragma warning (pop) |
| #endif |
| |
| #include "harness_assert.h" |
| #include "harness.h" |
| |
| #if __TBB_GCC_WARNING_SUPPRESSION_ENABLED |
| #pragma GCC diagnostic ignored "-Wuninitialized" |
| #endif |
| |
| static tbb::atomic<int> construction_counter; |
| static tbb::atomic<int> destruction_counter; |
| |
| const int REPETITIONS = 10; |
| const int N = 100000; |
| const int VALID_NUMBER_OF_KEYS = 100; |
| const double EXPECTED_SUM = (REPETITIONS + 1) * N; |
| |
| //! A minimal class that occupies N bytes. |
| /** Defines default and copy constructor, and allows implicit operator&. |
| Hides operator=. */ |
| template<size_t N=tbb::internal::NFS_MaxLineSize> |
| class minimal: NoAssign { |
| private: |
| int my_value; |
| bool is_constructed; |
| char pad[N-sizeof(int) - sizeof(bool)]; |
| public: |
| minimal() : NoAssign(), my_value(0) { ++construction_counter; is_constructed = true; } |
| minimal( const minimal &m ) : NoAssign(), my_value(m.my_value) { ++construction_counter; is_constructed = true; } |
| ~minimal() { ++destruction_counter; ASSERT(is_constructed, NULL); is_constructed = false; } |
| void set_value( const int i ) { ASSERT(is_constructed, NULL); my_value = i; } |
| int value( ) const { ASSERT(is_constructed, NULL); return my_value; } |
| }; |
| |
| // |
| // A helper class that simplifies writing the tests since minimal does not |
| // define = or + operators. |
| // |
| |
| template< typename T > |
| struct test_helper { |
| static inline void init(T &e) { e = static_cast<T>(0); } |
| static inline void sum(T &e, const int addend ) { e += static_cast<T>(addend); } |
| static inline void sum(T &e, const double addend ) { e += static_cast<T>(addend); } |
| static inline void set(T &e, const int value ) { e = static_cast<T>(value); } |
| static inline double get(const T &e ) { return static_cast<double>(e); } |
| }; |
| |
| template<size_t N> |
| struct test_helper<minimal<N> > { |
| static inline void init(minimal<N> &sum) { sum.set_value( 0 ); } |
| static inline void sum(minimal<N> &sum, const int addend ) { sum.set_value( sum.value() + addend); } |
| static inline void sum(minimal<N> &sum, const double addend ) { sum.set_value( sum.value() + static_cast<int>(addend)); } |
| static inline void sum(minimal<N> &sum, const minimal<N> &addend ) { sum.set_value( sum.value() + addend.value()); } |
| static inline void set(minimal<N> &v, const int value ) { v.set_value( static_cast<int>(value) ); } |
| static inline double get(const minimal<N> &sum ) { return static_cast<double>(sum.value()); } |
| }; |
| |
| //// functors and routines for initialization and combine |
| |
| // Addition |
| |
| template <typename T> |
| struct FunctorAddCombineRef { |
| T operator()(const T& left, const T& right) const { |
| return left+right; |
| } |
| }; |
| |
| template <size_t N> |
| struct FunctorAddCombineRef<minimal<N> > { |
| minimal<N> operator()(const minimal<N>& left, const minimal<N>& right) const { |
| minimal<N> result; |
| result.set_value( left.value() + right.value() ); |
| return result; |
| } |
| }; |
| |
| template <typename T, int Value> |
| struct FunctorFinit { |
| T operator()() { return Value; } |
| }; |
| |
| template <size_t N, int Value> |
| struct FunctorFinit<minimal<N>,Value> { |
| minimal<N> operator()() { |
| minimal<N> result; |
| result.set_value( Value ); |
| return result; |
| } |
| }; |
| |
| template <typename T> |
| struct FunctorAddCombine { |
| T operator()(T left, T right ) const { |
| return FunctorAddCombineRef<T>()( left, right ); |
| } |
| }; |
| |
| template <typename T> |
| T my_combine_ref( const T &left, const T &right) { |
| return FunctorAddCombineRef<T>()( left, right ); |
| } |
| |
| template <typename T> |
| T my_combine( T left, T right) { return my_combine_ref(left,right); } |
| |
| template <typename T> |
| class combine_one_helper { |
| public: |
| combine_one_helper(T& _result) : my_result(_result) {} |
| void operator()(const T& new_bit) { test_helper<T>::sum(my_result, new_bit); } |
| combine_one_helper& operator=(const combine_one_helper& other) { |
| test_helper<T>::set(my_result, test_helper<T>::get(other)); |
| return *this; |
| } |
| private: |
| T& my_result; |
| }; |
| |
| //// end functors and routines |
| |
| template< typename T > |
| void run_serial_scalar_tests(const char *test_name) { |
| tbb::tick_count t0; |
| T sum; |
| test_helper<T>::init(sum); |
| |
| REMARK("Testing serial %s... ", test_name); |
| for (int t = -1; t < REPETITIONS; ++t) { |
| if (Verbose && t == 0) t0 = tbb::tick_count::now(); |
| for (int i = 0; i < N; ++i) { |
| test_helper<T>::sum(sum,1); |
| } |
| } |
| |
| double result_value = test_helper<T>::get(sum); |
| ASSERT( EXPECTED_SUM == result_value, NULL); |
| REMARK("done\nserial %s, 0, %g, %g\n", test_name, result_value, ( tbb::tick_count::now() - t0).seconds()); |
| } |
| |
| |
| template <typename T> |
| class parallel_scalar_body: NoAssign { |
| |
| tbb::enumerable_thread_specific<T> &sums; |
| |
| public: |
| |
| parallel_scalar_body ( tbb::enumerable_thread_specific<T> &_sums ) : sums(_sums) { } |
| |
| void operator()( const tbb::blocked_range<int> &r ) const { |
| for (int i = r.begin(); i != r.end(); ++i) |
| test_helper<T>::sum( sums.local(), 1 ); |
| } |
| |
| }; |
| |
| template< typename T > |
| void run_parallel_scalar_tests_nocombine(const char *test_name) { |
| |
| typedef tbb::enumerable_thread_specific<T> ets_type; |
| |
| // We assume that static_sums zero-initialized or has a default constructor that zeros it. |
| static ets_type static_sums = ets_type( T() ); |
| |
| T exemplar; |
| test_helper<T>::init(exemplar); |
| T exemplar23; |
| test_helper<T>::set(exemplar23,23); |
| |
| for (int p = MinThread; p <= MaxThread; ++p) { |
| REMARK("Testing parallel %s on %d thread(s)... ", test_name, p); |
| tbb::task_scheduler_init init(p); |
| tbb::tick_count t0; |
| |
| T iterator_sum; |
| test_helper<T>::init(iterator_sum); |
| |
| T finit_ets_sum; |
| test_helper<T>::init(finit_ets_sum); |
| |
| T const_iterator_sum; |
| test_helper<T>::init(const_iterator_sum); |
| |
| T range_sum; |
| test_helper<T>::init(range_sum); |
| |
| T const_range_sum; |
| test_helper<T>::init(const_range_sum); |
| |
| T cconst_sum; |
| test_helper<T>::init(cconst_sum); |
| |
| T assign_sum; |
| test_helper<T>::init(assign_sum); |
| |
| T cassgn_sum; |
| test_helper<T>::init(cassgn_sum); |
| T non_cassgn_sum; |
| test_helper<T>::init(non_cassgn_sum); |
| |
| T static_sum; |
| test_helper<T>::init(static_sum); |
| |
| for (int t = -1; t < REPETITIONS; ++t) { |
| if (Verbose && t == 0) t0 = tbb::tick_count::now(); |
| |
| static_sums.clear(); |
| |
| ets_type sums(exemplar); |
| FunctorFinit<T,0> my_finit; |
| ets_type finit_ets(my_finit); |
| |
| ASSERT( sums.empty(), NULL); |
| tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( sums ) ); |
| ASSERT( !sums.empty(), NULL); |
| |
| ASSERT( finit_ets.empty(), NULL); |
| tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( finit_ets ) ); |
| ASSERT( !finit_ets.empty(), NULL); |
| |
| ASSERT(static_sums.empty(), NULL); |
| tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( static_sums ) ); |
| ASSERT( !static_sums.empty(), NULL); |
| |
| // use iterator |
| typename ets_type::size_type size = 0; |
| for ( typename ets_type::iterator i = sums.begin(); i != sums.end(); ++i ) { |
| ++size; |
| test_helper<T>::sum(iterator_sum, *i); |
| } |
| ASSERT( sums.size() == size, NULL); |
| |
| // use const_iterator |
| for ( typename ets_type::const_iterator i = sums.begin(); i != sums.end(); ++i ) { |
| test_helper<T>::sum(const_iterator_sum, *i); |
| } |
| |
| // use range_type |
| typename ets_type::range_type r = sums.range(); |
| for ( typename ets_type::range_type::const_iterator i = r.begin(); i != r.end(); ++i ) { |
| test_helper<T>::sum(range_sum, *i); |
| } |
| |
| // use const_range_type |
| typename ets_type::const_range_type cr = sums.range(); |
| for ( typename ets_type::const_range_type::iterator i = cr.begin(); i != cr.end(); ++i ) { |
| test_helper<T>::sum(const_range_sum, *i); |
| } |
| |
| // test copy constructor, with TLS-cached locals |
| typedef typename tbb::enumerable_thread_specific<T, tbb::cache_aligned_allocator<T>, tbb::ets_key_per_instance> cached_ets_type; |
| |
| cached_ets_type cconst(sums); |
| |
| for ( typename cached_ets_type::const_iterator i = cconst.begin(); i != cconst.end(); ++i ) { |
| test_helper<T>::sum(cconst_sum, *i); |
| } |
| |
| // test assignment |
| ets_type assigned; |
| assigned = sums; |
| |
| for ( typename ets_type::const_iterator i = assigned.begin(); i != assigned.end(); ++i ) { |
| test_helper<T>::sum(assign_sum, *i); |
| } |
| |
| // test assign to and from cached locals |
| cached_ets_type cassgn; |
| cassgn = sums; |
| for ( typename cached_ets_type::const_iterator i = cassgn.begin(); i != cassgn.end(); ++i ) { |
| test_helper<T>::sum(cassgn_sum, *i); |
| } |
| |
| ets_type non_cassgn; |
| non_cassgn = cassgn; |
| for ( typename ets_type::const_iterator i = non_cassgn.begin(); i != non_cassgn.end(); ++i ) { |
| test_helper<T>::sum(non_cassgn_sum, *i); |
| } |
| |
| // test finit-initialized ets |
| for(typename ets_type::const_iterator i = finit_ets.begin(); i != finit_ets.end(); ++i) { |
| test_helper<T>::sum(finit_ets_sum, *i); |
| } |
| |
| // test static ets |
| for(typename ets_type::const_iterator i = static_sums.begin(); i != static_sums.end(); ++i) { |
| test_helper<T>::sum(static_sum, *i); |
| } |
| |
| } |
| |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(iterator_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(const_iterator_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(range_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(const_range_sum), NULL); |
| |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(cconst_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(assign_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(cassgn_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(non_cassgn_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(finit_ets_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(static_sum), NULL); |
| |
| REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, test_helper<T>::get(iterator_sum), |
| ( tbb::tick_count::now() - t0).seconds()); |
| } |
| } |
| |
| template< typename T > |
| void run_parallel_scalar_tests(const char *test_name) { |
| |
| typedef tbb::enumerable_thread_specific<T> ets_type; |
| |
| // We assume that static_sums zero-initialized or has a default constructor that zeros it. |
| static ets_type static_sums = ets_type( T() ); |
| |
| T exemplar; |
| test_helper<T>::init(exemplar); |
| |
| run_parallel_scalar_tests_nocombine<T>(test_name); |
| |
| for (int p = MinThread; p <= MaxThread; ++p) { |
| REMARK("Testing parallel %s on %d thread(s)... ", test_name, p); |
| tbb::task_scheduler_init init(p); |
| tbb::tick_count t0; |
| |
| T combine_sum; |
| test_helper<T>::init(combine_sum); |
| |
| T combine_ref_sum; |
| test_helper<T>::init(combine_ref_sum); |
| |
| T combine_one_sum; |
| test_helper<T>::init(combine_one_sum); |
| |
| T static_sum; |
| test_helper<T>::init(static_sum); |
| |
| for (int t = -1; t < REPETITIONS; ++t) { |
| if (Verbose && t == 0) t0 = tbb::tick_count::now(); |
| |
| static_sums.clear(); |
| |
| ets_type sums(exemplar); |
| |
| ASSERT( sums.empty(), NULL); |
| tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( sums ) ); |
| ASSERT( !sums.empty(), NULL); |
| |
| ASSERT(static_sums.empty(), NULL); |
| tbb::parallel_for( tbb::blocked_range<int>( 0, N, 10000 ), parallel_scalar_body<T>( static_sums ) ); |
| ASSERT( !static_sums.empty(), NULL); |
| |
| |
| // Use combine |
| test_helper<T>::sum(combine_sum, sums.combine(my_combine<T>)); |
| test_helper<T>::sum(combine_ref_sum, sums.combine(my_combine_ref<T>)); |
| test_helper<T>::sum(static_sum, static_sums.combine(my_combine<T>)); |
| |
| combine_one_helper<T> my_helper(combine_one_sum); |
| sums.combine_each(my_helper); |
| } |
| |
| |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(combine_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(combine_ref_sum), NULL); |
| ASSERT( EXPECTED_SUM == test_helper<T>::get(static_sum), NULL); |
| |
| REMARK("done\nparallel combine %s, %d, %g, %g\n", test_name, p, test_helper<T>::get(combine_sum), |
| ( tbb::tick_count::now() - t0).seconds()); |
| } |
| } |
| |
| template <typename T> |
| class parallel_vector_for_body: NoAssign { |
| |
| tbb::enumerable_thread_specific< std::vector<T, tbb::tbb_allocator<T> > > &locals; |
| |
| public: |
| |
| parallel_vector_for_body ( tbb::enumerable_thread_specific< std::vector<T, tbb::tbb_allocator<T> > > &_locals ) : locals(_locals) { } |
| |
| void operator()( const tbb::blocked_range<int> &r ) const { |
| T one; |
| test_helper<T>::set(one, 1); |
| |
| for (int i = r.begin(); i < r.end(); ++i) { |
| locals.local().push_back( one ); |
| } |
| } |
| |
| }; |
| |
| template <typename R, typename T> |
| struct parallel_vector_reduce_body { |
| |
| T sum; |
| size_t count; |
| |
| parallel_vector_reduce_body ( ) : count(0) { test_helper<T>::init(sum); } |
| parallel_vector_reduce_body ( parallel_vector_reduce_body<R, T> &, tbb::split ) : count(0) { test_helper<T>::init(sum); } |
| |
| void operator()( const R &r ) { |
| for (typename R::iterator ri = r.begin(); ri != r.end(); ++ri) { |
| const std::vector< T, tbb::tbb_allocator<T> > &v = *ri; |
| ++count; |
| for (typename std::vector<T, tbb::tbb_allocator<T> >::const_iterator vi = v.begin(); vi != v.end(); ++vi) { |
| test_helper<T>::sum(sum, *vi); |
| } |
| } |
| } |
| |
| void join( const parallel_vector_reduce_body &b ) { |
| test_helper<T>::sum(sum,b.sum); |
| count += b.count; |
| } |
| |
| }; |
| |
| template< typename T > |
| void run_parallel_vector_tests(const char *test_name) { |
| tbb::tick_count t0; |
| typedef std::vector<T, tbb::tbb_allocator<T> > container_type; |
| |
| for (int p = MinThread; p <= MaxThread; ++p) { |
| REMARK("Testing parallel %s on %d thread(s)... ", test_name, p); |
| tbb::task_scheduler_init init(p); |
| |
| T sum; |
| test_helper<T>::init(sum); |
| |
| for (int t = -1; t < REPETITIONS; ++t) { |
| if (Verbose && t == 0) t0 = tbb::tick_count::now(); |
| typedef typename tbb::enumerable_thread_specific< container_type > ets_type; |
| ets_type vs; |
| |
| ASSERT( vs.empty(), NULL); |
| tbb::parallel_for ( tbb::blocked_range<int> (0, N, 10000), parallel_vector_for_body<T>( vs ) ); |
| ASSERT( !vs.empty(), NULL); |
| |
| // copy construct |
| ets_type vs2(vs); // this causes an assertion failure, related to allocators... |
| |
| // assign |
| ets_type vs3; |
| vs3 = vs; |
| |
| parallel_vector_reduce_body< typename tbb::enumerable_thread_specific< std::vector< T, tbb::tbb_allocator<T> > >::const_range_type, T > pvrb; |
| tbb::parallel_reduce ( vs.range(1), pvrb ); |
| |
| test_helper<T>::sum(sum, pvrb.sum); |
| |
| ASSERT( vs.size() == pvrb.count, NULL); |
| |
| tbb::flattened2d<ets_type> fvs = flatten2d(vs); |
| size_t ccount = fvs.size(); |
| size_t elem_cnt = 0; |
| for(typename tbb::flattened2d<ets_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) { |
| ++elem_cnt; |
| }; |
| ASSERT(ccount == elem_cnt, NULL); |
| |
| elem_cnt = 0; |
| for(typename tbb::flattened2d<ets_type>::iterator i = fvs.begin(); i != fvs.end(); ++i) { |
| ++elem_cnt; |
| }; |
| ASSERT(ccount == elem_cnt, NULL); |
| } |
| |
| double result_value = test_helper<T>::get(sum); |
| ASSERT( EXPECTED_SUM == result_value, NULL); |
| REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, result_value, ( tbb::tick_count::now() - t0).seconds()); |
| } |
| } |
| |
| template<typename T> |
| void run_cross_type_vector_tests(const char *test_name) { |
| tbb::tick_count t0; |
| typedef std::vector<T, tbb::tbb_allocator<T> > container_type; |
| |
| for (int p = MinThread; p <= MaxThread; ++p) { |
| REMARK("Testing parallel %s on %d thread(s)... ", test_name, p); |
| tbb::task_scheduler_init init(p); |
| |
| T sum; |
| test_helper<T>::init(sum); |
| |
| for (int t = -1; t < REPETITIONS; ++t) { |
| if (Verbose && t == 0) t0 = tbb::tick_count::now(); |
| typedef typename tbb::enumerable_thread_specific< container_type, tbb::cache_aligned_allocator<container_type>, tbb::ets_no_key > ets_nokey_type; |
| typedef typename tbb::enumerable_thread_specific< container_type, tbb::cache_aligned_allocator<container_type>, tbb::ets_key_per_instance > ets_tlskey_type; |
| ets_nokey_type vs; |
| |
| ASSERT( vs.empty(), NULL); |
| tbb::parallel_for ( tbb::blocked_range<int> (0, N, 10000), parallel_vector_for_body<T>( vs ) ); |
| ASSERT( !vs.empty(), NULL); |
| |
| // copy construct |
| ets_tlskey_type vs2(vs); |
| |
| // assign |
| ets_nokey_type vs3; |
| vs3 = vs2; |
| |
| parallel_vector_reduce_body< typename tbb::enumerable_thread_specific< std::vector< T, tbb::tbb_allocator<T> > >::const_range_type, T > pvrb; |
| tbb::parallel_reduce ( vs3.range(1), pvrb ); |
| |
| test_helper<T>::sum(sum, pvrb.sum); |
| |
| ASSERT( vs3.size() == pvrb.count, NULL); |
| |
| tbb::flattened2d<ets_nokey_type> fvs = flatten2d(vs3); |
| size_t ccount = fvs.size(); |
| size_t elem_cnt = 0; |
| for(typename tbb::flattened2d<ets_nokey_type>::const_iterator i = fvs.begin(); i != fvs.end(); ++i) { |
| ++elem_cnt; |
| }; |
| ASSERT(ccount == elem_cnt, NULL); |
| |
| elem_cnt = 0; |
| for(typename tbb::flattened2d<ets_nokey_type>::iterator i = fvs.begin(); i != fvs.end(); ++i) { |
| ++elem_cnt; |
| }; |
| ASSERT(ccount == elem_cnt, NULL); |
| } |
| |
| double result_value = test_helper<T>::get(sum); |
| ASSERT( EXPECTED_SUM == result_value, NULL); |
| REMARK("done\nparallel %s, %d, %g, %g\n", test_name, p, result_value, ( tbb::tick_count::now() - t0).seconds()); |
| } |
| } |
| |
| template< typename T > |
| void run_serial_vector_tests(const char *test_name) { |
| tbb::tick_count t0; |
| T sum; |
| test_helper<T>::init(sum); |
| T one; |
| test_helper<T>::set(one, 1); |
| |
| REMARK("Testing serial %s... ", test_name); |
| for (int t = -1; t < REPETITIONS; ++t) { |
| if (Verbose && t == 0) t0 = tbb::tick_count::now(); |
| std::vector<T, tbb::tbb_allocator<T> > v; |
| for (int i = 0; i < N; ++i) { |
| v.push_back( one ); |
| } |
| for (typename std::vector<T, tbb::tbb_allocator<T> >::const_iterator i = v.begin(); i != v.end(); ++i) |
| test_helper<T>::sum(sum, *i); |
| } |
| |
| double result_value = test_helper<T>::get(sum); |
| ASSERT( EXPECTED_SUM == result_value, NULL); |
| REMARK("done\nserial %s, 0, %g, %g\n", test_name, result_value, ( tbb::tick_count::now() - t0).seconds()); |
| } |
| |
| const size_t line_size = tbb::internal::NFS_MaxLineSize; |
| |
| void |
| run_serial_tests() { |
| run_serial_scalar_tests<int>("int"); |
| run_serial_scalar_tests<double>("double"); |
| run_serial_scalar_tests<minimal<> >("minimal<>"); |
| run_serial_vector_tests<int>("std::vector<int, tbb::tbb_allocator<int> >"); |
| run_serial_vector_tests<double>("std::vector<double, tbb::tbb_allocator<double> >"); |
| } |
| |
| void |
| run_parallel_tests() { |
| run_parallel_scalar_tests<int>("int"); |
| run_parallel_scalar_tests<double>("double"); |
| run_parallel_scalar_tests_nocombine<minimal<> >("minimal<>"); |
| run_parallel_vector_tests<int>("std::vector<int, tbb::tbb_allocator<int> >"); |
| run_parallel_vector_tests<double>("std::vector<double, tbb::tbb_allocator<double> >"); |
| } |
| |
| void |
| run_cross_type_tests() { |
| // cross-type scalar tests are part of run_serial_scalar_tests |
| run_cross_type_vector_tests<int>("std::vector<int, tbb::tbb_allocator<int> >"); |
| run_parallel_vector_tests<double>("std::vector<double, tbb::tbb_allocator<double> >"); |
| } |
| |
| typedef tbb::enumerable_thread_specific<minimal<line_size> > flogged_ets; |
| |
| class set_body { |
| flogged_ets *a; |
| |
| public: |
| set_body( flogged_ets*_a ) : a(_a) { } |
| |
| void operator() ( ) const { |
| for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) { |
| a[i].local().set_value(i + 1); |
| } |
| } |
| |
| }; |
| |
| void do_tbb_threads( int max_threads, flogged_ets a[] ) { |
| std::vector< tbb::tbb_thread * > threads; |
| |
| for (int p = 0; p < max_threads; ++p) { |
| threads.push_back( new tbb::tbb_thread ( set_body( a ) ) ); |
| } |
| |
| for (int p = 0; p < max_threads; ++p) { |
| threads[p]->join(); |
| } |
| |
| for(int p = 0; p < max_threads; ++p) { |
| delete threads[p]; |
| } |
| } |
| |
| void |
| flog_key_creation_and_deletion() { |
| const int FLOG_REPETITIONS = 100; |
| |
| for (int p = MinThread; p <= MaxThread; ++p) { |
| REMARK("Testing repeated deletes on %d threads... ", p); |
| |
| for (int j = 0; j < FLOG_REPETITIONS; ++j) { |
| construction_counter = 0; |
| destruction_counter = 0; |
| |
| // causes VALID_NUMER_OF_KEYS exemplar instances to be constructed |
| flogged_ets* a = new flogged_ets[VALID_NUMBER_OF_KEYS]; |
| ASSERT(int(construction_counter) == 0, NULL); // no exemplars or actual locals have been constructed |
| ASSERT(int(destruction_counter) == 0, NULL); // and none have been destroyed |
| |
| // causes p * VALID_NUMBER_OF_KEYS minimals to be created |
| do_tbb_threads(p, a); |
| |
| for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) { |
| int pcnt = 0; |
| for ( flogged_ets::iterator tli = a[i].begin(); tli != a[i].end(); ++tli ) { |
| ASSERT( (*tli).value() == i+1, NULL ); |
| ++pcnt; |
| } |
| ASSERT( pcnt == p, NULL); // should be one local per thread. |
| } |
| delete[] a; |
| } |
| |
| ASSERT( int(construction_counter) == (p)*VALID_NUMBER_OF_KEYS, NULL ); |
| ASSERT( int(destruction_counter) == (p)*VALID_NUMBER_OF_KEYS, NULL ); |
| |
| REMARK("done\nTesting repeated clears on %d threads... ", p); |
| |
| construction_counter = 0; |
| destruction_counter = 0; |
| |
| // causes VALID_NUMER_OF_KEYS exemplar instances to be constructed |
| flogged_ets* a = new flogged_ets[VALID_NUMBER_OF_KEYS]; |
| |
| for (int j = 0; j < FLOG_REPETITIONS; ++j) { |
| |
| // causes p * VALID_NUMBER_OF_KEYS minimals to be created |
| do_tbb_threads(p, a); |
| |
| for (int i = 0; i < VALID_NUMBER_OF_KEYS; ++i) { |
| for ( flogged_ets::iterator tli = a[i].begin(); tli != a[i].end(); ++tli ) { |
| ASSERT( (*tli).value() == i+1, NULL ); |
| } |
| a[i].clear(); |
| ASSERT( static_cast<int>(a[i].end() - a[i].begin()) == 0, NULL ); |
| } |
| |
| } |
| |
| delete[] a; |
| |
| ASSERT( int(construction_counter) == (FLOG_REPETITIONS*p)*VALID_NUMBER_OF_KEYS, NULL ); |
| ASSERT( int(destruction_counter) == (FLOG_REPETITIONS*p)*VALID_NUMBER_OF_KEYS, NULL ); |
| |
| REMARK("done\n"); |
| } |
| |
| } |
| |
| template <typename inner_container> |
| void |
| flog_segmented_interator() { |
| |
| bool found_error = false; |
| typedef typename inner_container::value_type T; |
| typedef std::vector< inner_container > nested_vec; |
| inner_container my_inner_container; |
| my_inner_container.clear(); |
| nested_vec my_vec; |
| |
| // simple nested vector (neither level empty) |
| const int maxval = 10; |
| for(int i=0; i < maxval; i++) { |
| my_vec.push_back(my_inner_container); |
| for(int j = 0; j < maxval; j++) { |
| my_vec.at(i).push_back((T)(maxval * i + j)); |
| } |
| } |
| |
| tbb::internal::segmented_iterator<nested_vec, T> my_si(my_vec); |
| |
| T ii; |
| for(my_si=my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) { |
| if((*my_si) != ii) { |
| found_error = true; |
| REMARK( "*my_si=%d\n", int(*my_si)); |
| } |
| } |
| |
| // outer level empty |
| my_vec.clear(); |
| for(my_si=my_vec.begin(); my_si != my_vec.end(); ++my_si) { |
| found_error = true; |
| } |
| |
| // inner levels empty |
| my_vec.clear(); |
| for(int i =0; i < maxval; ++i) { |
| my_vec.push_back(my_inner_container); |
| } |
| for(my_si = my_vec.begin(); my_si != my_vec.end(); ++my_si) { |
| found_error = true; |
| } |
| |
| // every other inner container is empty |
| my_vec.clear(); |
| for(int i=0; i < maxval; ++i) { |
| my_vec.push_back(my_inner_container); |
| if(i%2) { |
| for(int j = 0; j < maxval; ++j) { |
| my_vec.at(i).push_back((T)(maxval * (i/2) + j)); |
| } |
| } |
| } |
| for(my_si = my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) { |
| if((*my_si) != ii) { |
| found_error = true; |
| REMARK("*my_si=%d, ii=%d\n", (int)(*my_si), (int)ii); |
| } |
| } |
| |
| tbb::internal::segmented_iterator<nested_vec, const T> my_csi(my_vec); |
| for(my_csi=my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) { |
| if((*my_csi) != ii) { |
| found_error = true; |
| REMARK( "*my_csi=%d\n", int(*my_csi)); |
| } |
| } |
| |
| // outer level empty |
| my_vec.clear(); |
| for(my_csi=my_vec.begin(); my_csi != my_vec.end(); ++my_csi) { |
| found_error = true; |
| } |
| |
| // inner levels empty |
| my_vec.clear(); |
| for(int i =0; i < maxval; ++i) { |
| my_vec.push_back(my_inner_container); |
| } |
| for(my_csi = my_vec.begin(); my_csi != my_vec.end(); ++my_csi) { |
| found_error = true; |
| } |
| |
| // every other inner container is empty |
| my_vec.clear(); |
| for(int i=0; i < maxval; ++i) { |
| my_vec.push_back(my_inner_container); |
| if(i%2) { |
| for(int j = 0; j < maxval; ++j) { |
| my_vec.at(i).push_back((T)(maxval * (i/2) + j)); |
| } |
| } |
| } |
| for(my_csi = my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) { |
| if((*my_csi) != ii) { |
| found_error = true; |
| REMARK("*my_csi=%d, ii=%d\n", (int)(*my_csi), (int)ii); |
| } |
| } |
| |
| |
| if(found_error) REPORT("segmented_iterator failed\n"); |
| } |
| |
| template <typename Key, typename Val> |
| void |
| flog_segmented_iterator_map() { |
| typedef typename std::map<Key, Val> my_map; |
| typedef std::vector< my_map > nested_vec; |
| my_map my_inner_container; |
| my_inner_container.clear(); |
| nested_vec my_vec; |
| my_vec.clear(); |
| bool found_error = false; |
| |
| // simple nested vector (neither level empty) |
| const int maxval = 4; |
| for(int i=0; i < maxval; i++) { |
| my_vec.push_back(my_inner_container); |
| for(int j = 0; j < maxval; j++) { |
| my_vec.at(i).insert(std::make_pair<Key,Val>(maxval * i + j, 2*(maxval*i + j))); |
| } |
| } |
| |
| tbb::internal::segmented_iterator<nested_vec, std::pair<const Key, Val> > my_si(my_vec); |
| Key ii; |
| for(my_si=my_vec.begin(), ii=0; my_si != my_vec.end(); ++my_si, ++ii) { |
| if(((*my_si).first != ii) || ((*my_si).second != 2*ii)) { |
| found_error = true; |
| REMARK( "ii=%d, (*my_si).first=%d, second=%d\n",ii, int((*my_si).first), int((*my_si).second)); |
| } |
| } |
| |
| tbb::internal::segmented_iterator<nested_vec, const std::pair<const Key, Val> > my_csi(my_vec); |
| for(my_csi=my_vec.begin(), ii=0; my_csi != my_vec.end(); ++my_csi, ++ii) { |
| if(((*my_csi).first != ii) || ((*my_csi).second != 2*ii)) { |
| found_error = true; |
| REMARK( "ii=%d, (*my_csi).first=%d, second=%d\n",ii, int((*my_csi).first), int((*my_csi).second)); |
| } |
| } |
| } |
| |
| void |
| run_segmented_iterator_tests() { |
| // only the following containers can be used with the segmented iterator. |
| REMARK("Running Segmented Iterator Tests\n"); |
| flog_segmented_interator<std::vector< int > >(); |
| flog_segmented_interator<std::vector< double > >(); |
| flog_segmented_interator<std::deque< int > >(); |
| flog_segmented_interator<std::deque< double > >(); |
| flog_segmented_interator<std::list< int > >(); |
| flog_segmented_interator<std::list< double > >(); |
| |
| flog_segmented_iterator_map<int, int>(); |
| flog_segmented_iterator_map<int, double>(); |
| } |
| |
| template <typename T> |
| void |
| run_assign_and_copy_constructor_test(const char *test_name) { |
| REMARK("Testing assignment and copy construction for %s\n", test_name); |
| |
| // test initializer with exemplar |
| T initializer0; |
| test_helper<T>::init(initializer0); |
| T initializer7; |
| test_helper<T>::set(initializer7,7); |
| tbb::enumerable_thread_specific<T> create1(initializer7); |
| (void) create1.local(); // create an initialized value |
| ASSERT(7 == test_helper<T>::get(create1.local()), NULL); |
| |
| // test copy construction with exemplar initializer |
| create1.clear(); |
| tbb::enumerable_thread_specific<T> copy1(create1); |
| (void) copy1.local(); |
| ASSERT(7 == test_helper<T>::get(copy1.local()), NULL); |
| |
| // test copy assignment with exemplar initializer |
| create1.clear(); |
| tbb::enumerable_thread_specific<T> assign1(initializer0); |
| assign1 = create1; |
| (void) assign1.local(); |
| ASSERT(7 == test_helper<T>::get(assign1.local()), NULL); |
| |
| // test creation with finit function |
| FunctorFinit<T,7> my_finit7; |
| tbb::enumerable_thread_specific<T> create2(my_finit7); |
| (void) create2.local(); |
| ASSERT(7 == test_helper<T>::get(create2.local()), NULL); |
| |
| // test copy construction with function initializer |
| create2.clear(); |
| tbb::enumerable_thread_specific<T> copy2(create2); |
| (void) copy2.local(); |
| ASSERT(7 == test_helper<T>::get(copy2.local()), NULL); |
| |
| // test copy assignment with function initializer |
| create2.clear(); |
| FunctorFinit<T,0> my_finit; |
| tbb::enumerable_thread_specific<T> assign2(my_finit); |
| assign2 = create2; |
| (void) assign2.local(); |
| ASSERT(7 == test_helper<T>::get(assign2.local()), NULL); |
| } |
| |
| void |
| run_assignment_and_copy_constructor_tests() { |
| REMARK("Running assignment and copy constructor tests\n"); |
| run_assign_and_copy_constructor_test<int>("int"); |
| run_assign_and_copy_constructor_test<double>("double"); |
| // Try class sizes that are close to a cache line in size, in order to check padding calculations. |
| run_assign_and_copy_constructor_test<minimal<line_size-1> >("minimal<line_size-1>"); |
| run_assign_and_copy_constructor_test<minimal<line_size> >("minimal<line_size>"); |
| run_assign_and_copy_constructor_test<minimal<line_size+1> >("minimal<line_size+1>"); |
| } |
| |
| int TestMain () { |
| run_segmented_iterator_tests(); |
| flog_key_creation_and_deletion(); |
| |
| if (MinThread == 0) { |
| run_serial_tests(); |
| MinThread = 1; |
| } |
| if (MaxThread > 0) { |
| run_parallel_tests(); |
| run_cross_type_tests(); |
| } |
| |
| run_assignment_and_copy_constructor_tests(); |
| |
| return Harness::Done; |
| } |
| |