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.
33 #include <mutex> //unique_lock
35 #include "set2/set_type.h"
36 #include "cppunit/thread.h"
37 #include <cds/sync/spinlock.h>
41 #define TEST_CASE(TAG, X) void X();
43 class Set_InsDel_func: public CppUnitMini::TestCase
46 size_t c_nSetSize = 1000000; // set 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 ensure thread
50 size_t c_nThreadPassCount = 4; // pass count for each thread
51 size_t c_nMaxLoadFactor = 8; // maximum load factor
54 size_t c_nCuckooInitialSize = 1024;// initial size for CuckooSet
55 size_t c_nCuckooProbesetSize = 16; // CuckooSet probeset size (only for list-based probeset)
56 size_t c_nCuckooProbesetThreshold = 0; // CUckooSet probeset threshold (0 - use default)
58 size_t c_nFeldmanSet_HeadBits = 10;
59 size_t c_nFeldmanSet_ArrayBits = 4;
61 size_t c_nLoadFactor = 2;
64 typedef size_t key_type;
68 atomics::atomic<size_t> nUpdateCall;
69 bool volatile bInitialized;
70 cds::OS::ThreadId threadId ; // insert 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.load(atomics::memory_order_relaxed))
87 , bInitialized( s.bInitialized )
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.store( v.nUpdateCall.load(atomics::memory_order_relaxed), atomics::memory_order_relaxed );
97 bInitialized = v.bInitialized;
105 size_t * m_pKeyFirst;
110 class Inserter: public CppUnitMini::TestThread
113 typedef typename Set::value_type keyval_type;
115 virtual Inserter * clone()
117 return new Inserter( *this );
120 struct insert_functor {
121 size_t nTestFunctorRef;
127 void operator()( keyval_type& val )
129 std::unique_lock< typename value_type::lock_type> ac( val.val.m_access );
131 val.val.nKey = val.key;
132 val.val.nData = val.key * 8;
135 val.val.bInitialized = true;
140 size_t m_nInsertSuccess;
141 size_t m_nInsertFailed;
143 size_t m_nTestFunctorRef;
146 Inserter( CppUnitMini::ThreadPool& pool, Set& rSet )
147 : CppUnitMini::TestThread( pool )
150 Inserter( Inserter& src )
151 : CppUnitMini::TestThread( src )
155 Set_InsDel_func& getTest()
157 return reinterpret_cast<Set_InsDel_func&>( m_Pool.m_Test );
160 virtual void init() { cds::threading::Manager::attachThread() ; }
161 virtual void fini() { cds::threading::Manager::detachThread() ; }
169 m_nTestFunctorRef = 0;
171 size_t * pKeyFirst = getTest().m_pKeyFirst;
172 size_t * pKeyLast = getTest().m_pKeyLast;
173 size_t const nPassCount = getTest().c_nThreadPassCount;
175 // func is passed by reference
178 if ( m_nThreadNo & 1 ) {
179 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
180 for ( size_t * p = pKeyFirst; p < pKeyLast; ++p ) {
181 if ( rSet.insert( *p, std::ref(func) ) )
189 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
190 for ( size_t * p = pKeyLast - 1; p >= pKeyFirst; --p ) {
191 if ( rSet.insert( *p, std::ref(func) ) )
199 m_nTestFunctorRef = func.nTestFunctorRef;
204 class Updater: public CppUnitMini::TestThread
207 typedef typename Set::value_type keyval_type;
209 virtual Updater * clone()
211 return new Updater( *this );
214 struct update_functor {
223 void operator()( bool bNew, keyval_type& val, size_t /*nKey*/ )
225 std::unique_lock<typename value_type::lock_type> ac( val.val.m_access );
226 if ( !val.val.bInitialized )
228 val.val.nKey = val.key;
229 val.val.nData = val.key * 8;
230 val.val.bInitialized = true;
237 val.val.nUpdateCall.fetch_add( 1, atomics::memory_order_relaxed );
242 void operator()( keyval_type& cur, keyval_type * old )
244 operator()( old == nullptr, cur, 0 );
248 update_functor(const update_functor& );
252 size_t m_nUpdateFailed;
253 size_t m_nUpdateCreated;
254 size_t m_nUpdateExisted;
255 size_t m_nFunctorCreated;
256 size_t m_nFunctorModified;
259 Updater( CppUnitMini::ThreadPool& pool, Set& rSet )
260 : CppUnitMini::TestThread( pool )
263 Updater( Updater& src )
264 : CppUnitMini::TestThread( src )
268 Set_InsDel_func& getTest()
270 return reinterpret_cast<Set_InsDel_func&>( m_Pool.m_Test );
273 virtual void init() { cds::threading::Manager::attachThread() ; }
274 virtual void fini() { cds::threading::Manager::detachThread() ; }
284 size_t * pKeyFirst = getTest().m_pKeyFirst;
285 size_t * pKeyLast = getTest().m_pKeyLast;
286 size_t const nPassCount = getTest().c_nThreadPassCount;
290 if ( m_nThreadNo & 1 ) {
291 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
292 for ( size_t * p = pKeyFirst; p < pKeyLast; ++p ) {
293 std::pair<bool, bool> ret = rSet.update( *p, std::ref( func ), true );
306 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
307 for ( size_t * p = pKeyLast - 1 ; p >= pKeyFirst; --p ) {
308 std::pair<bool, bool> ret = rSet.update( *p, std::ref( func ), true );
321 m_nFunctorCreated = func.nCreated;
322 m_nFunctorModified = func.nModified;
327 class Deleter: public CppUnitMini::TestThread
330 typedef typename Set::value_type keyval_type;
332 virtual Deleter * clone()
334 return new Deleter( *this );
337 struct value_container
350 struct erase_functor {
351 value_container m_cnt;
353 void operator ()( keyval_type const& itm )
355 keyval_type& item = const_cast<keyval_type&>(itm);
359 std::unique_lock< typename value_type::lock_type> ac( item.val.m_access );
360 if ( item.val.bInitialized ) {
361 if ( m_cnt.nKeyExpected == item.val.nKey && m_cnt.nKeyExpected * 8 == item.val.nData )
362 ++m_cnt.nSuccessItem;
373 cds::backoff::yield()();
379 size_t m_nDeleteSuccess;
380 size_t m_nDeleteFailed;
382 size_t m_nValueSuccess;
383 size_t m_nValueFailed;
386 Deleter( CppUnitMini::ThreadPool& pool, Set& rSet )
387 : CppUnitMini::TestThread( pool )
390 Deleter( Deleter& src )
391 : CppUnitMini::TestThread( src )
395 Set_InsDel_func& getTest()
397 return reinterpret_cast<Set_InsDel_func&>( m_Pool.m_Test );
400 virtual void init() { cds::threading::Manager::attachThread() ; }
401 virtual void fini() { cds::threading::Manager::detachThread() ; }
410 size_t * pKeyFirst = getTest().m_pKeyFirst;
411 size_t * pKeyLast = getTest().m_pKeyLast;
412 size_t const nPassCount = getTest().c_nThreadPassCount;
416 if ( m_nThreadNo & 1 ) {
417 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
418 for ( size_t * p = pKeyFirst; p < pKeyLast; ++p ) {
419 func.m_cnt.nKeyExpected = *p;
420 if ( rSet.erase( *p, std::ref(func) ))
428 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
429 for ( size_t * p = pKeyLast - 1; p >= pKeyFirst; --p ) {
430 func.m_cnt.nKeyExpected = *p;
431 if ( rSet.erase( *p, std::ref(func) ))
439 m_nValueSuccess = func.m_cnt.nSuccessItem;
440 m_nValueFailed = func.m_cnt.nFailedItem;
447 void do_test( Set& testSet )
449 typedef Inserter<Set> InserterThread;
450 typedef Deleter<Set> DeleterThread;
451 typedef Updater<Set> UpdaterThread;
453 m_pKeyArr = new size_t[ c_nSetSize ];
454 m_pKeyFirst = m_pKeyArr;
455 m_pKeyLast = m_pKeyFirst + c_nSetSize;
456 for ( size_t i = 0; i < c_nSetSize; ++i )
458 shuffle( m_pKeyFirst, m_pKeyLast );
460 cds::OS::Timer timer;
462 CppUnitMini::ThreadPool pool( *this );
463 pool.add( new InserterThread( pool, testSet ), c_nInsertThreadCount );
464 pool.add( new DeleterThread( pool, testSet ), c_nDeleteThreadCount );
465 pool.add( new UpdaterThread( pool, testSet ), c_nUpdateThreadCount );
467 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
471 size_t nInsertSuccess = 0;
472 size_t nInsertFailed = 0;
473 size_t nDeleteSuccess = 0;
474 size_t nDeleteFailed = 0;
475 size_t nDelValueSuccess = 0;
476 size_t nDelValueFailed = 0;
477 size_t nUpdateFailed = 0;
478 size_t nUpdateCreated = 0;
479 size_t nUpdateModified = 0;
480 size_t nEnsFuncCreated = 0;
481 size_t nEnsFuncModified = 0;
482 size_t nTestFunctorRef = 0;
484 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
485 InserterThread * pThread = dynamic_cast<InserterThread *>( *it );
487 nInsertSuccess += pThread->m_nInsertSuccess;
488 nInsertFailed += pThread->m_nInsertFailed;
489 nTestFunctorRef += pThread->m_nTestFunctorRef;
492 DeleterThread * p = dynamic_cast<DeleterThread *>( *it );
494 nDeleteSuccess += p->m_nDeleteSuccess;
495 nDeleteFailed += p->m_nDeleteFailed;
496 nDelValueSuccess += p->m_nValueSuccess;
497 nDelValueFailed += p->m_nValueFailed;
500 UpdaterThread * pEns = static_cast<UpdaterThread *>( *it );
501 nUpdateCreated += pEns->m_nUpdateCreated;
502 nUpdateModified += pEns->m_nUpdateExisted;
503 nUpdateFailed += pEns->m_nUpdateFailed;
504 nEnsFuncCreated += pEns->m_nFunctorCreated;
505 nEnsFuncModified += pEns->m_nFunctorModified;
511 " Totals: Ins succ=" << nInsertSuccess
512 << " Del succ=" << nDeleteSuccess << "\n"
513 << " : Ins fail=" << nInsertFailed
514 << " Del fail=" << nDeleteFailed << "\n"
515 << " : Update succ=" << (nUpdateCreated + nUpdateModified) << " fail=" << nUpdateFailed
516 << " create=" << nUpdateCreated << " modify=" << nUpdateModified << "\n"
517 << " Set size=" << testSet.size()
520 CPPUNIT_CHECK_EX( nDelValueFailed == 0, "Functor del failed=" << nDelValueFailed );
521 CPPUNIT_CHECK_EX( nDelValueSuccess == nDeleteSuccess, "Delete success=" << nDeleteSuccess << " functor=" << nDelValueSuccess );
523 CPPUNIT_CHECK( nUpdateFailed == 0 );
525 CPPUNIT_CHECK_EX( nUpdateCreated == nEnsFuncCreated, "Update created=" << nUpdateCreated << " functor=" << nEnsFuncCreated );
526 CPPUNIT_CHECK_EX( nUpdateModified == nEnsFuncModified, "Update modified=" << nUpdateModified << " functor=" << nEnsFuncModified );
528 // nTestFunctorRef is call count of insert functor
529 CPPUNIT_CHECK_EX( nTestFunctorRef == nInsertSuccess, "nInsertSuccess=" << nInsertSuccess << " functor nTestFunctorRef=" << nTestFunctorRef );
531 CPPUNIT_MSG( " Clear set (single-threaded)..." );
534 CPPUNIT_MSG( " Duration=" << timer.duration() );
535 CPPUNIT_CHECK( testSet.empty() );
537 additional_check( testSet );
538 print_stat( testSet );
540 additional_cleanup( testSet );
546 CPPUNIT_MSG( "Thread count: insert=" << c_nInsertThreadCount
547 << " delete=" << c_nDeleteThreadCount
548 << " ensure=" << c_nUpdateThreadCount
549 << " pass count=" << c_nThreadPassCount
550 << " set size=" << c_nSetSize
553 if ( Set::c_bLoadFactorDepended ) {
554 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
555 CPPUNIT_MSG(" LoadFactor = " << c_nLoadFactor );
558 if ( c_bPrintGCState )
565 if ( c_bPrintGCState )
570 void setUpParams( const CppUnitMini::TestCfg& cfg );
572 # include "set2/set_defs.h"
573 CDSUNIT_DECLARE_MichaelSet
574 CDSUNIT_DECLARE_SkipListSet
575 CDSUNIT_DECLARE_SplitList
576 CDSUNIT_DECLARE_StripedSet
577 CDSUNIT_DECLARE_RefinableSet
578 CDSUNIT_DECLARE_CuckooSet
579 CDSUNIT_DECLARE_EllenBinTreeSet
580 CDSUNIT_DECLARE_FeldmanHashSet_fixed
581 CDSUNIT_DECLARE_FeldmanHashSet_city
583 CPPUNIT_TEST_SUITE_(Set_InsDel_func, "Map_InsDel_func")
584 CDSUNIT_TEST_MichaelSet
585 CDSUNIT_TEST_SplitList
586 CDSUNIT_TEST_SkipListSet
587 CDSUNIT_TEST_FeldmanHashSet_fixed
588 CDSUNIT_TEST_FeldmanHashSet_city
589 CDSUNIT_TEST_EllenBinTreeSet
590 CDSUNIT_TEST_StripedSet
591 CDSUNIT_TEST_RefinableSet
592 CDSUNIT_TEST_CuckooSet
593 CPPUNIT_TEST_SUITE_END();