3 #include "cppunit/thread.h"
4 #include "set2/set_type.h"
8 #define TEST_CASE(TAG, X) void X();
17 key_thread( size_t key, size_t threadNo )
18 : nKey( static_cast<uint32_t>(key))
19 , nThread( static_cast<uint16_t>(threadNo))
27 typedef set_type_base<key_thread, size_t>::key_val key_value_pair;
31 struct cmp<key_thread> {
32 int operator ()(key_thread const& k1, key_thread const& k2) const
34 if ( k1.nKey < k2.nKey )
36 if ( k1.nKey > k2.nKey )
38 if ( k1.nThread < k2.nThread )
40 if ( k1.nThread > k2.nThread )
44 int operator ()(key_thread const& k1, size_t k2) const
52 int operator ()(size_t k1, key_thread const& k2) const
66 struct less<set2::key_thread>
68 bool operator()(set2::key_thread const& k1, set2::key_thread const& k2) const
70 if ( k1.nKey <= k2.nKey )
71 return k1.nKey < k2.nKey || k1.nThread < k2.nThread;
77 struct hash<set2::key_thread>
79 typedef size_t result_type;
80 typedef set2::key_thread argument_type;
82 size_t operator()( set2::key_thread const& k ) const
84 return std::hash<size_t>()(k.nKey);
86 size_t operator()( size_t k ) const
88 return std::hash<size_t>()(k);
95 inline size_t hash_value( set2::key_thread const& k )
97 return std::hash<size_t>()( k.nKey );
101 struct hash<set2::key_thread>
103 typedef size_t result_type;
104 typedef set2::key_thread argument_type;
106 size_t operator()(set2::key_thread const& k) const
108 return boost::hash<size_t>()( k.nKey );
110 size_t operator()(size_t k) const
112 return boost::hash<size_t>()( k );
119 class Set_DelOdd: public CppUnitMini::TestCase
122 size_t c_nSetSize =1000000; // max set size
123 size_t c_nInsThreadCount = 4; // insert thread count
124 size_t c_nDelThreadCount = 4; // delete thread count
125 size_t c_nExtractThreadCount = 4; // extract thread count
126 size_t c_nMaxLoadFactor = 8; // maximum load factor
127 bool c_bPrintGCState = true;
129 size_t c_nCuckooInitialSize = 1024;// initial size for CuckooSet
130 size_t c_nCuckooProbesetSize = 16; // CuckooSet probeset size (only for list-based probeset)
131 size_t c_nCuckooProbesetThreshold = 0; // CUckooSet probeset threshold (0 - use default)
133 size_t c_nFeldmanSet_HeadBits = 10;
134 size_t c_nFeldmanSet_ArrayBits = 4;
136 size_t c_nLoadFactor = 2;
137 std::vector<size_t> m_arrData;
140 typedef key_thread key_type;
141 typedef size_t value_type;
143 atomics::atomic<size_t> m_nInsThreadCount;
145 // Inserts keys from [0..N)
147 class InsertThread: public CppUnitMini::TestThread
151 virtual InsertThread * clone()
153 return new InsertThread( *this );
156 struct update_functor
158 template <typename Q>
159 void operator()( bool /*bNew*/, key_value_pair const&, Q const& )
162 void operator()(key_value_pair& /*cur*/, key_value_pair * /*prev*/)
166 size_t m_nInsertSuccess;
167 size_t m_nInsertFailed;
170 InsertThread( CppUnitMini::ThreadPool& pool, Set& rMap )
171 : CppUnitMini::TestThread( pool )
174 InsertThread( InsertThread& src )
175 : CppUnitMini::TestThread( src )
179 Set_DelOdd& getTest()
181 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
184 virtual void init() { cds::threading::Manager::attachThread() ; }
185 virtual void fini() { cds::threading::Manager::detachThread() ; }
194 std::vector<size_t>& arrData = getTest().m_arrData;
195 for ( size_t i = 0; i < arrData.size(); ++i ) {
196 if ( rSet.insert( key_type( arrData[i], m_nThreadNo )))
203 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
204 if ( arrData[i] & 1 ) {
205 rSet.update( key_type( arrData[i], m_nThreadNo ), f, true );
209 getTest().m_nInsThreadCount.fetch_sub( 1, atomics::memory_order_release );
214 bool operator()( key_type const& k1, key_type const& k2 ) const
216 return k1.nKey == k2.nKey;
218 bool operator()( size_t k1, key_type const& k2 ) const
220 return k1 == k2.nKey;
222 bool operator()( key_type const& k1, size_t k2 ) const
224 return k1.nKey == k2;
226 bool operator ()( key_value_pair const& k1, key_value_pair const& k2 ) const
228 return operator()( k1.key, k2.key );
230 bool operator ()( key_value_pair const& k1, key_type const& k2 ) const
232 return operator()( k1.key, k2 );
234 bool operator ()( key_type const& k1, key_value_pair const& k2 ) const
236 return operator()( k1, k2.key );
238 bool operator ()( key_value_pair const& k1, size_t k2 ) const
240 return operator()( k1.key, k2 );
242 bool operator ()( size_t k1, key_value_pair const& k2 ) const
244 return operator()( k1, k2.key );
249 bool operator()( key_type const& k1, key_type const& k2 ) const
251 return k1.nKey < k2.nKey;
253 bool operator()( size_t k1, key_type const& k2 ) const
257 bool operator()( key_type const& k1, size_t k2 ) const
261 bool operator ()( key_value_pair const& k1, key_value_pair const& k2 ) const
263 return operator()( k1.key, k2.key );
265 bool operator ()( key_value_pair const& k1, key_type const& k2 ) const
267 return operator()( k1.key, k2 );
269 bool operator ()( key_type const& k1, key_value_pair const& k2 ) const
271 return operator()( k1, k2.key );
273 bool operator ()( key_value_pair const& k1, size_t k2 ) const
275 return operator()( k1.key, k2 );
277 bool operator ()( size_t k1, key_value_pair const& k2 ) const
279 return operator()( k1, k2.key );
282 typedef key_equal equal_to;
285 // Deletes odd keys from [0..N)
287 class DeleteThread: public CppUnitMini::TestThread
291 virtual DeleteThread * clone()
293 return new DeleteThread( *this );
296 size_t m_nDeleteSuccess;
297 size_t m_nDeleteFailed;
300 DeleteThread( CppUnitMini::ThreadPool& pool, Set& rMap )
301 : CppUnitMini::TestThread( pool )
304 DeleteThread( DeleteThread& src )
305 : CppUnitMini::TestThread( src )
309 Set_DelOdd& getTest()
311 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
314 virtual void init() { cds::threading::Manager::attachThread() ; }
315 virtual void fini() { cds::threading::Manager::detachThread() ; }
317 template <typename SetType, bool>
319 static bool erase( SetType& s, size_t key, size_t /*thread*/)
321 return s.erase_with( key, key_less() );
325 template <typename SetType>
326 struct eraser<SetType, true> {
327 static bool erase(SetType& s, size_t key, size_t thread)
329 return s.erase( key_type(key, thread));
340 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
341 std::vector<size_t>& arrData = getTest().m_arrData;
343 if ( m_nThreadNo & 1 ) {
344 for (size_t i = 0; i < arrData.size(); ++i) {
345 if (arrData[i] & 1) {
346 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
347 if ( eraser<Set, Set::c_bEraseExactKey>::erase( rSet, arrData[i], k ))
353 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
358 for (size_t i = arrData.size() - 1; i > 0; --i) {
359 if (arrData[i] & 1) {
360 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
361 if (eraser<Set, Set::c_bEraseExactKey>::erase(rSet, arrData[i], k))
367 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
374 // Extracts odd keys from [0..N)
375 template <typename GC, class Set>
376 class ExtractThread: public CppUnitMini::TestThread
380 virtual ExtractThread * clone()
382 return new ExtractThread( *this );
385 size_t m_nExtractSuccess;
386 size_t m_nExtractFailed;
389 ExtractThread( CppUnitMini::ThreadPool& pool, Set& rMap )
390 : CppUnitMini::TestThread( pool )
393 ExtractThread( ExtractThread& src )
394 : CppUnitMini::TestThread( src )
398 Set_DelOdd& getTest()
400 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
403 virtual void init() { cds::threading::Manager::attachThread() ; }
404 virtual void fini() { cds::threading::Manager::detachThread() ; }
406 template <typename SetType, bool>
408 static typename SetType::guarded_ptr extract(SetType& s, size_t key, size_t /*thread*/)
410 return s.extract_with( key, key_less());
414 template <typename SetType>
415 struct extractor<SetType, true> {
416 static typename SetType::guarded_ptr extract(SetType& s, size_t key, size_t thread)
418 return s.extract( key_type(key, thread));
427 m_nExtractFailed = 0;
429 typename Set::guarded_ptr gp;
431 std::vector<size_t>& arrData = getTest().m_arrData;
432 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
434 if ( m_nThreadNo & 1 ) {
435 for ( size_t i = 0; i < arrData.size(); ++i ) {
436 if (arrData[i] & 1) {
437 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
438 gp = extractor<Set, Set::c_bEraseExactKey>::extract( rSet, arrData[i], k );
446 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
451 for (size_t i = arrData.size() - 1; i > 0; --i) {
452 if (arrData[i] & 1) {
453 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
454 gp = extractor<Set, Set::c_bEraseExactKey>::extract( rSet, arrData[i], k);
462 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
469 template <typename RCU, class Set>
470 class ExtractThread< cds::urcu::gc<RCU>, Set >: public CppUnitMini::TestThread
474 virtual ExtractThread * clone()
476 return new ExtractThread( *this );
479 size_t m_nExtractSuccess;
480 size_t m_nExtractFailed;
483 ExtractThread( CppUnitMini::ThreadPool& pool, Set& rMap )
484 : CppUnitMini::TestThread( pool )
487 ExtractThread( ExtractThread& src )
488 : CppUnitMini::TestThread( src )
492 Set_DelOdd& getTest()
494 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
497 virtual void init() { cds::threading::Manager::attachThread() ; }
498 virtual void fini() { cds::threading::Manager::detachThread() ; }
500 template <typename SetType, bool>
502 static typename SetType::exempt_ptr extract(SetType& s, size_t key, size_t /*thread*/)
504 return s.extract_with(key, key_less());
508 template <typename SetType>
509 struct extractor<SetType, true> {
510 static typename SetType::exempt_ptr extract(SetType& s, size_t key, size_t thread)
512 return s.extract(key_type(key, thread));
521 m_nExtractFailed = 0;
523 typename Set::exempt_ptr xp;
525 std::vector<size_t>& arrData = getTest().m_arrData;
526 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
528 if ( m_nThreadNo & 1 ) {
529 for (size_t i = 0; i < arrData.size(); ++i) {
530 if (arrData[i] & 1) {
531 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
532 if ( Set::c_bExtractLockExternal ) {
533 typename Set::rcu_lock l;
534 xp = extractor<Set, Set::c_bEraseExactKey>::extract( rSet, arrData[i], k);
541 xp = extractor<Set, Set::c_bEraseExactKey>::extract(rSet, arrData[i], k);
550 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
555 for (size_t i = arrData.size() - 1; i > 0; --i) {
556 if (arrData[i] & 1) {
557 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
558 if ( Set::c_bExtractLockExternal ) {
559 typename Set::rcu_lock l;
560 xp = extractor<Set, Set::c_bEraseExactKey>::extract(rSet, arrData[i], k);
567 xp = extractor<Set, Set::c_bEraseExactKey>::extract(rSet, arrData[i], k);
576 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
585 void do_test( size_t nLoadFactor )
587 Set testSet( c_nSetSize, nLoadFactor );
588 do_test_with( testSet );
593 void do_test_extract( size_t nLoadFactor )
595 Set testSet( c_nSetSize, nLoadFactor );
596 do_test_extract_with( testSet );
601 void do_test_with( Set& testSet )
603 typedef InsertThread<Set> insert_thread;
604 typedef DeleteThread<Set> delete_thread;
606 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
608 CppUnitMini::ThreadPool pool( *this );
609 pool.add( new insert_thread( pool, testSet ), c_nInsThreadCount );
610 pool.add( new delete_thread( pool, testSet ), c_nDelThreadCount ? c_nDelThreadCount : cds::OS::topology::processor_count());
612 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
614 size_t nInsertSuccess = 0;
615 size_t nInsertFailed = 0;
616 size_t nDeleteSuccess = 0;
617 size_t nDeleteFailed = 0;
618 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
619 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
621 nInsertSuccess += pThread->m_nInsertSuccess;
622 nInsertFailed += pThread->m_nInsertFailed;
625 delete_thread * p = static_cast<delete_thread *>( *it );
626 nDeleteSuccess += p->m_nDeleteSuccess;
627 nDeleteFailed += p->m_nDeleteFailed;
631 CPPUNIT_CHECK( nInsertSuccess == c_nSetSize * c_nInsThreadCount );
632 CPPUNIT_CHECK( nInsertFailed == 0 );
634 CPPUNIT_MSG( " Totals (success/failed): \n\t"
635 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
636 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
641 void do_test_extract_with( Set& testSet )
643 typedef InsertThread<Set> insert_thread;
644 typedef DeleteThread<Set> delete_thread;
645 typedef ExtractThread< typename Set::gc, Set > extract_thread;
647 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
649 CppUnitMini::ThreadPool pool( *this );
650 pool.add( new insert_thread( pool, testSet ), c_nInsThreadCount );
651 if ( c_nDelThreadCount )
652 pool.add( new delete_thread( pool, testSet ), c_nDelThreadCount );
653 if ( c_nExtractThreadCount )
654 pool.add( new extract_thread( pool, testSet ), c_nExtractThreadCount );
656 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
658 size_t nInsertSuccess = 0;
659 size_t nInsertFailed = 0;
660 size_t nDeleteSuccess = 0;
661 size_t nDeleteFailed = 0;
662 size_t nExtractSuccess = 0;
663 size_t nExtractFailed = 0;
664 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
665 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
667 nInsertSuccess += pThread->m_nInsertSuccess;
668 nInsertFailed += pThread->m_nInsertFailed;
671 delete_thread * p = dynamic_cast<delete_thread *>( *it );
673 nDeleteSuccess += p->m_nDeleteSuccess;
674 nDeleteFailed += p->m_nDeleteFailed;
677 extract_thread * pExt = dynamic_cast<extract_thread *>( *it );
679 nExtractSuccess += pExt->m_nExtractSuccess;
680 nExtractFailed += pExt->m_nExtractFailed;
685 CPPUNIT_CHECK( nInsertSuccess == c_nSetSize * c_nInsThreadCount );
686 CPPUNIT_CHECK( nInsertFailed == 0 );
688 CPPUNIT_MSG( " Totals (success/failed): \n\t"
689 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
690 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
691 << " Extract=" << nExtractSuccess << '/' << nExtractFailed << "\n\t"
695 template <typename Set>
696 void analyze( Set& testSet )
698 // All even keys must be in the set
700 CPPUNIT_MSG( " Check even keys..." );
701 size_t nErrorCount = 0;
702 for ( size_t n = 0; n < c_nSetSize; n +=2 ) {
703 for ( size_t i = 0; i < c_nInsThreadCount; ++i ) {
704 if ( !testSet.contains( key_type(n, i) ) ) {
705 if ( ++nErrorCount < 10 ) {
706 CPPUNIT_MSG( "key " << n << "-" << i << " is not found!");
711 CPPUNIT_CHECK_EX( nErrorCount == 0, "Totals: " << nErrorCount << " keys is not found");
714 check_before_clear( testSet );
716 CPPUNIT_MSG( " Clear map (single-threaded)..." );
717 cds::OS::Timer timer;
719 CPPUNIT_MSG( " Duration=" << timer.duration() );
720 CPPUNIT_CHECK_EX( testSet.empty(), ((long long) testSet.size()) );
722 additional_check( testSet );
723 print_stat( testSet );
724 additional_cleanup( testSet );
730 static_assert( !Set::c_bExtractSupported, "Set class must not support extract() method" );
732 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
733 << " delete thread count=" << c_nDelThreadCount
734 << " set size=" << c_nSetSize
737 if ( Set::c_bLoadFactorDepended ) {
738 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
739 CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
741 Set testSet( *this );
742 do_test_with( testSet );
745 if ( c_bPrintGCState )
750 Set testSet( *this );
751 do_test_with( testSet );
754 if ( c_bPrintGCState )
760 void run_test_extract()
762 static_assert( Set::c_bExtractSupported, "Set class must support extract() method" );
764 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
765 << " delete thread count=" << c_nDelThreadCount
766 << " extract thread count=" << c_nExtractThreadCount
767 << " set size=" << c_nSetSize
770 if ( Set::c_bLoadFactorDepended ) {
771 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
772 CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
774 Set testSet( *this );
775 do_test_extract_with( testSet );
778 if ( c_bPrintGCState )
783 Set testSet( *this );
784 do_test_extract_with( testSet );
787 if ( c_bPrintGCState )
792 void setUpParams( const CppUnitMini::TestCfg& cfg );
793 virtual void endTestCase();
795 # include "set2/set_defs.h"
796 CDSUNIT_DECLARE_MichaelSet
797 CDSUNIT_DECLARE_SplitList
798 CDSUNIT_DECLARE_SkipListSet
799 CDSUNIT_DECLARE_EllenBinTreeSet
800 CDSUNIT_DECLARE_CuckooSet
801 CDSUNIT_DECLARE_FeldmanHashSet_fixed
802 CDSUNIT_DECLARE_FeldmanHashSet_city
804 CPPUNIT_TEST_SUITE_(Set_DelOdd, "Map_DelOdd")
805 CDSUNIT_TEST_MichaelSet
806 CDSUNIT_TEST_SplitList
807 CDSUNIT_TEST_SkipListSet
808 CDSUNIT_TEST_EllenBinTreeSet
809 CDSUNIT_TEST_FeldmanHashSet_fixed
810 CDSUNIT_TEST_FeldmanHashSet_city
811 CDSUNIT_TEST_CuckooSet
812 CPPUNIT_TEST_SUITE_END();