MultiLevelHashSet test, bugfixing
[libcds.git] / tests / unit / map2 / map_insdel_func.h
1 //$$CDS-header$$
2
3 #include <functional>
4 #include <mutex>    //unique_lock
5 #include "map2/map_type.h"
6 #include "cppunit/thread.h"
7
8 #include <cds/sync/spinlock.h>
9 #include <vector>
10
11 namespace map2 {
12
13 #define TEST_CASE(TAG, X)  void X();
14
15     class Map_InsDel_func: public CppUnitMini::TestCase
16     {
17     public:
18         size_t c_nMapSize = 1000000;      // map size
19         size_t c_nInsertThreadCount = 4;  // count of insertion thread
20         size_t c_nDeleteThreadCount = 4;  // count of deletion thread
21         size_t c_nUpdateThreadCount = 4;  // count of updating thread
22         size_t c_nThreadPassCount   = 4;  // pass count for each thread
23         size_t c_nMaxLoadFactor = 8;      // maximum load factor
24         bool   c_bPrintGCState = true;
25
26         size_t c_nCuckooInitialSize = 1024;// initial size for CuckooMap
27         size_t c_nCuckooProbesetSize = 16; // CuckooMap probeset size (only for list-based probeset)
28         size_t c_nCuckooProbesetThreshold = 0; // CUckooMap probeset threshold (o - use default)
29
30         size_t c_nMultiLevelMap_HeadBits = 10;
31         size_t c_nMultiLevelMap_ArrayBits = 4;
32
33         size_t  c_nLoadFactor;  // current load factor
34
35     private:
36         typedef size_t  key_type;
37         struct value_type {
38             size_t      nKey;
39             size_t      nData;
40             atomics::atomic<size_t> nUpdateCall;
41             atomics::atomic<bool>   bInitialized;
42             cds::OS::ThreadId          threadId     ;   // insert thread id
43
44             typedef cds::sync::spin_lock< cds::backoff::pause > lock_type;
45             mutable lock_type   m_access;
46
47             value_type()
48                 : nKey(0)
49                 , nData(0)
50                 , nUpdateCall(0)
51                 , bInitialized( false )
52                 , threadId( cds::OS::get_current_thread_id() )
53             {}
54
55             value_type( value_type const& s )
56                 : nKey(s.nKey)
57                 , nData(s.nData)
58                 , nUpdateCall(s.nUpdateCall.load(atomics::memory_order_relaxed))
59                 , bInitialized( s.bInitialized.load(atomics::memory_order_relaxed) )
60                 , threadId( cds::OS::get_current_thread_id() )
61             {}
62
63             // boost::container::flat_map requires operator =
64             value_type& operator=( value_type const& v )
65             {
66                 nKey = v.nKey;
67                 nData = v.nData;
68                 nUpdateCall.store( v.nUpdateCall.load(atomics::memory_order_relaxed), atomics::memory_order_relaxed );
69                 bInitialized.store(v.bInitialized.load(atomics::memory_order_relaxed), atomics::memory_order_relaxed);
70
71                 return *this;
72             }
73         };
74
75         typedef std::vector<key_type>   key_array;
76         key_array                       m_arrValues;
77
78         template <class Map>
79         class Inserter: public CppUnitMini::TestThread
80         {
81             Map&     m_Map;
82
83             virtual Inserter *    clone()
84             {
85                 return new Inserter( *this );
86             }
87
88             struct insert_functor {
89                 size_t nTestFunctorRef;
90
91                 insert_functor()
92                     : nTestFunctorRef(0)
93                 {}
94
95                 template <typename Pair>
96                 void operator()( Pair& val )
97                 {
98                     operator()( val.first, val.second );
99                 }
100
101                 template <typename Key, typename Val >
102                 void operator()( Key const& key, Val& v )
103                 {
104                     std::unique_lock< typename value_type::lock_type>    ac( v.m_access );
105
106                     v.nKey  = key;
107                     v.nData = key * 8;
108
109                     ++nTestFunctorRef;
110                     v.bInitialized.store( true, atomics::memory_order_relaxed);
111                 }
112             };
113
114         public:
115             size_t  m_nInsertSuccess;
116             size_t  m_nInsertFailed;
117
118             size_t  m_nTestFunctorRef;
119
120         public:
121             Inserter( CppUnitMini::ThreadPool& pool, Map& rMap )
122                 : CppUnitMini::TestThread( pool )
123                 , m_Map( rMap )
124             {}
125             Inserter( Inserter& src )
126                 : CppUnitMini::TestThread( src )
127                 , m_Map( src.m_Map )
128             {}
129
130             Map_InsDel_func&  getTest()
131             {
132                 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
133             }
134
135             virtual void init() { cds::threading::Manager::attachThread()   ; }
136             virtual void fini() { cds::threading::Manager::detachThread()   ; }
137
138             virtual void test()
139             {
140                 Map& rMap = m_Map;
141
142                 m_nInsertSuccess =
143                     m_nInsertFailed =
144                     m_nTestFunctorRef = 0;
145
146                 // func is passed by reference
147                 insert_functor  func;
148                 key_array const& arr = getTest().m_arrValues;
149                 size_t const nPassCount = getTest().c_nThreadPassCount;
150
151                 if ( m_nThreadNo & 1 ) {
152                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
153                         for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
154                             if ( rMap.insert_with( *it, std::ref(func) ) )
155                                 ++m_nInsertSuccess;
156                             else
157                                 ++m_nInsertFailed;
158                         }
159                     }
160                 }
161                 else {
162                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
163                         for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
164                             if ( rMap.insert_with( *it, std::ref(func) ) )
165                                 ++m_nInsertSuccess;
166                             else
167                                 ++m_nInsertFailed;
168                         }
169                     }
170                 }
171
172                 m_nTestFunctorRef = func.nTestFunctorRef;
173             }
174         };
175
176         template <class Map>
177         class Updater: public CppUnitMini::TestThread
178         {
179             Map&     m_Map;
180
181             virtual Updater *    clone()
182             {
183                 return new Updater( *this );
184             }
185
186             struct update_functor {
187                 size_t  nCreated;
188                 size_t  nModified;
189
190                 update_functor()
191                     : nCreated(0)
192                     , nModified(0)
193                 {}
194
195                 template <typename Key, typename Val>
196                 void operator()( bool bNew, Key const& key, Val& v )
197                 {
198                     std::unique_lock<typename value_type::lock_type>    ac( v.m_access );
199                     if ( bNew ) {
200                         ++nCreated;
201                         v.nKey = key;
202                         v.nData = key * 8;
203                         v.bInitialized.store( true, atomics::memory_order_relaxed);
204                     }
205                     else {
206                         v.nUpdateCall.fetch_add( 1, atomics::memory_order_relaxed );
207                         ++nModified;
208                     }
209                 }
210
211                 template <typename Pair>
212                 void operator()( bool bNew, Pair& val )
213                 {
214                     operator()( bNew, val.first, val.second );
215                 }
216
217                 // For MultiLevelHashMap
218                 template <typename Val>
219                 void operator()( Val& cur, Val * old )
220                 {
221                     operator()( old != nullptr, cur.first, cur.second );
222                 }
223
224             private:
225                 update_functor(const update_functor& ) = delete;
226             };
227
228         public:
229             size_t  m_nUpdateFailed;
230             size_t  m_nUpdateCreated;
231             size_t  m_nUpdateExisted;
232             size_t  m_nFunctorCreated;
233             size_t  m_nFunctorModified;
234
235         public:
236             Updater( CppUnitMini::ThreadPool& pool, Map& rMap )
237                 : CppUnitMini::TestThread( pool )
238                 , m_Map( rMap )
239             {}
240             Updater( Updater& src )
241                 : CppUnitMini::TestThread( src )
242                 , m_Map( src.m_Map )
243             {}
244
245             Map_InsDel_func&  getTest()
246             {
247                 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
248             }
249
250             virtual void init() { cds::threading::Manager::attachThread()   ; }
251             virtual void fini() { cds::threading::Manager::detachThread()   ; }
252
253             virtual void test()
254             {
255                 Map& rMap = m_Map;
256
257                 m_nUpdateCreated =
258                     m_nUpdateExisted =
259                     m_nUpdateFailed = 0;
260
261                 update_functor func;
262
263                 key_array const& arr = getTest().m_arrValues;
264                 size_t const nPassCount = getTest().c_nThreadPassCount;
265
266                 if ( m_nThreadNo & 1 ) {
267                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
268                         for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
269                         //for ( size_t nItem = 0; nItem < c_nMapSize; ++nItem ) {
270                             std::pair<bool, bool> ret = rMap.update( *it, std::ref( func ) );
271                             if ( ret.first  ) {
272                                 if ( ret.second )
273                                     ++m_nUpdateCreated;
274                                 else
275                                     ++m_nUpdateExisted;
276                             }
277                             else
278                                 ++m_nUpdateFailed;
279                         }
280                     }
281                 }
282                 else {
283                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
284                         for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
285                             std::pair<bool, bool> ret = rMap.update( *it, std::ref( func ) );
286                             if ( ret.first  ) {
287                                 if ( ret.second )
288                                     ++m_nUpdateCreated;
289                                 else
290                                     ++m_nUpdateExisted;
291                             }
292                             else
293                                 ++m_nUpdateFailed;
294                         }
295                     }
296                 }
297
298                 m_nFunctorCreated = func.nCreated;
299                 m_nFunctorModified = func.nModified;
300             }
301         };
302
303         template <class Map>
304         class Deleter: public CppUnitMini::TestThread
305         {
306             Map&     m_Map;
307             typedef typename Map::mapped_type value_type;
308
309             virtual Deleter *    clone()
310             {
311                 return new Deleter( *this );
312             }
313
314             struct value_container
315             {
316                 size_t      nKeyExpected;
317
318                 size_t      nSuccessItem;
319                 size_t      nFailedItem;
320
321                 value_container()
322                     : nSuccessItem(0)
323                     , nFailedItem(0)
324                 {}
325             };
326
327             struct erase_functor {
328                 value_container     m_cnt;
329
330                 template <typename Key, typename Val>
331                 void operator()( Key const& /*key*/, Val& v )
332                 {
333                     while ( true ) {
334                         if ( v.bInitialized.load( atomics::memory_order_relaxed )) {
335                             std::unique_lock< typename value_type::lock_type>    ac( v.m_access );
336
337                             if ( m_cnt.nKeyExpected == v.nKey && m_cnt.nKeyExpected * 8 == v.nData )
338                                 ++m_cnt.nSuccessItem;
339                             else
340                                 ++m_cnt.nFailedItem;
341                             v.nData++;
342                             v.nKey = 0;
343                             break;
344                         }
345                         else
346                             cds::backoff::yield()();
347                     }
348                 }
349
350                 template <typename Pair>
351                 void operator ()( Pair& item )
352                 {
353                     operator()( item.first, item.second );
354                 }
355             };
356
357         public:
358             size_t  m_nDeleteSuccess;
359             size_t  m_nDeleteFailed;
360
361             size_t  m_nValueSuccess;
362             size_t  m_nValueFailed;
363
364         public:
365             Deleter( CppUnitMini::ThreadPool& pool, Map& rMap )
366                 : CppUnitMini::TestThread( pool )
367                 , m_Map( rMap )
368             {}
369             Deleter( Deleter& src )
370                 : CppUnitMini::TestThread( src )
371                 , m_Map( src.m_Map )
372             {}
373
374             Map_InsDel_func&  getTest()
375             {
376                 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
377             }
378
379             virtual void init() { cds::threading::Manager::attachThread()   ; }
380             virtual void fini() { cds::threading::Manager::detachThread()   ; }
381
382             virtual void test()
383             {
384                 Map& rMap = m_Map;
385
386                 m_nDeleteSuccess =
387                     m_nDeleteFailed = 0;
388
389                 erase_functor   func;
390                 key_array const& arr = getTest().m_arrValues;
391                 size_t const nPassCount = getTest().c_nThreadPassCount;
392
393                 if ( m_nThreadNo & 1 ) {
394                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
395                         for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
396                             func.m_cnt.nKeyExpected = *it;
397                             if ( rMap.erase( *it, std::ref(func) ))
398                                 ++m_nDeleteSuccess;
399                             else
400                                 ++m_nDeleteFailed;
401                         }
402                     }
403                 }
404                 else {
405                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
406                         for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
407                             func.m_cnt.nKeyExpected = *it;
408                             if ( rMap.erase( *it, std::ref(func) ))
409                                 ++m_nDeleteSuccess;
410                             else
411                                 ++m_nDeleteFailed;
412                         }
413                     }
414                 }
415
416                 m_nValueSuccess = func.m_cnt.nSuccessItem;
417                 m_nValueFailed = func.m_cnt.nFailedItem;
418             }
419         };
420
421     protected:
422
423         template <class Map>
424         void do_test( Map& testMap )
425         {
426             typedef Inserter<Map>       InserterThread;
427             typedef Deleter<Map>        DeleterThread;
428             typedef Updater<Map>        UpdaterThread;
429             cds::OS::Timer    timer;
430
431             m_arrValues.clear();
432             m_arrValues.reserve( c_nMapSize );
433             for ( size_t i = 0; i < c_nMapSize; ++i )
434                 m_arrValues.push_back( i );
435             shuffle( m_arrValues.begin(), m_arrValues.end() );
436
437             CppUnitMini::ThreadPool pool( *this );
438             pool.add( new InserterThread( pool, testMap ), c_nInsertThreadCount );
439             pool.add( new DeleterThread( pool, testMap ), c_nDeleteThreadCount );
440             pool.add( new UpdaterThread( pool, testMap ), c_nUpdateThreadCount );
441             pool.run();
442             CPPUNIT_MSG( "   Duration=" << pool.avgDuration() );
443
444             size_t nInsertSuccess = 0;
445             size_t nInsertFailed = 0;
446             size_t nDeleteSuccess = 0;
447             size_t nDeleteFailed = 0;
448             size_t nDelValueSuccess = 0;
449             size_t nDelValueFailed = 0;
450             size_t nUpdateFailed = 0;
451             size_t nUpdateCreated = 0;
452             size_t nUpdateModified = 0;
453             size_t nEnsFuncCreated = 0;
454             size_t nEnsFuncModified = 0;
455             size_t nTestFunctorRef = 0;
456
457             for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
458                 InserterThread * pThread = dynamic_cast<InserterThread *>( *it );
459                 if ( pThread ) {
460                     nInsertSuccess += pThread->m_nInsertSuccess;
461                     nInsertFailed += pThread->m_nInsertFailed;
462                     nTestFunctorRef += pThread->m_nTestFunctorRef;
463                 }
464                 else {
465                     DeleterThread * p = dynamic_cast<DeleterThread *>( *it );
466                     if ( p ) {
467                         nDeleteSuccess += p->m_nDeleteSuccess;
468                         nDeleteFailed += p->m_nDeleteFailed;
469                         nDelValueSuccess += p->m_nValueSuccess;
470                         nDelValueFailed += p->m_nValueFailed;
471                     }
472                     else {
473                         UpdaterThread * pEns = static_cast<UpdaterThread *>( *it );
474                         nUpdateCreated += pEns->m_nUpdateCreated;
475                         nUpdateModified += pEns->m_nUpdateExisted;
476                         nUpdateFailed += pEns->m_nUpdateFailed;
477                         nEnsFuncCreated += pEns->m_nFunctorCreated;
478                         nEnsFuncModified += pEns->m_nFunctorModified;
479                     }
480                 }
481             }
482
483             CPPUNIT_MSG( "    Totals: Ins succ=" << nInsertSuccess
484                 << " Del succ=" << nDeleteSuccess << "\n"
485                 << "          : Ins fail=" << nInsertFailed
486                 << " Del fail=" << nDeleteFailed << "\n"
487                 << "          : Update succ=" << (nUpdateCreated + nUpdateModified) << " fail=" << nUpdateFailed
488                 << " create=" << nUpdateCreated << " modify=" << nUpdateModified << "\n"
489                 << "          Map size=" << testMap.size()
490                 );
491
492             CPPUNIT_CHECK_EX( nDelValueFailed == 0, "Functor del failed=" << nDelValueFailed );
493             CPPUNIT_CHECK_EX( nDelValueSuccess == nDeleteSuccess,  "Delete success=" << nDeleteSuccess << " functor=" << nDelValueSuccess );
494
495             CPPUNIT_CHECK( nUpdateFailed == 0 );
496
497             CPPUNIT_CHECK_EX( nUpdateCreated == nEnsFuncCreated, "Update created=" << nUpdateCreated << " functor=" << nEnsFuncCreated );
498             CPPUNIT_CHECK_EX( nUpdateModified == nEnsFuncModified, "Update modified=" << nUpdateModified << " functor=" << nEnsFuncModified );
499
500             // nTestFunctorRef is call count of insert functor
501             CPPUNIT_CHECK_EX( nTestFunctorRef == nInsertSuccess, "nInsertSuccess=" << nInsertSuccess << " functor nTestFunctorRef=" << nTestFunctorRef );
502
503             check_before_cleanup( testMap );
504
505             CPPUNIT_MSG( "  Clear map (single-threaded)..." );
506             timer.reset();
507             for ( size_t nItem = 0; nItem < c_nMapSize; ++nItem ) {
508                 testMap.erase( nItem );
509             }
510             CPPUNIT_MSG( "   Duration=" << timer.duration() );
511             CPPUNIT_CHECK( testMap.empty() );
512
513             additional_check( testMap );
514             print_stat( testMap );
515             additional_cleanup( testMap );
516         }
517
518         template <class Map>
519         void run_test()
520         {
521             CPPUNIT_MSG( "Thread count: insert=" << c_nInsertThreadCount
522                 << " delete=" << c_nDeleteThreadCount
523                 << " update=" << c_nUpdateThreadCount
524                 << " pass count=" << c_nThreadPassCount
525                 << " map size=" << c_nMapSize
526                 );
527
528             if ( Map::c_bLoadFactorDepended ) {
529                 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
530                     CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
531                     Map  testMap( *this );
532                     do_test( testMap );
533                     if ( c_bPrintGCState )
534                         print_gc_state();
535                 }
536             }
537             else {
538                 Map testMap( *this );
539                 do_test( testMap );
540                 if ( c_bPrintGCState )
541                     print_gc_state();
542             }
543         }
544
545         void setUpParams( const CppUnitMini::TestCfg& cfg );
546
547 #   include "map2/map_defs.h"
548         CDSUNIT_DECLARE_MichaelMap
549         CDSUNIT_DECLARE_SplitList
550         CDSUNIT_DECLARE_SkipListMap
551         CDSUNIT_DECLARE_EllenBinTreeMap
552         CDSUNIT_DECLARE_BronsonAVLTreeMap
553         CDSUNIT_DECLARE_MultiLevelHashMap_fixed
554         CDSUNIT_DECLARE_MultiLevelHashMap_city
555         CDSUNIT_DECLARE_StripedMap
556         CDSUNIT_DECLARE_RefinableMap
557         CDSUNIT_DECLARE_CuckooMap
558
559         CPPUNIT_TEST_SUITE(Map_InsDel_func)
560             CDSUNIT_TEST_MichaelMap
561             CDSUNIT_TEST_SplitList
562             CDSUNIT_TEST_SkipListMap
563             CDSUNIT_TEST_EllenBinTreeMap
564             CDSUNIT_TEST_BronsonAVLTreeMap
565             CDSUNIT_TEST_MultiLevelHashMap_fixed
566             CDSUNIT_TEST_MultiLevelHashMap_city
567             CDSUNIT_TEST_CuckooMap
568             CDSUNIT_TEST_StripedMap
569             CDSUNIT_TEST_RefinableMap
570         CPPUNIT_TEST_SUITE_END();
571
572     };
573 } // namespace map2