3 #include "cppunit/thread.h"
4 #include "set2/set_type.h"
8 #define TEST_CASE(TAG, X) void X();
16 key_thread( size_t key, size_t threadNo )
25 typedef set_type_base<key_thread, size_t>::key_val key_value_pair;
29 struct cmp<key_thread> {
30 int operator ()(key_thread const& k1, key_thread const& k2) const
32 if ( k1.nKey < k2.nKey )
34 if ( k1.nKey > k2.nKey )
36 if ( k1.nThread < k2.nThread )
38 if ( k1.nThread > k2.nThread )
42 int operator ()(key_thread const& k1, size_t k2) const
50 int operator ()(size_t k1, key_thread const& k2) const
64 struct less<set2::key_thread>
66 bool operator()(set2::key_thread const& k1, set2::key_thread const& k2) const
68 if ( k1.nKey <= k2.nKey )
69 return k1.nKey < k2.nKey || k1.nThread < k2.nThread;
75 struct hash<set2::key_thread>
77 typedef size_t result_type;
78 typedef set2::key_thread argument_type;
80 size_t operator()( set2::key_thread const& k ) const
82 return std::hash<size_t>()(k.nKey);
84 size_t operator()( size_t k ) const
86 return std::hash<size_t>()(k);
93 inline size_t hash_value( set2::key_thread const& k )
95 return std::hash<size_t>()( k.nKey );
99 struct hash<set2::key_thread>
101 typedef size_t result_type;
102 typedef set2::key_thread argument_type;
104 size_t operator()(set2::key_thread const& k) const
106 return boost::hash<size_t>()( k.nKey );
108 size_t operator()(size_t k) const
110 return boost::hash<size_t>()( k );
117 class Set_DelOdd: public CppUnitMini::TestCase
120 size_t c_nSetSize =1000000; // max set size
121 size_t c_nInsThreadCount = 4; // insert thread count
122 size_t c_nDelThreadCount = 4; // delete thread count
123 size_t c_nExtractThreadCount = 4; // extract thread count
124 size_t c_nMaxLoadFactor = 8; // maximum load factor
125 bool c_bPrintGCState = true;
127 size_t c_nCuckooInitialSize = 1024;// initial size for CuckooSet
128 size_t c_nCuckooProbesetSize = 16; // CuckooSet probeset size (only for list-based probeset)
129 size_t c_nCuckooProbesetThreshold = 0; // CUckooSet probeset threshold (0 - use default)
131 size_t c_nLoadFactor = 2;
132 std::vector<size_t> m_arrData;
135 typedef CppUnitMini::TestCase Base;
136 typedef key_thread key_type;
137 typedef size_t value_type;
139 atomics::atomic<size_t> m_nInsThreadCount;
141 // Inserts keys from [0..N)
143 class InsertThread: public CppUnitMini::TestThread
147 virtual InsertThread * clone()
149 return new InsertThread( *this );
152 struct update_functor
154 template <typename Q>
155 void operator()( bool /*bNew*/, key_value_pair const&, Q const& )
159 size_t m_nInsertSuccess;
160 size_t m_nInsertFailed;
163 InsertThread( CppUnitMini::ThreadPool& pool, Set& rMap )
164 : CppUnitMini::TestThread( pool )
167 InsertThread( InsertThread& src )
168 : CppUnitMini::TestThread( src )
172 Set_DelOdd& getTest()
174 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
177 virtual void init() { cds::threading::Manager::attachThread() ; }
178 virtual void fini() { cds::threading::Manager::detachThread() ; }
187 std::vector<size_t>& arrData = getTest().m_arrData;
188 for ( size_t i = 0; i < arrData.size(); ++i ) {
189 if ( rSet.insert( key_type( arrData[i], m_nThreadNo )))
196 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
197 if ( arrData[i] & 1 ) {
198 rSet.update( key_type( arrData[i], m_nThreadNo ), f, true );
202 getTest().m_nInsThreadCount.fetch_sub( 1, atomics::memory_order_release );
207 bool operator()( key_type const& k1, key_type const& k2 ) const
209 return k1.nKey == k2.nKey;
211 bool operator()( size_t k1, key_type const& k2 ) const
213 return k1 == k2.nKey;
215 bool operator()( key_type const& k1, size_t k2 ) const
217 return k1.nKey == k2;
219 bool operator ()( key_value_pair const& k1, key_value_pair const& k2 ) const
221 return operator()( k1.key, k2.key );
223 bool operator ()( key_value_pair const& k1, key_type const& k2 ) const
225 return operator()( k1.key, k2 );
227 bool operator ()( key_type const& k1, key_value_pair const& k2 ) const
229 return operator()( k1, k2.key );
231 bool operator ()( key_value_pair const& k1, size_t k2 ) const
233 return operator()( k1.key, k2 );
235 bool operator ()( size_t k1, key_value_pair const& k2 ) const
237 return operator()( k1, k2.key );
242 bool operator()( key_type const& k1, key_type const& k2 ) const
244 return k1.nKey < k2.nKey;
246 bool operator()( size_t k1, key_type const& k2 ) const
250 bool operator()( key_type const& k1, size_t k2 ) const
254 bool operator ()( key_value_pair const& k1, key_value_pair const& k2 ) const
256 return operator()( k1.key, k2.key );
258 bool operator ()( key_value_pair const& k1, key_type const& k2 ) const
260 return operator()( k1.key, k2 );
262 bool operator ()( key_type const& k1, key_value_pair const& k2 ) const
264 return operator()( k1, k2.key );
266 bool operator ()( key_value_pair const& k1, size_t k2 ) const
268 return operator()( k1.key, k2 );
270 bool operator ()( size_t k1, key_value_pair const& k2 ) const
272 return operator()( k1, k2.key );
275 typedef key_equal equal_to;
278 // Deletes odd keys from [0..N)
280 class DeleteThread: public CppUnitMini::TestThread
284 virtual DeleteThread * clone()
286 return new DeleteThread( *this );
289 size_t m_nDeleteSuccess;
290 size_t m_nDeleteFailed;
293 DeleteThread( CppUnitMini::ThreadPool& pool, Set& rMap )
294 : CppUnitMini::TestThread( pool )
297 DeleteThread( DeleteThread& src )
298 : CppUnitMini::TestThread( src )
302 Set_DelOdd& getTest()
304 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
307 virtual void init() { cds::threading::Manager::attachThread() ; }
308 virtual void fini() { cds::threading::Manager::detachThread() ; }
317 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
318 std::vector<size_t>& arrData = getTest().m_arrData;
320 if ( m_nThreadNo & 1 ) {
321 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
322 for ( size_t i = 0; i < arrData.size(); ++i ) {
323 if ( arrData[i] & 1 ) {
324 if ( rSet.erase_with( arrData[i], key_less() ))
330 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
335 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
336 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
337 if ( arrData[i] & 1 ) {
338 if ( rSet.erase_with( arrData[i], key_less() ))
344 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
351 // Extracts odd keys from [0..N)
352 template <typename GC, class Set>
353 class ExtractThread: public CppUnitMini::TestThread
357 virtual ExtractThread * clone()
359 return new ExtractThread( *this );
362 size_t m_nExtractSuccess;
363 size_t m_nExtractFailed;
366 ExtractThread( CppUnitMini::ThreadPool& pool, Set& rMap )
367 : CppUnitMini::TestThread( pool )
370 ExtractThread( ExtractThread& src )
371 : CppUnitMini::TestThread( src )
375 Set_DelOdd& getTest()
377 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
380 virtual void init() { cds::threading::Manager::attachThread() ; }
381 virtual void fini() { cds::threading::Manager::detachThread() ; }
388 m_nExtractFailed = 0;
390 typename Set::guarded_ptr gp;
392 std::vector<size_t>& arrData = getTest().m_arrData;
393 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
395 if ( m_nThreadNo & 1 ) {
396 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
397 for ( size_t i = 0; i < arrData.size(); ++i ) {
398 if ( arrData[i] & 1 ) {
399 gp = rSet.extract_with( arrData[i], key_less());
407 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
412 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
413 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
414 if ( arrData[i] & 1 ) {
415 gp = rSet.extract_with( arrData[i], key_less());
423 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
430 template <typename RCU, class Set>
431 class ExtractThread< cds::urcu::gc<RCU>, Set >: public CppUnitMini::TestThread
435 virtual ExtractThread * clone()
437 return new ExtractThread( *this );
440 size_t m_nExtractSuccess;
441 size_t m_nExtractFailed;
444 ExtractThread( CppUnitMini::ThreadPool& pool, Set& rMap )
445 : CppUnitMini::TestThread( pool )
448 ExtractThread( ExtractThread& src )
449 : CppUnitMini::TestThread( src )
453 Set_DelOdd& getTest()
455 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
458 virtual void init() { cds::threading::Manager::attachThread() ; }
459 virtual void fini() { cds::threading::Manager::detachThread() ; }
466 m_nExtractFailed = 0;
468 typename Set::exempt_ptr xp;
470 std::vector<size_t>& arrData = getTest().m_arrData;
471 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
473 if ( m_nThreadNo & 1 ) {
474 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
475 for ( size_t i = 0; i < arrData.size(); ++i ) {
476 if ( arrData[i] & 1 ) {
477 if ( Set::c_bExtractLockExternal ) {
478 typename Set::rcu_lock l;
479 xp = rSet.extract_with( arrData[i], key_less() );
486 xp = rSet.extract_with( arrData[i], key_less() );
495 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
500 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
501 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
502 if ( arrData[i] & 1 ) {
503 if ( Set::c_bExtractLockExternal ) {
504 typename Set::rcu_lock l;
505 xp = rSet.extract_with( arrData[i], key_less() );
512 xp = rSet.extract_with( arrData[i], key_less() );
521 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
530 void do_test( size_t nLoadFactor )
532 Set testSet( c_nSetSize, nLoadFactor );
533 do_test_with( testSet );
538 void do_test_extract( size_t nLoadFactor )
540 Set testSet( c_nSetSize, nLoadFactor );
541 do_test_extract_with( testSet );
546 void do_test_with( Set& testSet )
548 typedef InsertThread<Set> insert_thread;
549 typedef DeleteThread<Set> delete_thread;
551 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
553 CppUnitMini::ThreadPool pool( *this );
554 pool.add( new insert_thread( pool, testSet ), c_nInsThreadCount );
555 pool.add( new delete_thread( pool, testSet ), c_nDelThreadCount ? c_nDelThreadCount : cds::OS::topology::processor_count());
557 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
559 size_t nInsertSuccess = 0;
560 size_t nInsertFailed = 0;
561 size_t nDeleteSuccess = 0;
562 size_t nDeleteFailed = 0;
563 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
564 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
566 nInsertSuccess += pThread->m_nInsertSuccess;
567 nInsertFailed += pThread->m_nInsertFailed;
570 delete_thread * p = static_cast<delete_thread *>( *it );
571 nDeleteSuccess += p->m_nDeleteSuccess;
572 nDeleteFailed += p->m_nDeleteFailed;
576 CPPUNIT_CHECK( nInsertSuccess == c_nSetSize * c_nInsThreadCount );
577 CPPUNIT_CHECK( nInsertFailed == 0 );
579 CPPUNIT_MSG( " Totals (success/failed): \n\t"
580 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
581 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
586 void do_test_extract_with( Set& testSet )
588 typedef InsertThread<Set> insert_thread;
589 typedef DeleteThread<Set> delete_thread;
590 typedef ExtractThread< typename Set::gc, Set > extract_thread;
592 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
594 CppUnitMini::ThreadPool pool( *this );
595 pool.add( new insert_thread( pool, testSet ), c_nInsThreadCount );
596 if ( c_nDelThreadCount )
597 pool.add( new delete_thread( pool, testSet ), c_nDelThreadCount );
598 if ( c_nExtractThreadCount )
599 pool.add( new extract_thread( pool, testSet ), c_nExtractThreadCount );
601 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
603 size_t nInsertSuccess = 0;
604 size_t nInsertFailed = 0;
605 size_t nDeleteSuccess = 0;
606 size_t nDeleteFailed = 0;
607 size_t nExtractSuccess = 0;
608 size_t nExtractFailed = 0;
609 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
610 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
612 nInsertSuccess += pThread->m_nInsertSuccess;
613 nInsertFailed += pThread->m_nInsertFailed;
616 delete_thread * p = dynamic_cast<delete_thread *>( *it );
618 nDeleteSuccess += p->m_nDeleteSuccess;
619 nDeleteFailed += p->m_nDeleteFailed;
622 extract_thread * pExt = dynamic_cast<extract_thread *>( *it );
624 nExtractSuccess += pExt->m_nExtractSuccess;
625 nExtractFailed += pExt->m_nExtractFailed;
630 CPPUNIT_CHECK( nInsertSuccess == c_nSetSize * c_nInsThreadCount );
631 CPPUNIT_CHECK( nInsertFailed == 0 );
633 CPPUNIT_MSG( " Totals (success/failed): \n\t"
634 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
635 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
636 << " Extract=" << nExtractSuccess << '/' << nExtractFailed << "\n\t"
640 template <typename Set>
641 void analyze( Set& testSet )
643 // All even keys must be in the set
645 CPPUNIT_MSG( " Check even keys..." );
646 size_t nErrorCount = 0;
647 for ( size_t n = 0; n < c_nSetSize; n +=2 ) {
648 for ( size_t i = 0; i < c_nInsThreadCount; ++i ) {
649 if ( !testSet.contains( key_type(n, i) ) ) {
650 if ( ++nErrorCount < 10 ) {
651 CPPUNIT_MSG( "key " << n << "-" << i << " is not found!");
656 CPPUNIT_CHECK_EX( nErrorCount == 0, "Totals: " << nErrorCount << " keys is not found");
659 check_before_clear( testSet );
661 CPPUNIT_MSG( " Clear map (single-threaded)..." );
662 cds::OS::Timer timer;
664 CPPUNIT_MSG( " Duration=" << timer.duration() );
665 CPPUNIT_CHECK_EX( testSet.empty(), ((long long) testSet.size()) );
667 additional_check( testSet );
668 print_stat( testSet );
669 additional_cleanup( testSet );
675 static_assert( !Set::c_bExtractSupported, "Set class must not support extract() method" );
677 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
678 << " delete thread count=" << c_nDelThreadCount
679 << " set size=" << c_nSetSize
682 if ( Set::c_bLoadFactorDepended ) {
683 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
684 CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
686 Set testSet( *this );
687 do_test_with( testSet );
690 if ( c_bPrintGCState )
695 Set testSet( *this );
696 do_test_with( testSet );
699 if ( c_bPrintGCState )
705 void run_test_extract()
707 static_assert( Set::c_bExtractSupported, "Set class must support extract() method" );
709 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
710 << " delete thread count=" << c_nDelThreadCount
711 << " extract thread count=" << c_nExtractThreadCount
712 << " set size=" << c_nSetSize
715 if ( Set::c_bLoadFactorDepended ) {
716 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
717 CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
719 Set testSet( *this );
720 do_test_extract_with( testSet );
723 if ( c_bPrintGCState )
728 Set testSet( *this );
729 do_test_extract_with( testSet );
732 if ( c_bPrintGCState )
737 void setUpParams( const CppUnitMini::TestCfg& cfg );
739 # include "set2/set_defs.h"
740 CDSUNIT_DECLARE_MichaelSet
741 CDSUNIT_DECLARE_SplitList
742 CDSUNIT_DECLARE_SkipListSet
743 CDSUNIT_DECLARE_EllenBinTreeSet
744 CDSUNIT_DECLARE_CuckooSet
746 CPPUNIT_TEST_SUITE_(Set_DelOdd, "Map_DelOdd")
747 CDSUNIT_TEST_MichaelSet
748 CDSUNIT_TEST_SplitList
749 CDSUNIT_TEST_SkipListSet
750 CDSUNIT_TEST_EllenBinTreeSet
751 CDSUNIT_TEST_CuckooSet
753 //CDSUNIT_TEST_MultiLevelHashSet // the test is not suitable
754 CPPUNIT_TEST_SUITE_END();