2 This file is a part of libcds - Concurrent Data Structures library
4 (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
6 Source code repo: http://github.com/khizmax/libcds/
7 Download: http://sourceforge.net/projects/libcds/files/
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 key_thread( size_t key, size_t threadNo )
43 : nKey( static_cast<uint32_t>(key))
44 , nThread( static_cast<uint16_t>(threadNo))
54 struct cmp<key_thread> {
55 int operator ()(key_thread const& k1, key_thread const& k2) const
57 if ( k1.nKey < k2.nKey )
59 if ( k1.nKey > k2.nKey )
61 if ( k1.nThread < k2.nThread )
63 if ( k1.nThread > k2.nThread )
67 int operator ()(key_thread const& k1, size_t k2) const
75 int operator ()(size_t k1, key_thread const& k2) const
86 struct less<key_thread>
88 bool operator()( key_thread const& k1, key_thread const& k2 ) const
90 if ( k1.nKey <= k2.nKey )
91 return k1.nKey < k2.nKey || k1.nThread < k2.nThread;
97 struct hash<key_thread>
99 typedef size_t result_type;
100 typedef key_thread argument_type;
102 size_t operator()( key_thread const& k ) const
104 return std::hash<size_t>()(k.nKey);
106 size_t operator()( size_t k ) const
108 return std::hash<size_t>()(k);
112 class Map_DelOdd: public cds_test::stress_fixture
115 static size_t s_nInsThreadCount; // insert thread count
116 static size_t s_nDelThreadCount; // delete thread count
117 static size_t s_nExtractThreadCount; // extract thread count
118 static size_t s_nMapSize; // max map size
119 static size_t s_nMaxLoadFactor; // maximum load factor
121 static size_t s_nCuckooInitialSize; // initial size for CuckooMap
122 static size_t s_nCuckooProbesetSize; // CuckooMap probeset size (only for list-based probeset)
123 static size_t s_nCuckooProbesetThreshold; // CuckooMap probeset threshold (0 - use default)
125 static size_t s_nFeldmanMap_HeadBits;
126 static size_t s_nFeldmanMap_ArrayBits;
128 static size_t s_nLoadFactor; // current load factor
130 static std::vector<size_t> m_arrInsert;
131 static std::vector<size_t> m_arrRemove;
133 static void SetUpTestCase();
134 static void TearDownTestCase();
137 typedef key_thread key_type;
138 typedef size_t value_type;
139 typedef std::pair<key_type const, value_type> pair_type;
141 atomics::atomic<size_t> m_nInsThreadCount;
149 // Inserts keys from [0..N)
151 class Inserter: public cds_test::thread
153 typedef cds_test::thread base_class;
158 template <typename Q>
159 void operator()( bool /*bNew*/, Q const& )
161 template <typename Q, typename V>
162 void operator()( bool /*bNew*/, Q const&, V& )
166 template <typename Q>
167 void operator()( Q&, Q*)
171 size_t m_nInsertSuccess = 0;
172 size_t m_nInsertFailed = 0;
175 Inserter( cds_test::thread_pool& pool, Map& map )
176 : base_class( pool, inserter_thread )
180 Inserter( Inserter& src )
185 virtual thread * clone()
187 return new Inserter( *this );
193 Map_DelOdd& fixture = pool().template fixture<Map_DelOdd>();
195 std::vector<size_t>& arrData = fixture.m_arrInsert;
196 for ( size_t i = 0; i < arrData.size(); ++i ) {
197 if ( rMap.insert( key_type( arrData[i], id() )))
204 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
205 if ( arrData[i] & 1 ) {
206 rMap.update( key_type( arrData[i], id() ), f );
210 fixture.m_nInsThreadCount.fetch_sub( 1, atomics::memory_order_acquire );
215 bool operator()( key_type const& k1, key_type const& k2 ) const
217 return k1.nKey == k2.nKey;
219 bool operator()( size_t k1, key_type const& k2 ) const
221 return k1 == k2.nKey;
223 bool operator()( key_type const& k1, size_t k2 ) const
225 return k1.nKey == k2;
230 bool operator()( key_type const& k1, key_type const& k2 ) const
232 return k1.nKey < k2.nKey;
234 bool operator()( size_t k1, key_type const& k2 ) const
238 bool operator()( key_type const& k1, size_t k2 ) const
243 typedef key_equal equal_to;
246 // Deletes odd keys from [0..N)
248 class Deleter: public cds_test::thread
250 typedef cds_test::thread base_class;
254 size_t m_nDeleteSuccess = 0;
255 size_t m_nDeleteFailed = 0;
258 Deleter( cds_test::thread_pool& pool, Map& map )
259 : base_class( pool, deleter_thread )
262 Deleter( Deleter& src )
267 virtual thread * clone()
269 return new Deleter( *this );
272 template <typename MapType, bool>
274 static bool erase(MapType& map, size_t key, size_t /*insThread*/)
276 return map.erase_with(key, key_less());
280 template <typename MapType>
281 struct eraser<MapType, true>
283 static bool erase(MapType& map, size_t key, size_t insThread)
285 return map.erase(key_type(key, insThread));
293 Map_DelOdd& fixture = pool().template fixture<Map_DelOdd>();
294 size_t const nInsThreadCount = s_nInsThreadCount;
296 for ( size_t pass = 0; pass < 2; pass++ ) {
297 std::vector<size_t>& arrData = fixture.m_arrRemove;
299 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
300 for ( size_t i = 0; i < arrData.size(); ++i ) {
301 if ( arrData[i] & 1 ) {
302 if ( Map::c_bEraseExactKey ) {
303 for (size_t key = 0; key < nInsThreadCount; ++key) {
304 if ( eraser<Map, Map::c_bEraseExactKey>::erase( rMap, arrData[i], key ))
311 if ( eraser<Map, Map::c_bEraseExactKey>::erase(rMap, arrData[i], 0))
318 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
323 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
324 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
325 if ( arrData[i] & 1 ) {
326 if ( Map::c_bEraseExactKey ) {
327 for (size_t key = 0; key < nInsThreadCount; ++key) {
328 if (eraser<Map, Map::c_bEraseExactKey>::erase(rMap, arrData[i], key))
335 if (eraser<Map, Map::c_bEraseExactKey>::erase(rMap, arrData[i], 0))
342 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
350 // Deletes odd keys from [0..N)
351 template <class GC, class Map >
352 class Extractor: public cds_test::thread
354 typedef cds_test::thread base_class;
358 size_t m_nDeleteSuccess = 0;
359 size_t m_nDeleteFailed = 0;
362 Extractor( cds_test::thread_pool& pool, Map& map )
363 : base_class( pool, extractor_thread )
367 Extractor( Extractor& src )
372 virtual thread * clone()
374 return new Extractor( *this );
377 template <typename MapType, bool>
379 static typename Map::guarded_ptr extract(MapType& map, size_t key, size_t /*insThread*/)
381 return map.extract_with(key, key_less());
385 template <typename MapType>
386 struct extractor<MapType, true>
388 static typename Map::guarded_ptr extract(MapType& map, size_t key, size_t insThread)
390 return map.extract(key_type(key, insThread));
398 typename Map::guarded_ptr gp;
399 Map_DelOdd& fixture = pool().template fixture<Map_DelOdd>();
400 size_t const nInsThreadCount = s_nInsThreadCount;
402 for ( size_t pass = 0; pass < 2; ++pass ) {
403 std::vector<size_t>& arrData = fixture.m_arrRemove;
405 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
406 for ( size_t i = 0; i < arrData.size(); ++i ) {
407 if ( arrData[i] & 1 ) {
408 gp = extractor< Map, Map::c_bEraseExactKey >::extract( rMap, arrData[i], k );
416 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
421 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
422 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
423 if ( arrData[i] & 1 ) {
424 gp = extractor< Map, Map::c_bEraseExactKey >::extract( rMap, arrData[i], k);
432 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
440 template <class RCU, class Map >
441 class Extractor< cds::urcu::gc<RCU>, Map > : public cds_test::thread
443 typedef cds_test::thread base_class;
447 size_t m_nDeleteSuccess = 0;
448 size_t m_nDeleteFailed = 0;
451 Extractor( cds_test::thread_pool& pool, Map& map )
452 : base_class( pool, extractor_thread )
456 Extractor( Extractor& src )
461 virtual thread * clone()
463 return new Extractor( *this );
466 template <typename MapType, bool>
468 static typename Map::exempt_ptr extract( MapType& map, size_t key, size_t /*insThread*/ )
470 return map.extract_with( key, key_less());
474 template <typename MapType>
475 struct extractor<MapType, true>
477 static typename Map::exempt_ptr extract(MapType& map, size_t key, size_t insThread)
479 return map.extract( key_type(key, insThread));
486 Map_DelOdd& fixture = pool().template fixture<Map_DelOdd>();
488 typename Map::exempt_ptr xp;
489 size_t const nInsThreadCount = s_nInsThreadCount;
491 std::vector<size_t>& arrData = fixture.m_arrRemove;
493 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
494 for ( size_t i = 0; i < arrData.size(); ++i ) {
495 if ( arrData[i] & 1 ) {
496 if ( Map::c_bExtractLockExternal ) {
498 typename Map::rcu_lock l;
499 xp = extractor<Map, Map::c_bEraseExactKey>::extract( rMap, arrData[i], k );
507 xp = extractor<Map, Map::c_bEraseExactKey>::extract( rMap, arrData[i], k);
516 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
521 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
522 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
523 if ( arrData[i] & 1 ) {
524 if ( Map::c_bExtractLockExternal ) {
526 typename Map::rcu_lock l;
527 xp = extractor<Map, Map::c_bEraseExactKey>::extract(rMap, arrData[i], k);
535 xp = extractor<Map, Map::c_bEraseExactKey>::extract(rMap, arrData[i], k);
544 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
553 void do_test( Map& testMap )
555 typedef Inserter<Map> insert_thread;
556 typedef Deleter<Map> delete_thread;
558 m_nInsThreadCount.store( s_nInsThreadCount, atomics::memory_order_release );
560 cds_test::thread_pool& pool = get_pool();
561 pool.add( new insert_thread( pool, testMap ), s_nInsThreadCount );
562 pool.add( new delete_thread( pool, testMap ), s_nDelThreadCount ? s_nDelThreadCount : cds::OS::topology::processor_count() );
564 propout() << std::make_pair( "insert_thread_count", s_nInsThreadCount )
565 << std::make_pair( "delete_thread_count", s_nDelThreadCount )
566 << std::make_pair( "map_size", s_nMapSize );
568 std::chrono::milliseconds duration = pool.run();
570 propout() << std::make_pair( "duration", duration );
572 size_t nInsertSuccess = 0;
573 size_t nInsertFailed = 0;
574 size_t nDeleteSuccess = 0;
575 size_t nDeleteFailed = 0;
577 for ( size_t i = 0; i < pool.size(); ++i ) {
578 cds_test::thread& thr = pool.get( i );
579 if ( thr.type() == inserter_thread ) {
580 insert_thread& inserter = static_cast<insert_thread&>(thr);
581 nInsertSuccess += inserter.m_nInsertSuccess;
582 nInsertFailed += inserter.m_nInsertFailed;
585 assert( thr.type() == deleter_thread );
586 delete_thread& deleter = static_cast<delete_thread&>(thr);
587 nDeleteSuccess += deleter.m_nDeleteSuccess;
588 nDeleteFailed += deleter.m_nDeleteFailed;
592 EXPECT_EQ( nInsertSuccess, s_nMapSize * s_nInsThreadCount );
593 EXPECT_EQ( nInsertFailed, 0 );
596 << std::make_pair( "insert_success", nInsertSuccess )
597 << std::make_pair( "insert_failed", nInsertFailed )
598 << std::make_pair( "delete_success", nDeleteSuccess )
599 << std::make_pair( "delete_failed", nDeleteFailed );
605 void do_test_extract( Map& testMap )
607 typedef Inserter<Map> insert_thread;
608 typedef Deleter<Map> delete_thread;
609 typedef Extractor< typename Map::gc, Map > extract_thread;
611 m_nInsThreadCount.store( s_nInsThreadCount, atomics::memory_order_release );
613 cds_test::thread_pool& pool = get_pool();
614 pool.add( new insert_thread( pool, testMap ), s_nInsThreadCount );
615 if ( s_nDelThreadCount )
616 pool.add( new delete_thread( pool, testMap ), s_nDelThreadCount );
617 if ( s_nExtractThreadCount )
618 pool.add( new extract_thread( pool, testMap ), s_nExtractThreadCount );
620 propout() << std::make_pair( "insert_thread_count", s_nInsThreadCount )
621 << std::make_pair( "delete_thread_count", s_nDelThreadCount )
622 << std::make_pair( "extract_thread_count", s_nExtractThreadCount )
623 << std::make_pair( "map_size", s_nMapSize );
625 std::chrono::milliseconds duration = pool.run();
627 propout() << std::make_pair( "duration", duration );
629 size_t nInsertSuccess = 0;
630 size_t nInsertFailed = 0;
631 size_t nDeleteSuccess = 0;
632 size_t nDeleteFailed = 0;
633 size_t nExtractSuccess = 0;
634 size_t nExtractFailed = 0;
635 for ( size_t i = 0; i < pool.size(); ++i ) {
636 cds_test::thread& thr = pool.get( i );
637 switch ( thr.type() ) {
638 case inserter_thread:
640 insert_thread& inserter = static_cast<insert_thread&>(thr);
641 nInsertSuccess += inserter.m_nInsertSuccess;
642 nInsertFailed += inserter.m_nInsertFailed;
647 delete_thread& deleter = static_cast<delete_thread&>(thr);
648 nDeleteSuccess += deleter.m_nDeleteSuccess;
649 nDeleteFailed += deleter.m_nDeleteFailed;
652 case extractor_thread:
654 extract_thread& extractor = static_cast<extract_thread&>(thr);
655 nExtractSuccess += extractor.m_nDeleteSuccess;
656 nExtractFailed += extractor.m_nDeleteFailed;
664 EXPECT_EQ( nInsertSuccess, s_nMapSize * s_nInsThreadCount );
665 EXPECT_EQ( nInsertFailed, 0 );
668 << std::make_pair( "insert_success", nInsertSuccess )
669 << std::make_pair( "insert_failed", nInsertFailed )
670 << std::make_pair( "delete_success", nDeleteSuccess )
671 << std::make_pair( "delete_failed", nDeleteFailed )
672 << std::make_pair( "extract_success", nExtractSuccess )
673 << std::make_pair( "extract_failed", nExtractFailed );
679 void analyze( Map& testMap )
681 // All even keys must be in the map
683 for ( size_t n = 0; n < s_nMapSize; n +=2 ) {
684 for ( size_t i = 0; i < s_nInsThreadCount; ++i ) {
685 EXPECT_TRUE( testMap.contains( key_type( n, i ) ) ) << "key=" << n << "/" << i;
690 print_stat( propout(), testMap );
692 check_before_cleanup( testMap );
694 EXPECT_TRUE( testMap.empty() ) << "map.size=" << testMap.size();
696 additional_check( testMap );
697 additional_cleanup( testMap );
701 void run_test_extract()
703 static_assert( Map::c_bExtractSupported, "Map class must support extract() method" );
705 Map testMap( *this );
706 do_test_extract( testMap );
712 Map testMap( *this );
717 class Map_DelOdd_LF: public Map_DelOdd
718 , public ::testing::WithParamInterface<size_t>
724 s_nLoadFactor = GetParam();
725 propout() << std::make_pair( "load_factor", s_nLoadFactor );
726 Map_DelOdd::run_test<Map>();
730 void run_test_extract()
732 s_nLoadFactor = GetParam();
733 propout() << std::make_pair( "load_factor", s_nLoadFactor );
734 Map_DelOdd::run_test_extract<Map>();
737 static std::vector<size_t> get_load_factors();