3 #include "cppunit/thread.h"
4 #include "set2/set_types.h"
5 #include <algorithm> // random_shuffle
9 # define TEST_SET(X) void X() { test<SetTypes<key_type, value_type>::X >() ; }
10 # define TEST_SET_EXTRACT(X) void X() { test_extract<SetTypes<key_type, value_type>::X >() ; }
11 # define TEST_SET_NOLF(X) void X() { test_nolf<SetTypes<key_type, value_type>::X >() ; }
12 # define TEST_SET_NOLF_EXTRACT(X) void X() { test_nolf_extract<SetTypes<key_type, value_type>::X >() ; }
15 static size_t c_nSetSize = 1000000 ; // max set size
16 static size_t c_nInsThreadCount = 4 ; // insert thread count
17 static size_t c_nDelThreadCount = 4 ; // delete thread count
18 static size_t c_nExtractThreadCount = 4 ; // extract thread count
19 static size_t c_nMaxLoadFactor = 8 ; // maximum load factor
20 static bool c_bPrintGCState = true;
29 key_thread( size_t key, size_t threadNo )
38 typedef SetTypes<key_thread, size_t>::key_val key_value_pair;
42 struct cmp<key_thread> {
43 int operator ()(key_thread const& k1, key_thread const& k2) const
45 if ( k1.nKey < k2.nKey )
47 if ( k1.nKey > k2.nKey )
49 if ( k1.nThread < k2.nThread )
51 if ( k1.nThread > k2.nThread )
55 int operator ()(key_thread const& k1, size_t k2) const
63 int operator ()(size_t k1, key_thread const& k2) const
77 struct less<set2::key_thread>
79 bool operator()(set2::key_thread const& k1, set2::key_thread const& k2) const
81 if ( k1.nKey <= k2.nKey )
82 return k1.nKey < k2.nKey || k1.nThread < k2.nThread;
88 CDS_BEGIN_STD_HASH_NAMESPACE
90 struct hash<set2::key_thread>
92 typedef size_t result_type;
93 typedef set2::key_thread argument_type;
95 size_t operator()(set2::key_thread const& k) const
97 return CDS_STD_HASH_NAMESPACE::hash<size_t>()( k.nKey );
99 size_t operator()(size_t k) const
101 return CDS_STD_HASH_NAMESPACE::hash<size_t>()( k );
104 CDS_END_STD_HASH_NAMESPACE
107 inline size_t hash_value( set2::key_thread const& k )
109 return CDS_STD_HASH_NAMESPACE::hash<size_t>()( k.nKey );
113 struct hash<set2::key_thread>
115 typedef size_t result_type;
116 typedef set2::key_thread argument_type;
118 size_t operator()(set2::key_thread const& k) const
120 return boost::hash<size_t>()( k.nKey );
122 size_t operator()(size_t k) const
124 return boost::hash<size_t>()( k );
132 template <typename Set>
133 static inline void check_before_clear( Set& s )
136 template <typename GC, typename Key, typename T, typename Traits>
137 static inline void check_before_clear( cds::container::EllenBinTreeSet<GC, Key, T, Traits>& s )
139 CPPUNIT_CHECK_CURRENT( s.check_consistency() );
142 class Set_DelOdd: public CppUnitMini::TestCase
144 std::vector<size_t> m_arrData;
147 typedef key_thread key_type;
148 typedef size_t value_type;
150 atomics::atomic<size_t> m_nInsThreadCount;
152 // Inserts keys from [0..N)
154 class InsertThread: public CppUnitMini::TestThread
158 virtual InsertThread * clone()
160 return new InsertThread( *this );
165 template <typename Q>
166 void operator()( bool bNew, key_value_pair const&, Q const& )
170 size_t m_nInsertSuccess;
171 size_t m_nInsertFailed;
174 InsertThread( CppUnitMini::ThreadPool& pool, Set& rMap )
175 : CppUnitMini::TestThread( pool )
178 InsertThread( InsertThread& src )
179 : CppUnitMini::TestThread( src )
183 Set_DelOdd& getTest()
185 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
188 virtual void init() { cds::threading::Manager::attachThread() ; }
189 virtual void fini() { cds::threading::Manager::detachThread() ; }
198 std::vector<size_t>& arrData = getTest().m_arrData;
199 for ( size_t i = 0; i < arrData.size(); ++i ) {
200 if ( rSet.insert( key_type( arrData[i], m_nThreadNo )))
207 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
208 if ( arrData[i] & 1 ) {
209 rSet.ensure( key_type( arrData[i], m_nThreadNo ), f );
213 getTest().m_nInsThreadCount.fetch_sub( 1, atomics::memory_order_release );
218 bool operator()( key_type const& k1, key_type const& k2 ) const
220 return k1.nKey == k2.nKey;
222 bool operator()( size_t k1, key_type const& k2 ) const
224 return k1 == k2.nKey;
226 bool operator()( key_type const& k1, size_t k2 ) const
228 return k1.nKey == k2;
230 bool operator ()( key_value_pair const& k1, key_value_pair const& k2 ) const
232 return operator()( k1.key, k2.key );
234 bool operator ()( key_value_pair const& k1, key_type const& k2 ) const
236 return operator()( k1.key, k2 );
238 bool operator ()( key_type const& k1, key_value_pair const& k2 ) const
240 return operator()( k1, k2.key );
242 bool operator ()( key_value_pair const& k1, size_t k2 ) const
244 return operator()( k1.key, k2 );
246 bool operator ()( size_t k1, key_value_pair const& k2 ) const
248 return operator()( k1, k2.key );
253 bool operator()( key_type const& k1, key_type const& k2 ) const
255 return k1.nKey < k2.nKey;
257 bool operator()( size_t k1, key_type const& k2 ) const
261 bool operator()( key_type const& k1, size_t k2 ) const
265 bool operator ()( key_value_pair const& k1, key_value_pair const& k2 ) const
267 return operator()( k1.key, k2.key );
269 bool operator ()( key_value_pair const& k1, key_type const& k2 ) const
271 return operator()( k1.key, k2 );
273 bool operator ()( key_type const& k1, key_value_pair const& k2 ) const
275 return operator()( k1, k2.key );
277 bool operator ()( key_value_pair const& k1, size_t k2 ) const
279 return operator()( k1.key, k2 );
281 bool operator ()( size_t k1, key_value_pair const& k2 ) const
283 return operator()( k1, k2.key );
286 typedef key_equal equal_to;
289 // Deletes odd keys from [0..N)
291 class DeleteThread: public CppUnitMini::TestThread
295 virtual DeleteThread * clone()
297 return new DeleteThread( *this );
300 size_t m_nDeleteSuccess;
301 size_t m_nDeleteFailed;
304 DeleteThread( CppUnitMini::ThreadPool& pool, Set& rMap )
305 : CppUnitMini::TestThread( pool )
308 DeleteThread( DeleteThread& src )
309 : CppUnitMini::TestThread( src )
313 Set_DelOdd& getTest()
315 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
318 virtual void init() { cds::threading::Manager::attachThread() ; }
319 virtual void fini() { cds::threading::Manager::detachThread() ; }
328 std::vector<size_t>& arrData = getTest().m_arrData;
329 if ( m_nThreadNo & 1 ) {
330 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
331 for ( size_t i = 0; i < arrData.size(); ++i ) {
332 if ( arrData[i] & 1 ) {
333 if ( rSet.erase_with( arrData[i], key_less() ))
339 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
344 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
345 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
346 if ( arrData[i] & 1 ) {
347 if ( rSet.erase_with( arrData[i], key_less() ))
353 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
360 // Extracts odd keys from [0..N)
361 template <typename GC, class Set>
362 class ExtractThread: public CppUnitMini::TestThread
366 virtual ExtractThread * clone()
368 return new ExtractThread( *this );
371 size_t m_nExtractSuccess;
372 size_t m_nExtractFailed;
375 ExtractThread( CppUnitMini::ThreadPool& pool, Set& rMap )
376 : CppUnitMini::TestThread( pool )
379 ExtractThread( ExtractThread& src )
380 : CppUnitMini::TestThread( src )
384 Set_DelOdd& getTest()
386 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
389 virtual void init() { cds::threading::Manager::attachThread() ; }
390 virtual void fini() { cds::threading::Manager::detachThread() ; }
397 m_nExtractFailed = 0;
399 typename Set::guarded_ptr gp;
401 std::vector<size_t>& arrData = getTest().m_arrData;
402 if ( m_nThreadNo & 1 ) {
403 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
404 for ( size_t i = 0; i < arrData.size(); ++i ) {
405 if ( arrData[i] & 1 ) {
406 if ( rSet.extract_with( gp, arrData[i], key_less() ))
412 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
417 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
418 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
419 if ( arrData[i] & 1 ) {
420 if ( rSet.extract_with( gp, arrData[i], key_less() ))
426 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
433 template <typename RCU, class Set>
434 class ExtractThread< cds::urcu::gc<RCU>, Set >: public CppUnitMini::TestThread
438 virtual ExtractThread * clone()
440 return new ExtractThread( *this );
443 size_t m_nExtractSuccess;
444 size_t m_nExtractFailed;
447 ExtractThread( CppUnitMini::ThreadPool& pool, Set& rMap )
448 : CppUnitMini::TestThread( pool )
451 ExtractThread( ExtractThread& src )
452 : CppUnitMini::TestThread( src )
456 Set_DelOdd& getTest()
458 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
461 virtual void init() { cds::threading::Manager::attachThread() ; }
462 virtual void fini() { cds::threading::Manager::detachThread() ; }
469 m_nExtractFailed = 0;
471 typename Set::exempt_ptr xp;
473 std::vector<size_t>& arrData = getTest().m_arrData;
474 if ( m_nThreadNo & 1 ) {
475 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
476 for ( size_t i = 0; i < arrData.size(); ++i ) {
477 if ( arrData[i] & 1 ) {
478 if ( Set::c_bExtractLockExternal ) {
479 typename Set::rcu_lock l;
480 if ( rSet.extract_with( xp, arrData[i], key_less() ))
486 if ( rSet.extract_with( xp, arrData[i], key_less() ))
494 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
499 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
500 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
501 if ( arrData[i] & 1 ) {
502 if ( Set::c_bExtractLockExternal ) {
503 typename Set::rcu_lock l;
504 if ( rSet.extract_with( xp, arrData[i], key_less() ))
510 if ( rSet.extract_with( xp, arrData[i], key_less() ))
518 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
527 void do_test( size_t nLoadFactor )
529 Set testSet( c_nSetSize, nLoadFactor );
530 do_test_with( testSet );
535 void do_test_extract( size_t nLoadFactor )
537 Set testSet( c_nSetSize, nLoadFactor );
538 do_test_extract_with( testSet );
543 void do_test_with( Set& testSet )
545 typedef InsertThread<Set> insert_thread;
546 typedef DeleteThread<Set> delete_thread;
548 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
550 CppUnitMini::ThreadPool pool( *this );
551 pool.add( new insert_thread( pool, testSet ), c_nInsThreadCount );
552 pool.add( new delete_thread( pool, testSet ), c_nDelThreadCount ? c_nDelThreadCount : cds::OS::topology::processor_count());
554 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
556 size_t nInsertSuccess = 0;
557 size_t nInsertFailed = 0;
558 size_t nDeleteSuccess = 0;
559 size_t nDeleteFailed = 0;
560 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
561 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
563 nInsertSuccess += pThread->m_nInsertSuccess;
564 nInsertFailed += pThread->m_nInsertFailed;
567 delete_thread * p = static_cast<delete_thread *>( *it );
568 nDeleteSuccess += p->m_nDeleteSuccess;
569 nDeleteFailed += p->m_nDeleteFailed;
573 CPPUNIT_CHECK( nInsertSuccess == c_nSetSize * c_nInsThreadCount );
574 CPPUNIT_CHECK( nInsertFailed == 0 );
576 CPPUNIT_MSG( " Totals (success/failed): \n\t"
577 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
578 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
583 void do_test_extract_with( Set& testSet )
585 typedef InsertThread<Set> insert_thread;
586 typedef DeleteThread<Set> delete_thread;
587 typedef ExtractThread< typename Set::gc, Set > extract_thread;
589 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
591 CppUnitMini::ThreadPool pool( *this );
592 pool.add( new insert_thread( pool, testSet ), c_nInsThreadCount );
593 if ( c_nDelThreadCount )
594 pool.add( new delete_thread( pool, testSet ), c_nDelThreadCount );
595 if ( c_nExtractThreadCount )
596 pool.add( new extract_thread( pool, testSet ), c_nExtractThreadCount );
598 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
600 size_t nInsertSuccess = 0;
601 size_t nInsertFailed = 0;
602 size_t nDeleteSuccess = 0;
603 size_t nDeleteFailed = 0;
604 size_t nExtractSuccess = 0;
605 size_t nExtractFailed = 0;
606 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
607 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
609 nInsertSuccess += pThread->m_nInsertSuccess;
610 nInsertFailed += pThread->m_nInsertFailed;
613 delete_thread * p = dynamic_cast<delete_thread *>( *it );
615 nDeleteSuccess += p->m_nDeleteSuccess;
616 nDeleteFailed += p->m_nDeleteFailed;
619 extract_thread * pExt = dynamic_cast<extract_thread *>( *it );
621 nExtractSuccess += pExt->m_nExtractSuccess;
622 nExtractFailed += pExt->m_nExtractFailed;
627 CPPUNIT_CHECK( nInsertSuccess == c_nSetSize * c_nInsThreadCount );
628 CPPUNIT_CHECK( nInsertFailed == 0 );
630 CPPUNIT_MSG( " Totals (success/failed): \n\t"
631 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
632 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
633 << " Extract=" << nExtractSuccess << '/' << nExtractFailed << "\n\t"
637 template <typename Set>
638 void analyze( Set& testSet )
640 // All even keys must be in the set
642 CPPUNIT_MSG( " Check even keys..." );
643 size_t nErrorCount = 0;
644 for ( size_t n = 0; n < c_nSetSize; n +=2 ) {
645 for ( size_t i = 0; i < c_nInsThreadCount; ++i ) {
646 if ( !testSet.find( key_type(n, i) ) ) {
647 if ( ++nErrorCount < 10 ) {
648 CPPUNIT_MSG( "key " << n << "-" << i << " is not found!");
653 CPPUNIT_CHECK_EX( nErrorCount == 0, "Totals: " << nErrorCount << " keys is not found");
656 check_before_clear( testSet );
658 CPPUNIT_MSG( " Clear map (single-threaded)..." );
659 cds::OS::Timer timer;
661 CPPUNIT_MSG( " Duration=" << timer.duration() );
662 CPPUNIT_CHECK_EX( testSet.empty(), ((long long) testSet.size()) );
664 additional_check( testSet );
665 print_stat( testSet );
666 additional_cleanup( testSet );
672 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
673 << " delete thread count=" << c_nDelThreadCount
674 << " set size=" << c_nSetSize
677 for ( size_t nLoadFactor = 1; nLoadFactor <= c_nMaxLoadFactor; nLoadFactor *= 2 ) {
678 CPPUNIT_MSG( "Load factor=" << nLoadFactor );
679 do_test<Set>( nLoadFactor );
680 if ( c_bPrintGCState )
688 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
689 << " delete thread count=" << c_nDelThreadCount
690 << " extract thread count=" << c_nExtractThreadCount
691 << " set size=" << c_nSetSize
694 for ( size_t nLoadFactor = 1; nLoadFactor <= c_nMaxLoadFactor; nLoadFactor *= 2 ) {
695 CPPUNIT_MSG( "Load factor=" << nLoadFactor );
696 do_test_extract<Set>( nLoadFactor );
697 if ( c_bPrintGCState )
705 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
706 << " delete thread count=" << c_nDelThreadCount
707 << " set size=" << c_nSetSize
716 if ( c_bPrintGCState )
721 void test_nolf_extract()
723 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
724 << " delete thread count=" << c_nDelThreadCount
725 << " extract thread count=" << c_nExtractThreadCount
726 << " set size=" << c_nSetSize
731 do_test_extract_with( s );
735 if ( c_bPrintGCState )
739 void setUpParams( const CppUnitMini::TestCfg& cfg ) {
740 c_nSetSize = cfg.getULong("MapSize", static_cast<unsigned long>(c_nSetSize) );
741 c_nInsThreadCount = cfg.getULong("InsThreadCount", static_cast<unsigned long>(c_nInsThreadCount) );
742 c_nDelThreadCount = cfg.getULong("DelThreadCount", static_cast<unsigned long>(c_nDelThreadCount) );
743 c_nExtractThreadCount = cfg.getULong("ExtractThreadCount", static_cast<unsigned long>(c_nExtractThreadCount) );
744 c_nMaxLoadFactor = cfg.getULong("MaxLoadFactor", static_cast<unsigned long>(c_nMaxLoadFactor) );
745 c_bPrintGCState = cfg.getBool("PrintGCStateFlag", true );
747 if ( c_nInsThreadCount == 0 )
748 c_nInsThreadCount = cds::OS::topology::processor_count();
749 if ( c_nDelThreadCount == 0 && c_nExtractThreadCount == 0 ) {
750 c_nExtractThreadCount = cds::OS::topology::processor_count() / 2;
751 c_nDelThreadCount = cds::OS::topology::processor_count() - c_nExtractThreadCount;
754 m_arrData.resize( c_nSetSize );
755 for ( size_t i = 0; i < c_nSetSize; ++i )
757 std::random_shuffle( m_arrData.begin(), m_arrData.end() );
760 # include "set2/set_defs.h"
761 CDSUNIT_DECLARE_MichaelSet
762 CDSUNIT_DECLARE_SplitList
763 //CDSUNIT_DECLARE_StripedSet
764 //CDSUNIT_DECLARE_RefinableSet
765 CDSUNIT_DECLARE_CuckooSet
766 CDSUNIT_DECLARE_SkipListSet
767 CDSUNIT_DECLARE_EllenBinTreeSet
768 //CDSUNIT_DECLARE_StdSet
770 CPPUNIT_TEST_SUITE_( Set_DelOdd, "Map_DelOdd" )
771 CDSUNIT_TEST_MichaelSet
772 CDSUNIT_TEST_SplitList
773 CDSUNIT_TEST_SkipListSet
774 CDSUNIT_TEST_EllenBinTreeSet
775 //CDSUNIT_TEST_StripedSet
776 //CDSUNIT_TEST_RefinableSet
777 CDSUNIT_TEST_CuckooSet
778 //CDSUNIT_TEST_StdSet
779 CPPUNIT_TEST_SUITE_END()
782 CPPUNIT_TEST_SUITE_REGISTRATION( Set_DelOdd );