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))
55 static_assert(sizeof( key_thread ) % 8 == 0, "Key size mismatch!!!");
59 struct cmp<key_thread> {
60 int operator ()(key_thread const& k1, key_thread const& k2) const
62 if ( k1.nKey < k2.nKey )
64 if ( k1.nKey > k2.nKey )
66 if ( k1.nThread < k2.nThread )
68 if ( k1.nThread > k2.nThread )
72 int operator ()(key_thread const& k1, size_t k2) const
80 int operator ()(size_t k1, key_thread const& k2) const
91 struct less<key_thread>
93 bool operator()( key_thread const& k1, key_thread const& k2 ) const
95 if ( k1.nKey <= k2.nKey )
96 return k1.nKey < k2.nKey || k1.nThread < k2.nThread;
102 struct hash<key_thread>
104 typedef size_t result_type;
105 typedef key_thread argument_type;
107 size_t operator()( key_thread const& k ) const
109 return std::hash<size_t>()(k.nKey);
111 size_t operator()( size_t k ) const
113 return std::hash<size_t>()(k);
117 class Map_DelOdd: public cds_test::stress_fixture
120 static size_t s_nInsThreadCount; // insert thread count
121 static size_t s_nDelThreadCount; // delete thread count
122 static size_t s_nExtractThreadCount; // extract thread count
123 static size_t s_nMapSize; // max map size
124 static size_t s_nMaxLoadFactor; // maximum load factor
126 static size_t s_nCuckooInitialSize; // initial size for CuckooMap
127 static size_t s_nCuckooProbesetSize; // CuckooMap probeset size (only for list-based probeset)
128 static size_t s_nCuckooProbesetThreshold; // CuckooMap probeset threshold (0 - use default)
130 static size_t s_nFeldmanMap_HeadBits;
131 static size_t s_nFeldmanMap_ArrayBits;
133 static size_t s_nLoadFactor; // current load factor
135 static std::vector<size_t> m_arrInsert;
136 static std::vector<size_t> m_arrRemove;
138 static void SetUpTestCase();
139 static void TearDownTestCase();
142 typedef key_thread key_type;
143 typedef size_t value_type;
144 typedef std::pair<key_type const, value_type> pair_type;
146 atomics::atomic<size_t> m_nInsThreadCount;
154 // Inserts keys from [0..N)
156 class Inserter: public cds_test::thread
158 typedef cds_test::thread base_class;
163 template <typename Q>
164 void operator()( bool /*bNew*/, Q const& ) const
167 template <typename Q, typename V>
168 void operator()( bool /*bNew*/, Q const&, V& ) const
172 template <typename Q>
173 void operator()( Q&, Q*) const
177 size_t m_nInsertSuccess = 0;
178 size_t m_nInsertFailed = 0;
181 Inserter( cds_test::thread_pool& pool, Map& map )
182 : base_class( pool, inserter_thread )
186 Inserter( Inserter& src )
191 virtual thread * clone()
193 return new Inserter( *this );
199 Map_DelOdd& fixture = pool().template fixture<Map_DelOdd>();
201 std::vector<size_t>& arrData = fixture.m_arrInsert;
202 for ( size_t i = 0; i < arrData.size(); ++i ) {
203 if ( rMap.insert( key_type( arrData[i], id())))
210 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
211 if ( arrData[i] & 1 ) {
212 rMap.update( key_type( arrData[i], id()), f );
216 fixture.m_nInsThreadCount.fetch_sub( 1, atomics::memory_order_acquire );
221 bool operator()( key_type const& k1, key_type const& k2 ) const
223 return k1.nKey == k2.nKey;
225 bool operator()( size_t k1, key_type const& k2 ) const
227 return k1 == k2.nKey;
229 bool operator()( key_type const& k1, size_t k2 ) const
231 return k1.nKey == k2;
236 bool operator()( key_type const& k1, key_type const& k2 ) const
238 return k1.nKey < k2.nKey;
240 bool operator()( size_t k1, key_type const& k2 ) const
244 bool operator()( key_type const& k1, size_t k2 ) const
249 typedef key_equal equal_to;
252 // Deletes odd keys from [0..N)
254 class Deleter: public cds_test::thread
256 typedef cds_test::thread base_class;
260 size_t m_nDeleteSuccess = 0;
261 size_t m_nDeleteFailed = 0;
264 Deleter( cds_test::thread_pool& pool, Map& map )
265 : base_class( pool, deleter_thread )
268 Deleter( Deleter& src )
273 virtual thread * clone()
275 return new Deleter( *this );
278 template <typename MapType, bool>
280 static bool erase(MapType& map, size_t key, size_t /*insThread*/)
282 return map.erase_with(key, key_less());
286 template <typename MapType>
287 struct eraser<MapType, true>
289 static bool erase(MapType& map, size_t key, size_t insThread)
291 return map.erase(key_type(key, insThread));
299 Map_DelOdd& fixture = pool().template fixture<Map_DelOdd>();
300 size_t const nInsThreadCount = s_nInsThreadCount;
302 for ( size_t pass = 0; pass < 2; pass++ ) {
303 std::vector<size_t>& arrData = fixture.m_arrRemove;
305 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
306 for ( size_t i = 0; i < arrData.size(); ++i ) {
307 if ( arrData[i] & 1 ) {
308 if ( Map::c_bEraseExactKey ) {
309 for (size_t key = 0; key < nInsThreadCount; ++key) {
310 if ( eraser<Map, Map::c_bEraseExactKey>::erase( rMap, arrData[i], key ))
317 if ( eraser<Map, Map::c_bEraseExactKey>::erase(rMap, arrData[i], 0))
324 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
329 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
330 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
331 if ( arrData[i] & 1 ) {
332 if ( Map::c_bEraseExactKey ) {
333 for (size_t key = 0; key < nInsThreadCount; ++key) {
334 if (eraser<Map, Map::c_bEraseExactKey>::erase(rMap, arrData[i], key))
341 if (eraser<Map, Map::c_bEraseExactKey>::erase(rMap, arrData[i], 0))
348 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
356 // Deletes odd keys from [0..N)
357 template <class GC, class Map >
358 class Extractor: public cds_test::thread
360 typedef cds_test::thread base_class;
364 size_t m_nDeleteSuccess = 0;
365 size_t m_nDeleteFailed = 0;
368 Extractor( cds_test::thread_pool& pool, Map& map )
369 : base_class( pool, extractor_thread )
373 Extractor( Extractor& src )
378 virtual thread * clone()
380 return new Extractor( *this );
383 template <typename MapType, bool>
385 static typename Map::guarded_ptr extract(MapType& map, size_t key, size_t /*insThread*/)
387 return map.extract_with(key, key_less());
391 template <typename MapType>
392 struct extractor<MapType, true>
394 static typename Map::guarded_ptr extract(MapType& map, size_t key, size_t insThread)
396 return map.extract(key_type(key, insThread));
404 typename Map::guarded_ptr gp;
405 Map_DelOdd& fixture = pool().template fixture<Map_DelOdd>();
406 size_t const nInsThreadCount = s_nInsThreadCount;
408 for ( size_t pass = 0; pass < 2; ++pass ) {
409 std::vector<size_t>& arrData = fixture.m_arrRemove;
411 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
412 for ( size_t i = 0; i < arrData.size(); ++i ) {
413 if ( arrData[i] & 1 ) {
414 gp = extractor< Map, Map::c_bEraseExactKey >::extract( rMap, arrData[i], k );
422 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
427 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
428 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
429 if ( arrData[i] & 1 ) {
430 gp = extractor< Map, Map::c_bEraseExactKey >::extract( rMap, arrData[i], k);
438 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
446 template <class RCU, class Map >
447 class Extractor< cds::urcu::gc<RCU>, Map > : public cds_test::thread
449 typedef cds_test::thread base_class;
453 size_t m_nDeleteSuccess = 0;
454 size_t m_nDeleteFailed = 0;
457 Extractor( cds_test::thread_pool& pool, Map& map )
458 : base_class( pool, extractor_thread )
462 Extractor( Extractor& src )
467 virtual thread * clone()
469 return new Extractor( *this );
472 template <typename MapType, bool>
474 static typename Map::exempt_ptr extract( MapType& map, size_t key, size_t /*insThread*/ )
476 return map.extract_with( key, key_less());
480 template <typename MapType>
481 struct extractor<MapType, true>
483 static typename Map::exempt_ptr extract(MapType& map, size_t key, size_t insThread)
485 return map.extract( key_type(key, insThread));
492 Map_DelOdd& fixture = pool().template fixture<Map_DelOdd>();
494 typename Map::exempt_ptr xp;
495 size_t const nInsThreadCount = s_nInsThreadCount;
497 std::vector<size_t>& arrData = fixture.m_arrRemove;
499 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
500 for ( size_t i = 0; i < arrData.size(); ++i ) {
501 if ( arrData[i] & 1 ) {
502 if ( Map::c_bExtractLockExternal ) {
504 typename Map::rcu_lock l;
505 xp = extractor<Map, Map::c_bEraseExactKey>::extract( rMap, arrData[i], k );
513 xp = extractor<Map, Map::c_bEraseExactKey>::extract( rMap, arrData[i], k);
522 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
527 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
528 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
529 if ( arrData[i] & 1 ) {
530 if ( Map::c_bExtractLockExternal ) {
532 typename Map::rcu_lock l;
533 xp = extractor<Map, Map::c_bEraseExactKey>::extract(rMap, arrData[i], k);
541 xp = extractor<Map, Map::c_bEraseExactKey>::extract(rMap, arrData[i], k);
550 if ( fixture.m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
559 void do_test( Map& testMap )
561 typedef Inserter<Map> insert_thread;
562 typedef Deleter<Map> delete_thread;
564 m_nInsThreadCount.store( s_nInsThreadCount, atomics::memory_order_release );
566 cds_test::thread_pool& pool = get_pool();
567 pool.add( new insert_thread( pool, testMap ), s_nInsThreadCount );
568 pool.add( new delete_thread( pool, testMap ), s_nDelThreadCount ? s_nDelThreadCount : cds::OS::topology::processor_count());
570 propout() << std::make_pair( "insert_thread_count", s_nInsThreadCount )
571 << std::make_pair( "delete_thread_count", s_nDelThreadCount )
572 << std::make_pair( "map_size", s_nMapSize );
574 std::chrono::milliseconds duration = pool.run();
576 propout() << std::make_pair( "duration", duration );
578 size_t nInsertSuccess = 0;
579 size_t nInsertFailed = 0;
580 size_t nDeleteSuccess = 0;
581 size_t nDeleteFailed = 0;
583 for ( size_t i = 0; i < pool.size(); ++i ) {
584 cds_test::thread& thr = pool.get( i );
585 if ( thr.type() == inserter_thread ) {
586 insert_thread& inserter = static_cast<insert_thread&>(thr);
587 nInsertSuccess += inserter.m_nInsertSuccess;
588 nInsertFailed += inserter.m_nInsertFailed;
591 assert( thr.type() == deleter_thread );
592 delete_thread& deleter = static_cast<delete_thread&>(thr);
593 nDeleteSuccess += deleter.m_nDeleteSuccess;
594 nDeleteFailed += deleter.m_nDeleteFailed;
598 EXPECT_EQ( nInsertSuccess, s_nMapSize * s_nInsThreadCount );
599 EXPECT_EQ( nInsertFailed, 0u );
602 << std::make_pair( "insert_success", nInsertSuccess )
603 << std::make_pair( "insert_failed", nInsertFailed )
604 << std::make_pair( "delete_success", nDeleteSuccess )
605 << std::make_pair( "delete_failed", nDeleteFailed );
611 void do_test_extract( Map& testMap )
613 typedef Inserter<Map> insert_thread;
614 typedef Deleter<Map> delete_thread;
615 typedef Extractor< typename Map::gc, Map > extract_thread;
617 m_nInsThreadCount.store( s_nInsThreadCount, atomics::memory_order_release );
619 cds_test::thread_pool& pool = get_pool();
620 pool.add( new insert_thread( pool, testMap ), s_nInsThreadCount );
621 if ( s_nDelThreadCount )
622 pool.add( new delete_thread( pool, testMap ), s_nDelThreadCount );
623 if ( s_nExtractThreadCount )
624 pool.add( new extract_thread( pool, testMap ), s_nExtractThreadCount );
626 propout() << std::make_pair( "insert_thread_count", s_nInsThreadCount )
627 << std::make_pair( "delete_thread_count", s_nDelThreadCount )
628 << std::make_pair( "extract_thread_count", s_nExtractThreadCount )
629 << std::make_pair( "map_size", s_nMapSize );
631 std::chrono::milliseconds duration = pool.run();
633 propout() << std::make_pair( "duration", duration );
635 size_t nInsertSuccess = 0;
636 size_t nInsertFailed = 0;
637 size_t nDeleteSuccess = 0;
638 size_t nDeleteFailed = 0;
639 size_t nExtractSuccess = 0;
640 size_t nExtractFailed = 0;
641 for ( size_t i = 0; i < pool.size(); ++i ) {
642 cds_test::thread& thr = pool.get( i );
643 switch ( thr.type()) {
644 case inserter_thread:
646 insert_thread& inserter = static_cast<insert_thread&>(thr);
647 nInsertSuccess += inserter.m_nInsertSuccess;
648 nInsertFailed += inserter.m_nInsertFailed;
653 delete_thread& deleter = static_cast<delete_thread&>(thr);
654 nDeleteSuccess += deleter.m_nDeleteSuccess;
655 nDeleteFailed += deleter.m_nDeleteFailed;
658 case extractor_thread:
660 extract_thread& extractor = static_cast<extract_thread&>(thr);
661 nExtractSuccess += extractor.m_nDeleteSuccess;
662 nExtractFailed += extractor.m_nDeleteFailed;
670 EXPECT_EQ( nInsertSuccess, s_nMapSize * s_nInsThreadCount );
671 EXPECT_EQ( nInsertFailed, 0u );
674 << std::make_pair( "insert_success", nInsertSuccess )
675 << std::make_pair( "insert_failed", nInsertFailed )
676 << std::make_pair( "delete_success", nDeleteSuccess )
677 << std::make_pair( "delete_failed", nDeleteFailed )
678 << std::make_pair( "extract_success", nExtractSuccess )
679 << std::make_pair( "extract_failed", nExtractFailed );
685 void analyze( Map& testMap )
687 // All even keys must be in the map
689 for ( size_t n = 0; n < s_nMapSize; n +=2 ) {
690 for ( size_t i = 0; i < s_nInsThreadCount; ++i ) {
691 EXPECT_TRUE( testMap.contains( key_type( n, i ))) << "key=" << n << "/" << i;
696 print_stat( propout(), testMap );
698 check_before_cleanup( testMap );
700 EXPECT_TRUE( testMap.empty()) << "map.size=" << testMap.size();
702 additional_check( testMap );
703 additional_cleanup( testMap );
707 void run_test_extract()
709 static_assert( Map::c_bExtractSupported, "Map class must support extract() method" );
711 Map testMap( *this );
712 do_test_extract( testMap );
718 Map testMap( *this );
723 class Map_DelOdd_LF: public Map_DelOdd
724 , public ::testing::WithParamInterface<size_t>
730 s_nLoadFactor = GetParam();
731 propout() << std::make_pair( "load_factor", s_nLoadFactor );
732 Map_DelOdd::run_test<Map>();
736 void run_test_extract()
738 s_nLoadFactor = GetParam();
739 propout() << std::make_pair( "load_factor", s_nLoadFactor );
740 Map_DelOdd::run_test_extract<Map>();
743 static std::vector<size_t> get_load_factors();