3 #include "cppunit/thread.h"
4 #include "map2/map_types.h"
5 #include <algorithm> // random_shuffle
9 # define TEST_MAP(X) void X() { test<MapTypes<key_type, value_type>::X >(); }
10 # define TEST_MAP_EXTRACT(X) void X() { test_extract<MapTypes<key_type, value_type>::X >(); }
11 # define TEST_MAP_NOLF(X) void X() { test_nolf<MapTypes<key_type, value_type>::X >(); }
12 # define TEST_MAP_NOLF_EXTRACT(X) void X() { test_nolf_extract<MapTypes<key_type, value_type>::X >(); }
15 static size_t c_nMapSize = 1000000 ; // max map 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 MapTypes<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<map2::key_thread>
79 bool operator()(map2::key_thread const& k1, map2::key_thread const& k2) const
81 if ( k1.nKey <= k2.nKey )
82 return k1.nKey < k2.nKey || k1.nThread < k2.nThread;
88 struct hash<map2::key_thread>
90 typedef size_t result_type;
91 typedef map2::key_thread argument_type;
93 size_t operator()( map2::key_thread const& k ) const
95 return std::hash<size_t>()(k.nKey);
97 size_t operator()( size_t k ) const
99 return std::hash<size_t>()(k);
105 inline size_t hash_value( map2::key_thread const& k )
107 return std::hash<size_t>()( k.nKey );
111 struct hash<map2::key_thread>
113 typedef size_t result_type;
114 typedef map2::key_thread argument_type;
116 size_t operator()(map2::key_thread const& k) const
118 return boost::hash<size_t>()( k.nKey );
120 size_t operator()(size_t k) const
122 return boost::hash<size_t>()( k );
129 template <typename Map>
130 static inline void check_before_clear( Map& /*s*/ )
133 template <typename GC, typename Key, typename T, typename Traits>
134 static inline void check_before_clear( cds::container::EllenBinTreeMap<GC, Key, T, Traits>& s )
136 CPPUNIT_CHECK_CURRENT( s.check_consistency() );
139 class Map_DelOdd: public CppUnitMini::TestCase
141 std::vector<size_t> m_arrData;
144 typedef key_thread key_type;
145 typedef size_t value_type;
146 typedef std::pair<key_type const, value_type> pair_type;
148 atomics::atomic<size_t> m_nInsThreadCount;
150 // Inserts keys from [0..N)
152 class InsertThread: public CppUnitMini::TestThread
156 virtual InsertThread * clone()
158 return new InsertThread( *this );
163 template <typename Q>
164 void operator()( bool /*bNew*/, Q const& )
166 template <typename Q, typename V>
167 void operator()( bool /*bNew*/, Q const&, V& )
171 size_t m_nInsertSuccess;
172 size_t m_nInsertFailed;
175 InsertThread( CppUnitMini::ThreadPool& pool, Map& rMap )
176 : CppUnitMini::TestThread( pool )
179 InsertThread( InsertThread& src )
180 : CppUnitMini::TestThread( src )
184 Map_DelOdd& getTest()
186 return reinterpret_cast<Map_DelOdd&>( m_Pool.m_Test );
189 virtual void init() { cds::threading::Manager::attachThread() ; }
190 virtual void fini() { cds::threading::Manager::detachThread() ; }
199 std::vector<size_t>& arrData = getTest().m_arrData;
200 for ( size_t i = 0; i < arrData.size(); ++i ) {
201 if ( rMap.insert( key_type( arrData[i], m_nThreadNo )))
208 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
209 if ( arrData[i] & 1 ) {
210 rMap.ensure( key_type( arrData[i], m_nThreadNo ), f );
214 getTest().m_nInsThreadCount.fetch_sub( 1, atomics::memory_order_acquire );
219 bool operator()( key_type const& k1, key_type const& k2 ) const
221 return k1.nKey == k2.nKey;
223 bool operator()( size_t k1, key_type const& k2 ) const
225 return k1 == k2.nKey;
227 bool operator()( key_type const& k1, size_t k2 ) const
229 return k1.nKey == k2;
234 bool operator()( key_type const& k1, key_type const& k2 ) const
236 return k1.nKey < k2.nKey;
238 bool operator()( size_t k1, key_type const& k2 ) const
242 bool operator()( key_type const& k1, size_t k2 ) const
247 typedef key_equal equal_to;
250 // Deletes odd keys from [0..N)
252 class DeleteThread: public CppUnitMini::TestThread
256 virtual DeleteThread * clone()
258 return new DeleteThread( *this );
261 size_t m_nDeleteSuccess;
262 size_t m_nDeleteFailed;
265 DeleteThread( CppUnitMini::ThreadPool& pool, Map& rMap )
266 : CppUnitMini::TestThread( pool )
269 DeleteThread( DeleteThread& src )
270 : CppUnitMini::TestThread( src )
274 Map_DelOdd& getTest()
276 return reinterpret_cast<Map_DelOdd&>( m_Pool.m_Test );
279 virtual void init() { cds::threading::Manager::attachThread() ; }
280 virtual void fini() { cds::threading::Manager::detachThread() ; }
289 std::vector<size_t>& arrData = getTest().m_arrData;
290 if ( m_nThreadNo & 1 ) {
291 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
292 for ( size_t i = 0; i < arrData.size(); ++i ) {
293 if ( arrData[i] & 1 ) {
294 if ( rMap.erase_with( arrData[i], key_less() ))
300 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
305 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
306 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
307 if ( arrData[i] & 1 ) {
308 if ( rMap.erase_with( arrData[i], key_less() ))
314 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
321 // Deletes odd keys from [0..N)
322 template <class GC, class Map >
323 class ExtractThread: public CppUnitMini::TestThread
327 virtual ExtractThread * clone()
329 return new ExtractThread( *this );
332 size_t m_nDeleteSuccess;
333 size_t m_nDeleteFailed;
336 ExtractThread( CppUnitMini::ThreadPool& pool, Map& rMap )
337 : CppUnitMini::TestThread( pool )
340 ExtractThread( ExtractThread& src )
341 : CppUnitMini::TestThread( src )
345 Map_DelOdd& getTest()
347 return reinterpret_cast<Map_DelOdd&>( m_Pool.m_Test );
350 virtual void init() { cds::threading::Manager::attachThread() ; }
351 virtual void fini() { cds::threading::Manager::detachThread() ; }
360 typename Map::guarded_ptr gp;
362 std::vector<size_t>& arrData = getTest().m_arrData;
363 if ( m_nThreadNo & 1 ) {
364 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
365 for ( size_t i = 0; i < arrData.size(); ++i ) {
366 if ( arrData[i] & 1 ) {
367 gp = rMap.extract_with( arrData[i], key_less());
375 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
380 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
381 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
382 if ( arrData[i] & 1 ) {
383 gp = rMap.extract_with( arrData[i], key_less());
391 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
398 template <class RCU, class Map >
399 class ExtractThread< cds::urcu::gc<RCU>, Map > : public CppUnitMini::TestThread
403 virtual ExtractThread * clone()
405 return new ExtractThread( *this );
408 size_t m_nDeleteSuccess;
409 size_t m_nDeleteFailed;
412 ExtractThread( CppUnitMini::ThreadPool& pool, Map& rMap )
413 : CppUnitMini::TestThread( pool )
416 ExtractThread( ExtractThread& src )
417 : CppUnitMini::TestThread( src )
421 Map_DelOdd& getTest()
423 return reinterpret_cast<Map_DelOdd&>( m_Pool.m_Test );
426 virtual void init() { cds::threading::Manager::attachThread() ; }
427 virtual void fini() { cds::threading::Manager::detachThread() ; }
436 typename Map::exempt_ptr xp;
438 std::vector<size_t>& arrData = getTest().m_arrData;
439 if ( m_nThreadNo & 1 ) {
440 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
441 for ( size_t i = 0; i < arrData.size(); ++i ) {
442 if ( arrData[i] & 1 ) {
443 if ( Map::c_bExtractLockExternal ) {
445 typename Map::rcu_lock l;
446 xp = rMap.extract_with( arrData[i], key_less() );
454 xp = rMap.extract_with( arrData[i], key_less() );
463 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
468 for ( size_t k = 0; k < c_nInsThreadCount; ++k ) {
469 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
470 if ( arrData[i] & 1 ) {
471 if ( Map::c_bExtractLockExternal ) {
473 typename Map::rcu_lock l;
474 xp = rMap.extract_with( arrData[i], key_less() );
482 xp = rMap.extract_with( arrData[i], key_less() );
491 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
500 void do_test( size_t nLoadFactor )
502 Map testMap( c_nMapSize, nLoadFactor );
503 do_test_with( testMap );
507 void do_test_extract( size_t nLoadFactor )
509 Map testMap( c_nMapSize, nLoadFactor );
510 do_test_extract_with( testMap );
514 void do_test_with( Map& testMap )
516 typedef InsertThread<Map> insert_thread;
517 typedef DeleteThread<Map> delete_thread;
519 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
521 CppUnitMini::ThreadPool pool( *this );
522 pool.add( new insert_thread( pool, testMap ), c_nInsThreadCount );
523 pool.add( new delete_thread( pool, testMap ), c_nDelThreadCount ? c_nDelThreadCount : cds::OS::topology::processor_count());
525 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
527 size_t nInsertSuccess = 0;
528 size_t nInsertFailed = 0;
529 size_t nDeleteSuccess = 0;
530 size_t nDeleteFailed = 0;
531 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
532 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
534 nInsertSuccess += pThread->m_nInsertSuccess;
535 nInsertFailed += pThread->m_nInsertFailed;
538 delete_thread * p = static_cast<delete_thread *>( *it );
539 nDeleteSuccess += p->m_nDeleteSuccess;
540 nDeleteFailed += p->m_nDeleteFailed;
544 CPPUNIT_MSG( " Totals (success/failed): \n\t"
545 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
546 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
548 CPPUNIT_CHECK( nInsertSuccess == c_nMapSize * c_nInsThreadCount );
549 CPPUNIT_CHECK( nInsertFailed == 0 );
555 void do_test_extract_with( Map& testMap )
557 typedef InsertThread<Map> insert_thread;
558 typedef DeleteThread<Map> delete_thread;
559 typedef ExtractThread< typename Map::gc, Map > extract_thread;
561 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
563 CppUnitMini::ThreadPool pool( *this );
564 pool.add( new insert_thread( pool, testMap ), c_nInsThreadCount );
565 if ( c_nDelThreadCount )
566 pool.add( new delete_thread( pool, testMap ), c_nDelThreadCount );
567 if ( c_nExtractThreadCount )
568 pool.add( new extract_thread( pool, testMap ), c_nExtractThreadCount );
570 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
572 size_t nInsertSuccess = 0;
573 size_t nInsertFailed = 0;
574 size_t nDeleteSuccess = 0;
575 size_t nDeleteFailed = 0;
576 size_t nExtractSuccess = 0;
577 size_t nExtractFailed = 0;
578 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
579 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
581 nInsertSuccess += pThread->m_nInsertSuccess;
582 nInsertFailed += pThread->m_nInsertFailed;
585 delete_thread * p = dynamic_cast<delete_thread *>( *it );
587 nDeleteSuccess += p->m_nDeleteSuccess;
588 nDeleteFailed += p->m_nDeleteFailed;
591 extract_thread * pExtract = dynamic_cast<extract_thread *>( *it );
593 nExtractSuccess += pExtract->m_nDeleteSuccess;
594 nExtractFailed += pExtract->m_nDeleteFailed;
599 CPPUNIT_MSG( " Totals (success/failed): \n\t"
600 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
601 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
602 << " Extract=" << nExtractSuccess << '/' << nExtractFailed << "\n\t"
604 CPPUNIT_CHECK( nInsertSuccess == c_nMapSize * c_nInsThreadCount );
605 CPPUNIT_CHECK( nInsertFailed == 0 );
611 void analyze( Map& testMap )
613 cds::OS::Timer timer;
615 // All even keys must be in the map
617 size_t nErrorCount = 0;
618 CPPUNIT_MSG( " Check even keys..." );
619 for ( size_t n = 0; n < c_nMapSize; n +=2 ) {
620 for ( size_t i = 0; i < c_nInsThreadCount; ++i ) {
621 if ( !testMap.find( key_type(n, i) ) ) {
622 if ( ++nErrorCount < 10 ) {
623 CPPUNIT_MSG( "key " << n << "-" << i << " is not found!");
628 CPPUNIT_CHECK_EX( nErrorCount == 0, "Totals: " << nErrorCount << " keys is not found");
631 check_before_clear( testMap );
633 CPPUNIT_MSG( " Clear map (single-threaded)..." );
636 CPPUNIT_MSG( " Duration=" << timer.duration() );
637 CPPUNIT_CHECK_EX( testMap.empty(), ((long long) testMap.size()) );
639 additional_check( testMap );
640 print_stat( testMap );
642 additional_cleanup( testMap );
649 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
650 << " delete thread count=" << c_nDelThreadCount
651 << " set size=" << c_nMapSize
654 for ( size_t nLoadFactor = 1; nLoadFactor <= c_nMaxLoadFactor; nLoadFactor *= 2 ) {
655 CPPUNIT_MSG( "Load factor=" << nLoadFactor );
656 do_test<Map>( nLoadFactor );
657 if ( c_bPrintGCState )
665 CPPUNIT_MSG( "Thread count: insert=" << c_nInsThreadCount
666 << ", delete=" << c_nDelThreadCount
667 << ", extract=" << c_nExtractThreadCount
668 << "; set size=" << c_nMapSize
671 for ( size_t nLoadFactor = 1; nLoadFactor <= c_nMaxLoadFactor; nLoadFactor *= 2 ) {
672 CPPUNIT_MSG( "Load factor=" << nLoadFactor );
673 do_test_extract<Map>( nLoadFactor );
674 if ( c_bPrintGCState )
682 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
683 << " delete thread count=" << c_nDelThreadCount
684 << " set size=" << c_nMapSize
689 if ( c_bPrintGCState )
694 void test_nolf_extract()
696 CPPUNIT_MSG( "Thread count: insert=" << c_nInsThreadCount
697 << ", delete=" << c_nDelThreadCount
698 << ", extract=" << c_nExtractThreadCount
699 << "; set size=" << c_nMapSize
703 do_test_extract_with( s );
704 if ( c_bPrintGCState )
708 void setUpParams( const CppUnitMini::TestCfg& cfg ) {
709 c_nMapSize = cfg.getULong("MapSize", static_cast<unsigned long>(c_nMapSize) );
710 c_nInsThreadCount = cfg.getULong("InsThreadCount", static_cast<unsigned long>(c_nInsThreadCount) );
711 c_nDelThreadCount = cfg.getULong("DelThreadCount", static_cast<unsigned long>(c_nDelThreadCount) );
712 c_nExtractThreadCount = cfg.getULong("ExtractThreadCount", static_cast<unsigned long>(c_nExtractThreadCount) );
713 c_nMaxLoadFactor = cfg.getULong("MaxLoadFactor", static_cast<unsigned long>(c_nMaxLoadFactor) );
714 c_bPrintGCState = cfg.getBool("PrintGCStateFlag", true );
716 if ( c_nInsThreadCount == 0 )
717 c_nInsThreadCount = cds::OS::topology::processor_count();
718 if ( c_nDelThreadCount == 0 && c_nExtractThreadCount == 0 ) {
719 c_nExtractThreadCount = cds::OS::topology::processor_count() / 2;
720 c_nDelThreadCount = cds::OS::topology::processor_count() - c_nExtractThreadCount;
723 m_arrData.resize( c_nMapSize );
724 for ( size_t i = 0; i < c_nMapSize; ++i )
726 std::random_shuffle( m_arrData.begin(), m_arrData.end() );
729 # include "map2/map_defs.h"
730 CDSUNIT_DECLARE_MichaelMap
731 CDSUNIT_DECLARE_SplitList
732 //CDSUNIT_DECLARE_StripedMap
733 //CDSUNIT_DECLARE_RefinableMap
734 CDSUNIT_DECLARE_CuckooMap
735 CDSUNIT_DECLARE_SkipListMap
736 CDSUNIT_DECLARE_EllenBinTreeMap
737 CDSUNIT_DECLARE_BronsonAVLTreeMap
738 //CDSUNIT_DECLARE_StdMap
740 CPPUNIT_TEST_SUITE( Map_DelOdd )
741 CDSUNIT_TEST_MichaelMap
742 CDSUNIT_TEST_SplitList
743 CDSUNIT_TEST_SkipListMap
744 CDSUNIT_TEST_EllenBinTreeMap
745 CDSUNIT_TEST_BronsonAVLTreeMap
746 //CDSUNIT_TEST_StripedMap
747 //CDSUNIT_TEST_RefinableMap
748 CDSUNIT_TEST_CuckooMap
749 //CDSUNIT_TEST_StdMap
750 CPPUNIT_TEST_SUITE_END()
753 CPPUNIT_TEST_SUITE_REGISTRATION( Map_DelOdd );