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.
31 #include "cppunit/thread.h"
32 #include "map2/map_type.h"
33 #include <cds/os/topology.h>
37 # define TEST_CASE(TAG, X) void X();
46 key_thread( size_t key, size_t threadNo )
47 : nKey( static_cast<uint32_t>(key))
48 , nThread( static_cast<uint16_t>(threadNo))
58 struct cmp<key_thread> {
59 int operator ()(key_thread const& k1, key_thread const& k2) const
61 if ( k1.nKey < k2.nKey )
63 if ( k1.nKey > k2.nKey )
65 if ( k1.nThread < k2.nThread )
67 if ( k1.nThread > k2.nThread )
71 int operator ()(key_thread const& k1, size_t k2) const
79 int operator ()(size_t k1, key_thread const& k2) const
93 struct less<map2::key_thread>
95 bool operator()(map2::key_thread const& k1, map2::key_thread const& k2) const
97 if ( k1.nKey <= k2.nKey )
98 return k1.nKey < k2.nKey || k1.nThread < k2.nThread;
104 struct hash<map2::key_thread>
106 typedef size_t result_type;
107 typedef map2::key_thread argument_type;
109 size_t operator()( map2::key_thread const& k ) const
111 return std::hash<size_t>()(k.nKey);
113 size_t operator()( size_t k ) const
115 return std::hash<size_t>()(k);
121 inline size_t hash_value( map2::key_thread const& k )
123 return std::hash<size_t>()( k.nKey );
127 struct hash<map2::key_thread>
129 typedef size_t result_type;
130 typedef map2::key_thread argument_type;
132 size_t operator()(map2::key_thread const& k) const
134 return boost::hash<size_t>()( k.nKey );
136 size_t operator()(size_t k) const
138 return boost::hash<size_t>()( k );
145 class Map_DelOdd: public CppUnitMini::TestCase
148 size_t c_nInsThreadCount = 4; // insert thread count
149 size_t c_nDelThreadCount = 4; // delete thread count
150 size_t c_nExtractThreadCount = 4; // extract thread count
151 size_t c_nMapSize = 1000000; // max map size
152 size_t c_nMaxLoadFactor = 8; // maximum load factor
154 size_t c_nCuckooInitialSize = 1024;// initial size for CuckooMap
155 size_t c_nCuckooProbesetSize = 16; // CuckooMap probeset size (only for list-based probeset)
156 size_t c_nCuckooProbesetThreshold = 0; // CUckooMap probeset threshold (0 - use default)
158 size_t c_nFeldmanMap_HeadBits = 10;
159 size_t c_nFeldmanMap_ArrayBits = 4;
161 bool c_bPrintGCState = true;
163 size_t c_nLoadFactor; // current load factor
166 std::vector<size_t> m_arrInsert;
167 std::vector<size_t> m_arrRemove;
170 typedef key_thread key_type;
171 typedef size_t value_type;
172 typedef std::pair<key_type const, value_type> pair_type;
174 atomics::atomic<size_t> m_nInsThreadCount;
176 // Inserts keys from [0..N)
178 class InsertThread: public CppUnitMini::TestThread
182 virtual InsertThread * clone()
184 return new InsertThread( *this );
189 template <typename Q>
190 void operator()( bool /*bNew*/, Q const& )
192 template <typename Q, typename V>
193 void operator()( bool /*bNew*/, Q const&, V& )
197 template <typename Q>
198 void operator()( Q&, Q*)
202 size_t m_nInsertSuccess;
203 size_t m_nInsertFailed;
206 InsertThread( CppUnitMini::ThreadPool& pool, Map& rMap )
207 : CppUnitMini::TestThread( pool )
210 InsertThread( InsertThread& src )
211 : CppUnitMini::TestThread( src )
215 Map_DelOdd& getTest()
217 return reinterpret_cast<Map_DelOdd&>( m_Pool.m_Test );
220 virtual void init() { cds::threading::Manager::attachThread() ; }
221 virtual void fini() { cds::threading::Manager::detachThread() ; }
230 std::vector<size_t>& arrData = getTest().m_arrInsert;
231 for ( size_t i = 0; i < arrData.size(); ++i ) {
232 if ( rMap.insert( key_type( arrData[i], m_nThreadNo )))
239 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
240 if ( arrData[i] & 1 ) {
241 rMap.update( key_type( arrData[i], m_nThreadNo ), f );
245 getTest().m_nInsThreadCount.fetch_sub( 1, atomics::memory_order_acquire );
250 bool operator()( key_type const& k1, key_type const& k2 ) const
252 return k1.nKey == k2.nKey;
254 bool operator()( size_t k1, key_type const& k2 ) const
256 return k1 == k2.nKey;
258 bool operator()( key_type const& k1, size_t k2 ) const
260 return k1.nKey == k2;
265 bool operator()( key_type const& k1, key_type const& k2 ) const
267 return k1.nKey < k2.nKey;
269 bool operator()( size_t k1, key_type const& k2 ) const
273 bool operator()( key_type const& k1, size_t k2 ) const
278 typedef key_equal equal_to;
281 // Deletes odd keys from [0..N)
283 class DeleteThread: public CppUnitMini::TestThread
287 virtual DeleteThread * clone()
289 return new DeleteThread( *this );
292 size_t m_nDeleteSuccess;
293 size_t m_nDeleteFailed;
296 DeleteThread( CppUnitMini::ThreadPool& pool, Map& rMap )
297 : CppUnitMini::TestThread( pool )
300 DeleteThread( DeleteThread& src )
301 : CppUnitMini::TestThread( src )
305 Map_DelOdd& getTest()
307 return reinterpret_cast<Map_DelOdd&>( m_Pool.m_Test );
310 virtual void init() { cds::threading::Manager::attachThread() ; }
311 virtual void fini() { cds::threading::Manager::detachThread() ; }
313 template <typename MapType, bool>
315 static bool erase(MapType& map, size_t key, size_t /*insThread*/)
317 return map.erase_with(key, key_less());
321 template <typename MapType>
322 struct eraser<MapType, true>
324 static bool erase(MapType& map, size_t key, size_t insThread)
326 return map.erase(key_type(key, insThread));
337 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
339 for ( size_t pass = 0; pass < 2; pass++ ) {
340 std::vector<size_t>& arrData = getTest().m_arrRemove;
341 if ( m_nThreadNo & 1 ) {
342 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
343 for ( size_t i = 0; i < arrData.size(); ++i ) {
344 if ( arrData[i] & 1 ) {
345 if ( Map::c_bEraseExactKey ) {
346 for (size_t key = 0; key < nInsThreadCount; ++key) {
347 if ( eraser<Map, Map::c_bEraseExactKey>::erase( rMap, arrData[i], key ))
354 if ( eraser<Map, Map::c_bEraseExactKey>::erase(rMap, arrData[i], 0))
361 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
366 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
367 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
368 if ( arrData[i] & 1 ) {
369 if ( Map::c_bEraseExactKey ) {
370 for (size_t key = 0; key < nInsThreadCount; ++key) {
371 if (eraser<Map, Map::c_bEraseExactKey>::erase(rMap, arrData[i], key))
378 if (eraser<Map, Map::c_bEraseExactKey>::erase(rMap, arrData[i], 0))
385 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
393 // Deletes odd keys from [0..N)
394 template <class GC, class Map >
395 class ExtractThread: public CppUnitMini::TestThread
399 virtual ExtractThread * clone()
401 return new ExtractThread( *this );
404 size_t m_nDeleteSuccess;
405 size_t m_nDeleteFailed;
408 ExtractThread( CppUnitMini::ThreadPool& pool, Map& rMap )
409 : CppUnitMini::TestThread( pool )
412 ExtractThread( ExtractThread& src )
413 : CppUnitMini::TestThread( src )
417 Map_DelOdd& getTest()
419 return reinterpret_cast<Map_DelOdd&>( m_Pool.m_Test );
422 virtual void init() { cds::threading::Manager::attachThread() ; }
423 virtual void fini() { cds::threading::Manager::detachThread() ; }
425 template <typename MapType, bool>
427 static typename Map::guarded_ptr extract(MapType& map, size_t key, size_t /*insThread*/)
429 return map.extract_with(key, key_less());
433 template <typename MapType>
434 struct extractor<MapType, true>
436 static typename Map::guarded_ptr extract(MapType& map, size_t key, size_t insThread)
438 return map.extract(key_type(key, insThread));
449 typename Map::guarded_ptr gp;
450 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
452 for ( size_t pass = 0; pass < 2; ++pass ) {
453 std::vector<size_t>& arrData = getTest().m_arrRemove;
454 if ( m_nThreadNo & 1 ) {
455 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
456 for ( size_t i = 0; i < arrData.size(); ++i ) {
457 if ( arrData[i] & 1 ) {
458 gp = extractor< Map, Map::c_bEraseExactKey >::extract( rMap, arrData[i], k );
466 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
471 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
472 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
473 if ( arrData[i] & 1 ) {
474 gp = extractor< Map, Map::c_bEraseExactKey >::extract( rMap, arrData[i], k);
482 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
490 template <class RCU, class Map >
491 class ExtractThread< cds::urcu::gc<RCU>, Map > : public CppUnitMini::TestThread
495 virtual ExtractThread * clone()
497 return new ExtractThread( *this );
500 size_t m_nDeleteSuccess;
501 size_t m_nDeleteFailed;
504 ExtractThread( CppUnitMini::ThreadPool& pool, Map& rMap )
505 : CppUnitMini::TestThread( pool )
508 ExtractThread( ExtractThread& src )
509 : CppUnitMini::TestThread( src )
513 Map_DelOdd& getTest()
515 return reinterpret_cast<Map_DelOdd&>( m_Pool.m_Test );
518 virtual void init() { cds::threading::Manager::attachThread() ; }
519 virtual void fini() { cds::threading::Manager::detachThread() ; }
521 template <typename MapType, bool>
523 static typename Map::exempt_ptr extract( MapType& map, size_t key, size_t /*insThread*/ )
525 return map.extract_with( key, key_less());
529 template <typename MapType>
530 struct extractor<MapType, true>
532 static typename Map::exempt_ptr extract(MapType& map, size_t key, size_t insThread)
534 return map.extract( key_type(key, insThread));
545 typename Map::exempt_ptr xp;
546 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
548 std::vector<size_t>& arrData = getTest().m_arrRemove;
549 if ( m_nThreadNo & 1 ) {
550 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
551 for ( size_t i = 0; i < arrData.size(); ++i ) {
552 if ( arrData[i] & 1 ) {
553 if ( Map::c_bExtractLockExternal ) {
555 typename Map::rcu_lock l;
556 xp = extractor<Map, Map::c_bEraseExactKey>::extract( rMap, arrData[i], k );
564 xp = extractor<Map, Map::c_bEraseExactKey>::extract( rMap, arrData[i], k);
573 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
578 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
579 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
580 if ( arrData[i] & 1 ) {
581 if ( Map::c_bExtractLockExternal ) {
583 typename Map::rcu_lock l;
584 xp = extractor<Map, Map::c_bEraseExactKey>::extract(rMap, arrData[i], k);
592 xp = extractor<Map, Map::c_bEraseExactKey>::extract(rMap, arrData[i], k);
601 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
612 Map testMap( *this );
613 do_test_with( testMap );
617 void do_test_extract()
619 Map testMap( *this );
620 do_test_extract_with( testMap );
624 void do_test_with( Map& testMap )
626 typedef InsertThread<Map> insert_thread;
627 typedef DeleteThread<Map> delete_thread;
629 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
631 CppUnitMini::ThreadPool pool( *this );
632 pool.add( new insert_thread( pool, testMap ), c_nInsThreadCount );
633 pool.add( new delete_thread( pool, testMap ), c_nDelThreadCount ? c_nDelThreadCount : cds::OS::topology::processor_count());
635 CPPUNIT_MSG( " Duration=" << pool.avgDuration());
637 size_t nInsertSuccess = 0;
638 size_t nInsertFailed = 0;
639 size_t nDeleteSuccess = 0;
640 size_t nDeleteFailed = 0;
641 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
642 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
644 nInsertSuccess += pThread->m_nInsertSuccess;
645 nInsertFailed += pThread->m_nInsertFailed;
648 delete_thread * p = static_cast<delete_thread *>( *it );
649 nDeleteSuccess += p->m_nDeleteSuccess;
650 nDeleteFailed += p->m_nDeleteFailed;
654 CPPUNIT_MSG( " Totals (success/failed): \n\t"
655 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
656 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
658 CPPUNIT_CHECK( nInsertSuccess == c_nMapSize * c_nInsThreadCount );
659 CPPUNIT_CHECK( nInsertFailed == 0 );
665 void do_test_extract_with( Map& testMap )
667 typedef InsertThread<Map> insert_thread;
668 typedef DeleteThread<Map> delete_thread;
669 typedef ExtractThread< typename Map::gc, Map > extract_thread;
671 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
673 CppUnitMini::ThreadPool pool( *this );
674 pool.add( new insert_thread( pool, testMap ), c_nInsThreadCount );
675 if ( c_nDelThreadCount )
676 pool.add( new delete_thread( pool, testMap ), c_nDelThreadCount );
677 if ( c_nExtractThreadCount )
678 pool.add( new extract_thread( pool, testMap ), c_nExtractThreadCount );
680 CPPUNIT_MSG( " Duration=" << pool.avgDuration());
682 size_t nInsertSuccess = 0;
683 size_t nInsertFailed = 0;
684 size_t nDeleteSuccess = 0;
685 size_t nDeleteFailed = 0;
686 size_t nExtractSuccess = 0;
687 size_t nExtractFailed = 0;
688 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
689 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
691 nInsertSuccess += pThread->m_nInsertSuccess;
692 nInsertFailed += pThread->m_nInsertFailed;
695 delete_thread * p = dynamic_cast<delete_thread *>( *it );
697 nDeleteSuccess += p->m_nDeleteSuccess;
698 nDeleteFailed += p->m_nDeleteFailed;
701 extract_thread * pExtract = dynamic_cast<extract_thread *>( *it );
703 nExtractSuccess += pExtract->m_nDeleteSuccess;
704 nExtractFailed += pExtract->m_nDeleteFailed;
709 CPPUNIT_MSG( " Totals (success/failed): \n\t"
710 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
711 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
712 << " Extract=" << nExtractSuccess << '/' << nExtractFailed << "\n\t"
714 CPPUNIT_CHECK( nInsertSuccess == c_nMapSize * c_nInsThreadCount );
715 CPPUNIT_CHECK( nInsertFailed == 0 );
721 void analyze( Map& testMap )
723 cds::OS::Timer timer;
725 // All even keys must be in the map
727 size_t nErrorCount = 0;
728 CPPUNIT_MSG( " Check even keys..." );
729 for ( size_t n = 0; n < c_nMapSize; n +=2 ) {
730 for ( size_t i = 0; i < c_nInsThreadCount; ++i ) {
731 if ( !testMap.contains( key_type(n, i))) {
732 if ( ++nErrorCount < 10 ) {
733 CPPUNIT_MSG( "key " << n << "-" << i << " is not found!");
738 CPPUNIT_CHECK_EX( nErrorCount == 0, "Totals: " << nErrorCount << " keys is not found");
743 check_before_cleanup( testMap );
745 CPPUNIT_MSG( " Clear map (single-threaded)..." );
748 CPPUNIT_MSG( " Duration=" << timer.duration());
749 CPPUNIT_CHECK_EX( testMap.empty(), ((long long) testMap.size()));
751 additional_check( testMap );
752 print_stat( testMap );
754 additional_cleanup( testMap );
760 static_assert( Map::c_bExtractSupported, "Map class must support extract() method" );
762 CPPUNIT_MSG( "Thread count: insert=" << c_nInsThreadCount
763 << ", delete=" << c_nDelThreadCount
764 << ", extract=" << c_nExtractThreadCount
765 << "; set size=" << c_nMapSize
767 if ( Map::c_bLoadFactorDepended ) {
768 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
769 CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
770 do_test_extract<Map>();
771 if ( c_bPrintGCState )
776 do_test_extract<Map>();
780 void run_test_no_extract()
782 static_assert( !Map::c_bExtractSupported, "Map class must not support extract() method" );
784 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
785 << " delete thread count=" << c_nDelThreadCount
786 << " set size=" << c_nMapSize
788 if ( Map::c_bLoadFactorDepended ) {
789 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
790 CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
792 if ( c_bPrintGCState )
800 void setUpParams( const CppUnitMini::TestCfg& cfg );
801 virtual void endTestCase();
803 # include "map2/map_defs.h"
804 CDSUNIT_DECLARE_MichaelMap
805 CDSUNIT_DECLARE_SplitList
806 CDSUNIT_DECLARE_SkipListMap
807 CDSUNIT_DECLARE_EllenBinTreeMap
808 CDSUNIT_DECLARE_BronsonAVLTreeMap
809 CDSUNIT_DECLARE_FeldmanHashMap_fixed
810 //CDSUNIT_DECLARE_FeldmanHashMap_city
811 CDSUNIT_DECLARE_CuckooMap
813 CPPUNIT_TEST_SUITE_(Map_DelOdd, "map_delodd")
814 CDSUNIT_TEST_MichaelMap
815 CDSUNIT_TEST_SplitList
816 CDSUNIT_TEST_SkipListMap
817 CDSUNIT_TEST_EllenBinTreeMap
818 CDSUNIT_TEST_BronsonAVLTreeMap
819 CDSUNIT_TEST_FeldmanHashMap_fixed
820 //CDSUNIT_TEST_FeldmanHashMap_city
821 CDSUNIT_TEST_CuckooMap
822 CPPUNIT_TEST_SUITE_END();
824 // Not implemented yet
825 ////CDSUNIT_DECLARE_StripedMap
826 ////CDSUNIT_DECLARE_RefinableMap
827 ////CDSUNIT_DECLARE_StdMap