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.
32 #include <mutex> //unique_lock
33 #include "map2/map_type.h"
34 #include "cppunit/thread.h"
36 #include <cds/sync/spinlock.h>
41 #define TEST_CASE(TAG, X) void X();
43 class Map_InsDel_func: public CppUnitMini::TestCase
46 size_t c_nMapSize = 1000000; // map size
47 size_t c_nInsertThreadCount = 4; // count of insertion thread
48 size_t c_nDeleteThreadCount = 4; // count of deletion thread
49 size_t c_nUpdateThreadCount = 4; // count of updating thread
50 size_t c_nThreadPassCount = 4; // pass count for each thread
51 size_t c_nMaxLoadFactor = 8; // maximum load factor
52 bool c_bPrintGCState = true;
54 size_t c_nCuckooInitialSize = 1024;// initial size for CuckooMap
55 size_t c_nCuckooProbesetSize = 16; // CuckooMap probeset size (only for list-based probeset)
56 size_t c_nCuckooProbesetThreshold = 0; // CUckooMap probeset threshold (o - use default)
58 size_t c_nFeldmanMap_HeadBits = 10;
59 size_t c_nFeldmanMap_ArrayBits = 4;
61 size_t c_nLoadFactor; // current load factor
64 typedef size_t key_type;
69 atomics::atomic<bool> bInitialized;
70 cds::OS::ThreadId threadId; // inserter thread id
72 typedef cds::sync::spin_lock< cds::backoff::pause > lock_type;
73 mutable lock_type m_access;
79 , bInitialized( false )
80 , threadId( cds::OS::get_current_thread_id())
83 value_type( value_type const& s )
86 , nUpdateCall( s.nUpdateCall )
87 , bInitialized( s.bInitialized.load(atomics::memory_order_relaxed))
88 , threadId( cds::OS::get_current_thread_id())
91 // boost::container::flat_map requires operator =
92 value_type& operator=( value_type const& v )
96 nUpdateCall = v.nUpdateCall;
97 bInitialized.store(v.bInitialized.load(atomics::memory_order_relaxed), atomics::memory_order_relaxed);
103 typedef std::vector<key_type> key_array;
104 key_array m_arrValues;
107 class Inserter: public CppUnitMini::TestThread
111 virtual Inserter * clone()
113 return new Inserter( *this );
116 struct insert_functor {
117 size_t nTestFunctorRef;
123 template <typename Pair>
124 void operator()( Pair& val )
126 operator()( val.first, val.second );
129 template <typename Key, typename Val >
130 void operator()( Key const& key, Val& v )
132 std::unique_lock< typename value_type::lock_type> ac( v.m_access );
138 v.bInitialized.store( true, atomics::memory_order_relaxed);
143 size_t m_nInsertSuccess;
144 size_t m_nInsertFailed;
146 size_t m_nTestFunctorRef;
149 Inserter( CppUnitMini::ThreadPool& pool, Map& rMap )
150 : CppUnitMini::TestThread( pool )
153 Inserter( Inserter& src )
154 : CppUnitMini::TestThread( src )
158 Map_InsDel_func& getTest()
160 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
163 virtual void init() { cds::threading::Manager::attachThread() ; }
164 virtual void fini() { cds::threading::Manager::detachThread() ; }
172 m_nTestFunctorRef = 0;
174 // func is passed by reference
176 key_array const& arr = getTest().m_arrValues;
177 size_t const nPassCount = getTest().c_nThreadPassCount;
179 if ( m_nThreadNo & 1 ) {
180 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
181 for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
182 if ( rMap.insert_with( *it, std::ref(func)))
190 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
191 for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
192 if ( rMap.insert_with( *it, std::ref(func)))
200 m_nTestFunctorRef = func.nTestFunctorRef;
205 class Updater: public CppUnitMini::TestThread
209 virtual Updater * clone()
211 return new Updater( *this );
214 struct update_functor {
223 template <typename Key, typename Val>
224 void operator()( bool /*bNew*/, Key const& key, Val& v )
226 std::unique_lock<typename value_type::lock_type> ac( v.m_access );
227 if ( !v.bInitialized.load( atomics::memory_order_acquire )) {
231 v.bInitialized.store( true, atomics::memory_order_relaxed);
239 template <typename Pair>
240 void operator()( bool bNew, Pair& val )
242 operator()( bNew, val.first, val.second );
245 // For FeldmanHashMap
246 template <typename Val>
247 void operator()( Val& cur, Val * old )
250 // If a key exists, FeldmanHashMap creates a new node too
251 // We should manually copy important values from old to cur
252 std::unique_lock<typename value_type::lock_type> ac( cur.second.m_access );
253 cur.second.nKey = cur.first;
254 cur.second.nData = cur.first * 8;
255 cur.second.bInitialized.store( true, atomics::memory_order_release );
257 operator()( old == nullptr, cur.first, cur.second );
261 update_functor(const update_functor& ) = delete;
265 size_t m_nUpdateFailed;
266 size_t m_nUpdateCreated;
267 size_t m_nUpdateExisted;
268 size_t m_nFunctorCreated;
269 size_t m_nFunctorModified;
272 Updater( CppUnitMini::ThreadPool& pool, Map& rMap )
273 : CppUnitMini::TestThread( pool )
276 Updater( Updater& src )
277 : CppUnitMini::TestThread( src )
281 Map_InsDel_func& getTest()
283 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
286 virtual void init() { cds::threading::Manager::attachThread() ; }
287 virtual void fini() { cds::threading::Manager::detachThread() ; }
299 key_array const& arr = getTest().m_arrValues;
300 size_t const nPassCount = getTest().c_nThreadPassCount;
302 if ( m_nThreadNo & 1 ) {
303 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
304 for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
305 std::pair<bool, bool> ret = rMap.update( *it, std::ref( func ));
318 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
319 for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
320 std::pair<bool, bool> ret = rMap.update( *it, std::ref( func ));
333 m_nFunctorCreated = func.nCreated;
334 m_nFunctorModified = func.nModified;
339 class Deleter: public CppUnitMini::TestThread
342 typedef typename Map::mapped_type value_type;
344 virtual Deleter * clone()
346 return new Deleter( *this );
349 struct value_container
362 struct erase_functor {
363 value_container m_cnt;
365 template <typename Key, typename Val>
366 void operator()( Key const& /*key*/, Val& v )
369 if ( v.bInitialized.load( atomics::memory_order_relaxed )) {
370 std::unique_lock< typename value_type::lock_type> ac( v.m_access );
372 if ( m_cnt.nKeyExpected == v.nKey && m_cnt.nKeyExpected * 8 == v.nData )
373 ++m_cnt.nSuccessItem;
381 cds::backoff::yield()();
385 template <typename Pair>
386 void operator ()( Pair& item )
388 operator()( item.first, item.second );
393 size_t m_nDeleteSuccess;
394 size_t m_nDeleteFailed;
396 size_t m_nValueSuccess;
397 size_t m_nValueFailed;
400 Deleter( CppUnitMini::ThreadPool& pool, Map& rMap )
401 : CppUnitMini::TestThread( pool )
404 Deleter( Deleter& src )
405 : CppUnitMini::TestThread( src )
409 Map_InsDel_func& getTest()
411 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
414 virtual void init() { cds::threading::Manager::attachThread() ; }
415 virtual void fini() { cds::threading::Manager::detachThread() ; }
425 key_array const& arr = getTest().m_arrValues;
426 size_t const nPassCount = getTest().c_nThreadPassCount;
428 if ( m_nThreadNo & 1 ) {
429 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
430 for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
431 func.m_cnt.nKeyExpected = *it;
432 if ( rMap.erase( *it, std::ref(func)))
440 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
441 for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
442 func.m_cnt.nKeyExpected = *it;
443 if ( rMap.erase( *it, std::ref(func)))
451 m_nValueSuccess = func.m_cnt.nSuccessItem;
452 m_nValueFailed = func.m_cnt.nFailedItem;
459 void do_test( Map& testMap )
461 typedef Inserter<Map> InserterThread;
462 typedef Deleter<Map> DeleterThread;
463 typedef Updater<Map> UpdaterThread;
464 cds::OS::Timer timer;
467 m_arrValues.reserve( c_nMapSize );
468 for ( size_t i = 0; i < c_nMapSize; ++i )
469 m_arrValues.push_back( i );
470 shuffle( m_arrValues.begin(), m_arrValues.end());
472 CppUnitMini::ThreadPool pool( *this );
473 pool.add( new InserterThread( pool, testMap ), c_nInsertThreadCount );
474 pool.add( new DeleterThread( pool, testMap ), c_nDeleteThreadCount );
475 pool.add( new UpdaterThread( pool, testMap ), c_nUpdateThreadCount );
477 CPPUNIT_MSG( " Duration=" << pool.avgDuration());
479 size_t nInsertSuccess = 0;
480 size_t nInsertFailed = 0;
481 size_t nDeleteSuccess = 0;
482 size_t nDeleteFailed = 0;
483 size_t nDelValueSuccess = 0;
484 size_t nDelValueFailed = 0;
485 size_t nUpdateFailed = 0;
486 size_t nUpdateCreated = 0;
487 size_t nUpdateModified = 0;
488 size_t nEnsFuncCreated = 0;
489 size_t nEnsFuncModified = 0;
490 size_t nInsFuncCalled = 0;
492 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
493 InserterThread * pThread = dynamic_cast<InserterThread *>( *it );
495 nInsertSuccess += pThread->m_nInsertSuccess;
496 nInsertFailed += pThread->m_nInsertFailed;
497 nInsFuncCalled += pThread->m_nTestFunctorRef;
500 DeleterThread * p = dynamic_cast<DeleterThread *>( *it );
502 nDeleteSuccess += p->m_nDeleteSuccess;
503 nDeleteFailed += p->m_nDeleteFailed;
504 nDelValueSuccess += p->m_nValueSuccess;
505 nDelValueFailed += p->m_nValueFailed;
508 UpdaterThread * pEns = static_cast<UpdaterThread *>( *it );
509 nUpdateCreated += pEns->m_nUpdateCreated;
510 nUpdateModified += pEns->m_nUpdateExisted;
511 nUpdateFailed += pEns->m_nUpdateFailed;
512 nEnsFuncCreated += pEns->m_nFunctorCreated;
513 nEnsFuncModified += pEns->m_nFunctorModified;
518 CPPUNIT_MSG( " Totals: Ins succ=" << nInsertSuccess
519 << " Del succ=" << nDeleteSuccess << "\n"
520 << " : Ins fail=" << nInsertFailed
521 << " Del fail=" << nDeleteFailed << "\n"
522 << " : Update succ=" << (nUpdateCreated + nUpdateModified) << " fail=" << nUpdateFailed
523 << " create=" << nUpdateCreated << " modify=" << nUpdateModified << "\n"
524 << " : update functor: create=" << nEnsFuncCreated << " modify=" << nEnsFuncModified << "\n"
525 << " Map size=" << testMap.size()
528 CPPUNIT_CHECK_EX( nDelValueFailed == 0, "Functor del failed=" << nDelValueFailed );
529 CPPUNIT_CHECK_EX( nDelValueSuccess == nDeleteSuccess, "Delete success=" << nDeleteSuccess << " functor=" << nDelValueSuccess );
531 CPPUNIT_CHECK( nUpdateFailed == 0 );
532 CPPUNIT_CHECK( nUpdateCreated + nUpdateModified == nEnsFuncCreated + nEnsFuncModified );
534 // nInsFuncCalled is call count of insert functor
535 CPPUNIT_CHECK_EX( nInsFuncCalled == nInsertSuccess, "nInsertSuccess=" << nInsertSuccess << " functor nInsFuncCalled=" << nInsFuncCalled );
537 check_before_cleanup( testMap );
539 CPPUNIT_MSG( " Clear map (single-threaded)..." );
541 for ( size_t nItem = 0; nItem < c_nMapSize; ++nItem ) {
542 testMap.erase( nItem );
544 CPPUNIT_MSG( " Duration=" << timer.duration());
545 CPPUNIT_CHECK( testMap.empty());
547 additional_check( testMap );
548 print_stat( testMap );
549 additional_cleanup( testMap );
555 CPPUNIT_MSG( "Thread count: insert=" << c_nInsertThreadCount
556 << " delete=" << c_nDeleteThreadCount
557 << " update=" << c_nUpdateThreadCount
558 << " pass count=" << c_nThreadPassCount
559 << " map size=" << c_nMapSize
562 if ( Map::c_bLoadFactorDepended ) {
563 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
564 CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
565 Map testMap( *this );
567 if ( c_bPrintGCState )
572 Map testMap( *this );
574 if ( c_bPrintGCState )
579 void setUpParams( const CppUnitMini::TestCfg& cfg );
581 # include "map2/map_defs.h"
582 CDSUNIT_DECLARE_MichaelMap
583 CDSUNIT_DECLARE_SplitList
584 CDSUNIT_DECLARE_SkipListMap
585 CDSUNIT_DECLARE_EllenBinTreeMap
586 CDSUNIT_DECLARE_BronsonAVLTreeMap
587 CDSUNIT_DECLARE_FeldmanHashMap_fixed
588 CDSUNIT_DECLARE_FeldmanHashMap_city
589 CDSUNIT_DECLARE_StripedMap
590 CDSUNIT_DECLARE_RefinableMap
591 CDSUNIT_DECLARE_CuckooMap
593 CPPUNIT_TEST_SUITE(Map_InsDel_func)
594 CDSUNIT_TEST_MichaelMap
595 CDSUNIT_TEST_SplitList
596 CDSUNIT_TEST_SkipListMap
597 CDSUNIT_TEST_EllenBinTreeMap
598 CDSUNIT_TEST_BronsonAVLTreeMap
599 CDSUNIT_TEST_FeldmanHashMap_fixed
600 CDSUNIT_TEST_FeldmanHashMap_city
601 CDSUNIT_TEST_CuckooMap
602 CDSUNIT_TEST_StripedMap
603 CDSUNIT_TEST_RefinableMap
604 CPPUNIT_TEST_SUITE_END();