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 "set2/set_type.h"
36 #define TEST_CASE(TAG, X) void X();
45 key_thread( size_t key, size_t threadNo )
46 : nKey( static_cast<uint32_t>(key))
47 , nThread( static_cast<uint16_t>(threadNo))
55 typedef set_type_base<key_thread, size_t>::key_val key_value_pair;
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
94 struct less<set2::key_thread>
96 bool operator()(set2::key_thread const& k1, set2::key_thread const& k2) const
98 if ( k1.nKey <= k2.nKey )
99 return k1.nKey < k2.nKey || k1.nThread < k2.nThread;
105 struct hash<set2::key_thread>
107 typedef size_t result_type;
108 typedef set2::key_thread argument_type;
110 size_t operator()( set2::key_thread const& k ) const
112 return std::hash<size_t>()(k.nKey);
114 size_t operator()( size_t k ) const
116 return std::hash<size_t>()(k);
123 inline size_t hash_value( set2::key_thread const& k )
125 return std::hash<size_t>()( k.nKey );
129 struct hash<set2::key_thread>
131 typedef size_t result_type;
132 typedef set2::key_thread argument_type;
134 size_t operator()(set2::key_thread const& k) const
136 return boost::hash<size_t>()( k.nKey );
138 size_t operator()(size_t k) const
140 return boost::hash<size_t>()( k );
147 class Set_DelOdd: public CppUnitMini::TestCase
150 size_t c_nSetSize =1000000; // max set size
151 size_t c_nInsThreadCount = 4; // insert thread count
152 size_t c_nDelThreadCount = 4; // delete thread count
153 size_t c_nExtractThreadCount = 4; // extract thread count
154 size_t c_nMaxLoadFactor = 8; // maximum load factor
155 bool c_bPrintGCState = true;
157 size_t c_nCuckooInitialSize = 1024;// initial size for CuckooSet
158 size_t c_nCuckooProbesetSize = 16; // CuckooSet probeset size (only for list-based probeset)
159 size_t c_nCuckooProbesetThreshold = 0; // CUckooSet probeset threshold (0 - use default)
161 size_t c_nFeldmanSet_HeadBits = 10;
162 size_t c_nFeldmanSet_ArrayBits = 4;
164 size_t c_nLoadFactor = 2;
165 std::vector<size_t> m_arrData;
168 typedef key_thread key_type;
169 typedef size_t value_type;
171 atomics::atomic<size_t> m_nInsThreadCount;
173 // Inserts keys from [0..N)
175 class InsertThread: public CppUnitMini::TestThread
179 virtual InsertThread * clone()
181 return new InsertThread( *this );
184 struct update_functor
186 template <typename Q>
187 void operator()( bool /*bNew*/, key_value_pair const&, Q const& )
190 void operator()(key_value_pair& /*cur*/, key_value_pair * /*prev*/)
194 size_t m_nInsertSuccess;
195 size_t m_nInsertFailed;
198 InsertThread( CppUnitMini::ThreadPool& pool, Set& rMap )
199 : CppUnitMini::TestThread( pool )
202 InsertThread( InsertThread& src )
203 : CppUnitMini::TestThread( src )
207 Set_DelOdd& getTest()
209 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
212 virtual void init() { cds::threading::Manager::attachThread() ; }
213 virtual void fini() { cds::threading::Manager::detachThread() ; }
222 std::vector<size_t>& arrData = getTest().m_arrData;
223 for ( size_t i = 0; i < arrData.size(); ++i ) {
224 if ( rSet.insert( key_type( arrData[i], m_nThreadNo )))
231 for ( size_t i = arrData.size() - 1; i > 0; --i ) {
232 if ( arrData[i] & 1 ) {
233 rSet.update( key_type( arrData[i], m_nThreadNo ), f, true );
237 getTest().m_nInsThreadCount.fetch_sub( 1, atomics::memory_order_release );
242 bool operator()( key_type const& k1, key_type const& k2 ) const
244 return k1.nKey == k2.nKey;
246 bool operator()( size_t k1, key_type const& k2 ) const
248 return k1 == k2.nKey;
250 bool operator()( key_type const& k1, size_t k2 ) const
252 return k1.nKey == k2;
254 bool operator ()( key_value_pair const& k1, key_value_pair const& k2 ) const
256 return operator()( k1.key, k2.key );
258 bool operator ()( key_value_pair const& k1, key_type const& k2 ) const
260 return operator()( k1.key, k2 );
262 bool operator ()( key_type const& k1, key_value_pair const& k2 ) const
264 return operator()( k1, k2.key );
266 bool operator ()( key_value_pair const& k1, size_t k2 ) const
268 return operator()( k1.key, k2 );
270 bool operator ()( size_t k1, key_value_pair const& k2 ) const
272 return operator()( k1, k2.key );
277 bool operator()( key_type const& k1, key_type const& k2 ) const
279 return k1.nKey < k2.nKey;
281 bool operator()( size_t k1, key_type const& k2 ) const
285 bool operator()( key_type const& k1, size_t k2 ) const
289 bool operator ()( key_value_pair const& k1, key_value_pair const& k2 ) const
291 return operator()( k1.key, k2.key );
293 bool operator ()( key_value_pair const& k1, key_type const& k2 ) const
295 return operator()( k1.key, k2 );
297 bool operator ()( key_type const& k1, key_value_pair const& k2 ) const
299 return operator()( k1, k2.key );
301 bool operator ()( key_value_pair const& k1, size_t k2 ) const
303 return operator()( k1.key, k2 );
305 bool operator ()( size_t k1, key_value_pair const& k2 ) const
307 return operator()( k1, k2.key );
310 typedef key_equal equal_to;
313 // Deletes odd keys from [0..N)
315 class DeleteThread: public CppUnitMini::TestThread
319 virtual DeleteThread * clone()
321 return new DeleteThread( *this );
324 size_t m_nDeleteSuccess;
325 size_t m_nDeleteFailed;
328 DeleteThread( CppUnitMini::ThreadPool& pool, Set& rMap )
329 : CppUnitMini::TestThread( pool )
332 DeleteThread( DeleteThread& src )
333 : CppUnitMini::TestThread( src )
337 Set_DelOdd& getTest()
339 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
342 virtual void init() { cds::threading::Manager::attachThread() ; }
343 virtual void fini() { cds::threading::Manager::detachThread() ; }
345 template <typename SetType, bool>
347 static bool erase( SetType& s, size_t key, size_t /*thread*/)
349 return s.erase_with( key, key_less() );
353 template <typename SetType>
354 struct eraser<SetType, true> {
355 static bool erase(SetType& s, size_t key, size_t thread)
357 return s.erase( key_type(key, thread));
368 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
369 std::vector<size_t>& arrData = getTest().m_arrData;
371 if ( m_nThreadNo & 1 ) {
372 for (size_t i = 0; i < arrData.size(); ++i) {
373 if (arrData[i] & 1) {
374 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
375 if ( eraser<Set, Set::c_bEraseExactKey>::erase( rSet, arrData[i], k ))
381 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
386 for (size_t i = arrData.size() - 1; i > 0; --i) {
387 if (arrData[i] & 1) {
388 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
389 if (eraser<Set, Set::c_bEraseExactKey>::erase(rSet, arrData[i], k))
395 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
402 // Extracts odd keys from [0..N)
403 template <typename GC, class Set>
404 class ExtractThread: public CppUnitMini::TestThread
408 virtual ExtractThread * clone()
410 return new ExtractThread( *this );
413 size_t m_nExtractSuccess;
414 size_t m_nExtractFailed;
417 ExtractThread( CppUnitMini::ThreadPool& pool, Set& rMap )
418 : CppUnitMini::TestThread( pool )
421 ExtractThread( ExtractThread& src )
422 : CppUnitMini::TestThread( src )
426 Set_DelOdd& getTest()
428 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
431 virtual void init() { cds::threading::Manager::attachThread() ; }
432 virtual void fini() { cds::threading::Manager::detachThread() ; }
434 template <typename SetType, bool>
436 static typename SetType::guarded_ptr extract(SetType& s, size_t key, size_t /*thread*/)
438 return s.extract_with( key, key_less());
442 template <typename SetType>
443 struct extractor<SetType, true> {
444 static typename SetType::guarded_ptr extract(SetType& s, size_t key, size_t thread)
446 return s.extract( key_type(key, thread));
455 m_nExtractFailed = 0;
457 typename Set::guarded_ptr gp;
459 std::vector<size_t>& arrData = getTest().m_arrData;
460 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
462 if ( m_nThreadNo & 1 ) {
463 for ( size_t i = 0; i < arrData.size(); ++i ) {
464 if (arrData[i] & 1) {
465 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
466 gp = extractor<Set, Set::c_bEraseExactKey>::extract( rSet, arrData[i], k );
474 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
479 for (size_t i = arrData.size() - 1; i > 0; --i) {
480 if (arrData[i] & 1) {
481 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
482 gp = extractor<Set, Set::c_bEraseExactKey>::extract( rSet, arrData[i], k);
490 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
497 template <typename RCU, class Set>
498 class ExtractThread< cds::urcu::gc<RCU>, Set >: public CppUnitMini::TestThread
502 virtual ExtractThread * clone()
504 return new ExtractThread( *this );
507 size_t m_nExtractSuccess;
508 size_t m_nExtractFailed;
511 ExtractThread( CppUnitMini::ThreadPool& pool, Set& rMap )
512 : CppUnitMini::TestThread( pool )
515 ExtractThread( ExtractThread& src )
516 : CppUnitMini::TestThread( src )
520 Set_DelOdd& getTest()
522 return reinterpret_cast<Set_DelOdd&>( m_Pool.m_Test );
525 virtual void init() { cds::threading::Manager::attachThread() ; }
526 virtual void fini() { cds::threading::Manager::detachThread() ; }
528 template <typename SetType, bool>
530 static typename SetType::exempt_ptr extract(SetType& s, size_t key, size_t /*thread*/)
532 return s.extract_with(key, key_less());
536 template <typename SetType>
537 struct extractor<SetType, true> {
538 static typename SetType::exempt_ptr extract(SetType& s, size_t key, size_t thread)
540 return s.extract(key_type(key, thread));
549 m_nExtractFailed = 0;
551 typename Set::exempt_ptr xp;
553 std::vector<size_t>& arrData = getTest().m_arrData;
554 size_t const nInsThreadCount = getTest().c_nInsThreadCount;
556 if ( m_nThreadNo & 1 ) {
557 for (size_t i = 0; i < arrData.size(); ++i) {
558 if (arrData[i] & 1) {
559 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
560 if ( Set::c_bExtractLockExternal ) {
561 typename Set::rcu_lock l;
562 xp = extractor<Set, Set::c_bEraseExactKey>::extract( rSet, arrData[i], k);
569 xp = extractor<Set, Set::c_bEraseExactKey>::extract(rSet, arrData[i], k);
578 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
583 for (size_t i = arrData.size() - 1; i > 0; --i) {
584 if (arrData[i] & 1) {
585 for ( size_t k = 0; k < nInsThreadCount; ++k ) {
586 if ( Set::c_bExtractLockExternal ) {
587 typename Set::rcu_lock l;
588 xp = extractor<Set, Set::c_bEraseExactKey>::extract(rSet, arrData[i], k);
595 xp = extractor<Set, Set::c_bEraseExactKey>::extract(rSet, arrData[i], k);
604 if ( getTest().m_nInsThreadCount.load( atomics::memory_order_acquire ) == 0 )
613 void do_test( size_t nLoadFactor )
615 Set testSet( c_nSetSize, nLoadFactor );
616 do_test_with( testSet );
621 void do_test_extract( size_t nLoadFactor )
623 Set testSet( c_nSetSize, nLoadFactor );
624 do_test_extract_with( testSet );
629 void do_test_with( Set& testSet )
631 typedef InsertThread<Set> insert_thread;
632 typedef DeleteThread<Set> delete_thread;
634 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
636 CppUnitMini::ThreadPool pool( *this );
637 pool.add( new insert_thread( pool, testSet ), c_nInsThreadCount );
638 pool.add( new delete_thread( pool, testSet ), c_nDelThreadCount ? c_nDelThreadCount : cds::OS::topology::processor_count());
640 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
642 size_t nInsertSuccess = 0;
643 size_t nInsertFailed = 0;
644 size_t nDeleteSuccess = 0;
645 size_t nDeleteFailed = 0;
646 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
647 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
649 nInsertSuccess += pThread->m_nInsertSuccess;
650 nInsertFailed += pThread->m_nInsertFailed;
653 delete_thread * p = static_cast<delete_thread *>( *it );
654 nDeleteSuccess += p->m_nDeleteSuccess;
655 nDeleteFailed += p->m_nDeleteFailed;
659 CPPUNIT_CHECK( nInsertSuccess == c_nSetSize * c_nInsThreadCount );
660 CPPUNIT_CHECK( nInsertFailed == 0 );
662 CPPUNIT_MSG( " Totals (success/failed): \n\t"
663 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
664 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
669 void do_test_extract_with( Set& testSet )
671 typedef InsertThread<Set> insert_thread;
672 typedef DeleteThread<Set> delete_thread;
673 typedef ExtractThread< typename Set::gc, Set > extract_thread;
675 m_nInsThreadCount.store( c_nInsThreadCount, atomics::memory_order_release );
677 CppUnitMini::ThreadPool pool( *this );
678 pool.add( new insert_thread( pool, testSet ), c_nInsThreadCount );
679 if ( c_nDelThreadCount )
680 pool.add( new delete_thread( pool, testSet ), c_nDelThreadCount );
681 if ( c_nExtractThreadCount )
682 pool.add( new extract_thread( pool, testSet ), c_nExtractThreadCount );
684 CPPUNIT_MSG( " Duration=" << pool.avgDuration() );
686 size_t nInsertSuccess = 0;
687 size_t nInsertFailed = 0;
688 size_t nDeleteSuccess = 0;
689 size_t nDeleteFailed = 0;
690 size_t nExtractSuccess = 0;
691 size_t nExtractFailed = 0;
692 for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
693 insert_thread * pThread = dynamic_cast<insert_thread *>( *it );
695 nInsertSuccess += pThread->m_nInsertSuccess;
696 nInsertFailed += pThread->m_nInsertFailed;
699 delete_thread * p = dynamic_cast<delete_thread *>( *it );
701 nDeleteSuccess += p->m_nDeleteSuccess;
702 nDeleteFailed += p->m_nDeleteFailed;
705 extract_thread * pExt = dynamic_cast<extract_thread *>( *it );
707 nExtractSuccess += pExt->m_nExtractSuccess;
708 nExtractFailed += pExt->m_nExtractFailed;
713 CPPUNIT_CHECK( nInsertSuccess == c_nSetSize * c_nInsThreadCount );
714 CPPUNIT_CHECK( nInsertFailed == 0 );
716 CPPUNIT_MSG( " Totals (success/failed): \n\t"
717 << " Insert=" << nInsertSuccess << '/' << nInsertFailed << "\n\t"
718 << " Delete=" << nDeleteSuccess << '/' << nDeleteFailed << "\n\t"
719 << " Extract=" << nExtractSuccess << '/' << nExtractFailed << "\n\t"
723 template <typename Set>
724 void analyze( Set& testSet )
726 // All even keys must be in the set
728 CPPUNIT_MSG( " Check even keys..." );
729 size_t nErrorCount = 0;
730 for ( size_t n = 0; n < c_nSetSize; n +=2 ) {
731 for ( size_t i = 0; i < c_nInsThreadCount; ++i ) {
732 if ( !testSet.contains( key_type(n, i) ) ) {
733 if ( ++nErrorCount < 10 ) {
734 CPPUNIT_MSG( "key " << n << "-" << i << " is not found!");
739 CPPUNIT_CHECK_EX( nErrorCount == 0, "Totals: " << nErrorCount << " keys is not found");
742 check_before_clear( testSet );
744 CPPUNIT_MSG( " Clear map (single-threaded)..." );
745 cds::OS::Timer timer;
747 CPPUNIT_MSG( " Duration=" << timer.duration() );
748 CPPUNIT_CHECK_EX( testSet.empty(), ((long long) testSet.size()) );
750 additional_check( testSet );
751 print_stat( testSet );
752 additional_cleanup( testSet );
758 static_assert( !Set::c_bExtractSupported, "Set class must not support extract() method" );
760 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
761 << " delete thread count=" << c_nDelThreadCount
762 << " set size=" << c_nSetSize
765 if ( Set::c_bLoadFactorDepended ) {
766 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
767 CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
769 Set testSet( *this );
770 do_test_with( testSet );
773 if ( c_bPrintGCState )
778 Set testSet( *this );
779 do_test_with( testSet );
782 if ( c_bPrintGCState )
788 void run_test_extract()
790 static_assert( Set::c_bExtractSupported, "Set class must support extract() method" );
792 CPPUNIT_MSG( "Insert thread count=" << c_nInsThreadCount
793 << " delete thread count=" << c_nDelThreadCount
794 << " extract thread count=" << c_nExtractThreadCount
795 << " set size=" << c_nSetSize
798 if ( Set::c_bLoadFactorDepended ) {
799 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
800 CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
802 Set testSet( *this );
803 do_test_extract_with( testSet );
806 if ( c_bPrintGCState )
811 Set testSet( *this );
812 do_test_extract_with( testSet );
815 if ( c_bPrintGCState )
820 void setUpParams( const CppUnitMini::TestCfg& cfg );
821 virtual void endTestCase();
823 # include "set2/set_defs.h"
824 CDSUNIT_DECLARE_MichaelSet
825 CDSUNIT_DECLARE_SplitList
826 CDSUNIT_DECLARE_SkipListSet
827 CDSUNIT_DECLARE_EllenBinTreeSet
828 CDSUNIT_DECLARE_CuckooSet
829 CDSUNIT_DECLARE_FeldmanHashSet_fixed
830 CDSUNIT_DECLARE_FeldmanHashSet_city
832 CPPUNIT_TEST_SUITE_(Set_DelOdd, "Map_DelOdd")
833 CDSUNIT_TEST_MichaelSet
834 CDSUNIT_TEST_SplitList
835 CDSUNIT_TEST_SkipListSet
836 CDSUNIT_TEST_EllenBinTreeSet
837 CDSUNIT_TEST_FeldmanHashSet_fixed
838 CDSUNIT_TEST_FeldmanHashSet_city
839 CDSUNIT_TEST_CuckooSet
840 CPPUNIT_TEST_SUITE_END();