3b98c5789e3dd561166f46e73a8d6a332c719525
[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_nFeldmanMap_HeadBits = 10;
31         size_t c_nFeldmanMap_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             size_t      nUpdateCall;
41             atomics::atomic<bool>   bInitialized;
42             cds::OS::ThreadId       threadId;   // inserter 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 )
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 = v.nUpdateCall;
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 ( !v.bInitialized.load( atomics::memory_order_acquire )) {
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;
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 FeldmanHashMap
218                 template <typename Val>
219                 void operator()( Val& cur, Val * old )
220                 {
221                     if ( old ) {
222                         cur.second.nKey = cur.first;
223                         cur.second.nData = cur.first * 8;
224                         cur.second.bInitialized.store( true, atomics::memory_order_release );
225                     }
226                     operator()( old == nullptr, cur.first, cur.second );
227                 }
228
229             private:
230                 update_functor(const update_functor& ) = delete;
231             };
232
233         public:
234             size_t  m_nUpdateFailed;
235             size_t  m_nUpdateCreated;
236             size_t  m_nUpdateExisted;
237             size_t  m_nFunctorCreated;
238             size_t  m_nFunctorModified;
239
240         public:
241             Updater( CppUnitMini::ThreadPool& pool, Map& rMap )
242                 : CppUnitMini::TestThread( pool )
243                 , m_Map( rMap )
244             {}
245             Updater( Updater& src )
246                 : CppUnitMini::TestThread( src )
247                 , m_Map( src.m_Map )
248             {}
249
250             Map_InsDel_func&  getTest()
251             {
252                 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
253             }
254
255             virtual void init() { cds::threading::Manager::attachThread()   ; }
256             virtual void fini() { cds::threading::Manager::detachThread()   ; }
257
258             virtual void test()
259             {
260                 Map& rMap = m_Map;
261
262                 m_nUpdateCreated =
263                     m_nUpdateExisted =
264                     m_nUpdateFailed = 0;
265
266                 update_functor func;
267
268                 key_array const& arr = getTest().m_arrValues;
269                 size_t const nPassCount = getTest().c_nThreadPassCount;
270
271                 if ( m_nThreadNo & 1 ) {
272                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
273                         for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
274                             std::pair<bool, bool> ret = rMap.update( *it, std::ref( func ));
275                             if ( ret.first  ) {
276                                 if ( ret.second )
277                                     ++m_nUpdateCreated;
278                                 else
279                                     ++m_nUpdateExisted;
280                             }
281                             else
282                                 ++m_nUpdateFailed;
283                         }
284                     }
285                 }
286                 else {
287                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
288                         for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
289                             std::pair<bool, bool> ret = rMap.update( *it, std::ref( func ));
290                             if ( ret.first  ) {
291                                 if ( ret.second )
292                                     ++m_nUpdateCreated;
293                                 else
294                                     ++m_nUpdateExisted;
295                             }
296                             else
297                                 ++m_nUpdateFailed;
298                         }
299                     }
300                 }
301
302                 m_nFunctorCreated = func.nCreated;
303                 m_nFunctorModified = func.nModified;
304             }
305         };
306
307         template <class Map>
308         class Deleter: public CppUnitMini::TestThread
309         {
310             Map&     m_Map;
311             typedef typename Map::mapped_type value_type;
312
313             virtual Deleter *    clone()
314             {
315                 return new Deleter( *this );
316             }
317
318             struct value_container
319             {
320                 size_t      nKeyExpected;
321
322                 size_t      nSuccessItem;
323                 size_t      nFailedItem;
324
325                 value_container()
326                     : nSuccessItem(0)
327                     , nFailedItem(0)
328                 {}
329             };
330
331             struct erase_functor {
332                 value_container     m_cnt;
333
334                 template <typename Key, typename Val>
335                 void operator()( Key const& /*key*/, Val& v )
336                 {
337                     while ( true ) {
338                         if ( v.bInitialized.load( atomics::memory_order_relaxed )) {
339                             std::unique_lock< typename value_type::lock_type> ac( v.m_access );
340
341                             if ( m_cnt.nKeyExpected == v.nKey && m_cnt.nKeyExpected * 8 == v.nData )
342                                 ++m_cnt.nSuccessItem;
343                             else
344                                 ++m_cnt.nFailedItem;
345                             v.nData++;
346                             v.nKey = 0;
347                             break;
348                         }
349                         else
350                             cds::backoff::yield()();
351                     }
352                 }
353
354                 template <typename Pair>
355                 void operator ()( Pair& item )
356                 {
357                     operator()( item.first, item.second );
358                 }
359             };
360
361         public:
362             size_t  m_nDeleteSuccess;
363             size_t  m_nDeleteFailed;
364
365             size_t  m_nValueSuccess;
366             size_t  m_nValueFailed;
367
368         public:
369             Deleter( CppUnitMini::ThreadPool& pool, Map& rMap )
370                 : CppUnitMini::TestThread( pool )
371                 , m_Map( rMap )
372             {}
373             Deleter( Deleter& src )
374                 : CppUnitMini::TestThread( src )
375                 , m_Map( src.m_Map )
376             {}
377
378             Map_InsDel_func&  getTest()
379             {
380                 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
381             }
382
383             virtual void init() { cds::threading::Manager::attachThread()   ; }
384             virtual void fini() { cds::threading::Manager::detachThread()   ; }
385
386             virtual void test()
387             {
388                 Map& rMap = m_Map;
389
390                 m_nDeleteSuccess =
391                     m_nDeleteFailed = 0;
392
393                 erase_functor   func;
394                 key_array const& arr = getTest().m_arrValues;
395                 size_t const nPassCount = getTest().c_nThreadPassCount;
396
397                 if ( m_nThreadNo & 1 ) {
398                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
399                         for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
400                             func.m_cnt.nKeyExpected = *it;
401                             if ( rMap.erase( *it, std::ref(func)))
402                                 ++m_nDeleteSuccess;
403                             else
404                                 ++m_nDeleteFailed;
405                         }
406                     }
407                 }
408                 else {
409                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
410                         for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
411                             func.m_cnt.nKeyExpected = *it;
412                             if ( rMap.erase( *it, std::ref(func)))
413                                 ++m_nDeleteSuccess;
414                             else
415                                 ++m_nDeleteFailed;
416                         }
417                     }
418                 }
419
420                 m_nValueSuccess = func.m_cnt.nSuccessItem;
421                 m_nValueFailed = func.m_cnt.nFailedItem;
422             }
423         };
424
425     protected:
426
427         template <class Map>
428         void do_test( Map& testMap )
429         {
430             typedef Inserter<Map>       InserterThread;
431             typedef Deleter<Map>        DeleterThread;
432             typedef Updater<Map>        UpdaterThread;
433             cds::OS::Timer    timer;
434
435             m_arrValues.clear();
436             m_arrValues.reserve( c_nMapSize );
437             for ( size_t i = 0; i < c_nMapSize; ++i )
438                 m_arrValues.push_back( i );
439             shuffle( m_arrValues.begin(), m_arrValues.end());
440
441             CppUnitMini::ThreadPool pool( *this );
442             pool.add( new InserterThread( pool, testMap ), c_nInsertThreadCount );
443             pool.add( new DeleterThread( pool, testMap ), c_nDeleteThreadCount );
444             pool.add( new UpdaterThread( pool, testMap ), c_nUpdateThreadCount );
445             pool.run();
446             CPPUNIT_MSG( "   Duration=" << pool.avgDuration());
447
448             size_t nInsertSuccess = 0;
449             size_t nInsertFailed = 0;
450             size_t nDeleteSuccess = 0;
451             size_t nDeleteFailed = 0;
452             size_t nDelValueSuccess = 0;
453             size_t nDelValueFailed = 0;
454             size_t nUpdateFailed = 0;
455             size_t nUpdateCreated = 0;
456             size_t nUpdateModified = 0;
457             size_t nEnsFuncCreated = 0;
458             size_t nEnsFuncModified = 0;
459             size_t nInsFuncCalled = 0;
460
461             for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
462                 InserterThread * pThread = dynamic_cast<InserterThread *>( *it );
463                 if ( pThread ) {
464                     nInsertSuccess += pThread->m_nInsertSuccess;
465                     nInsertFailed += pThread->m_nInsertFailed;
466                     nInsFuncCalled += pThread->m_nTestFunctorRef;
467                 }
468                 else {
469                     DeleterThread * p = dynamic_cast<DeleterThread *>( *it );
470                     if ( p ) {
471                         nDeleteSuccess += p->m_nDeleteSuccess;
472                         nDeleteFailed += p->m_nDeleteFailed;
473                         nDelValueSuccess += p->m_nValueSuccess;
474                         nDelValueFailed += p->m_nValueFailed;
475                     }
476                     else {
477                         UpdaterThread * pEns = static_cast<UpdaterThread *>( *it );
478                         nUpdateCreated += pEns->m_nUpdateCreated;
479                         nUpdateModified += pEns->m_nUpdateExisted;
480                         nUpdateFailed += pEns->m_nUpdateFailed;
481                         nEnsFuncCreated += pEns->m_nFunctorCreated;
482                         nEnsFuncModified += pEns->m_nFunctorModified;
483                     }
484                 }
485             }
486
487             CPPUNIT_MSG( "    Totals: Ins succ=" << nInsertSuccess
488                 << " Del succ=" << nDeleteSuccess << "\n"
489                 << "          : Ins fail=" << nInsertFailed
490                 << " Del fail=" << nDeleteFailed << "\n"
491                 << "          : Update succ=" << (nUpdateCreated + nUpdateModified) << " fail=" << nUpdateFailed
492                 << " create=" << nUpdateCreated << " modify=" << nUpdateModified << "\n"
493                 << "          Map size=" << testMap.size()
494                 );
495
496             CPPUNIT_CHECK_EX( nDelValueFailed == 0, "Functor del failed=" << nDelValueFailed );
497             CPPUNIT_CHECK_EX( nDelValueSuccess == nDeleteSuccess,  "Delete success=" << nDeleteSuccess << " functor=" << nDelValueSuccess );
498
499             CPPUNIT_CHECK( nUpdateFailed == 0 );
500
501             CPPUNIT_CHECK_EX( nUpdateCreated == nEnsFuncCreated, "Update created=" << nUpdateCreated << " functor=" << nEnsFuncCreated );
502             CPPUNIT_CHECK_EX( nUpdateModified == nEnsFuncModified, "Update modified=" << nUpdateModified << " functor=" << nEnsFuncModified );
503
504             // nInsFuncCalled is call count of insert functor
505             CPPUNIT_CHECK_EX( nInsFuncCalled == nInsertSuccess, "nInsertSuccess=" << nInsertSuccess << " functor nInsFuncCalled=" << nInsFuncCalled );
506
507             check_before_cleanup( testMap );
508
509             CPPUNIT_MSG( "  Clear map (single-threaded)..." );
510             timer.reset();
511             for ( size_t nItem = 0; nItem < c_nMapSize; ++nItem ) {
512                 testMap.erase( nItem );
513             }
514             CPPUNIT_MSG( "   Duration=" << timer.duration());
515             CPPUNIT_CHECK( testMap.empty());
516
517             additional_check( testMap );
518             print_stat( testMap );
519             additional_cleanup( testMap );
520         }
521
522         template <class Map>
523         void run_test()
524         {
525             CPPUNIT_MSG( "Thread count: insert=" << c_nInsertThreadCount
526                 << " delete=" << c_nDeleteThreadCount
527                 << " update=" << c_nUpdateThreadCount
528                 << " pass count=" << c_nThreadPassCount
529                 << " map size=" << c_nMapSize
530                 );
531
532             if ( Map::c_bLoadFactorDepended ) {
533                 for ( c_nLoadFactor = 1; c_nLoadFactor <= c_nMaxLoadFactor; c_nLoadFactor *= 2 ) {
534                     CPPUNIT_MSG( "Load factor=" << c_nLoadFactor );
535                     Map  testMap( *this );
536                     do_test( testMap );
537                     if ( c_bPrintGCState )
538                         print_gc_state();
539                 }
540             }
541             else {
542                 Map testMap( *this );
543                 do_test( testMap );
544                 if ( c_bPrintGCState )
545                     print_gc_state();
546             }
547         }
548
549         void setUpParams( const CppUnitMini::TestCfg& cfg );
550
551 #   include "map2/map_defs.h"
552         CDSUNIT_DECLARE_MichaelMap
553         CDSUNIT_DECLARE_SplitList
554         CDSUNIT_DECLARE_SkipListMap
555         CDSUNIT_DECLARE_EllenBinTreeMap
556         CDSUNIT_DECLARE_BronsonAVLTreeMap
557         CDSUNIT_DECLARE_FeldmanHashMap_fixed
558         CDSUNIT_DECLARE_FeldmanHashMap_city
559         CDSUNIT_DECLARE_StripedMap
560         CDSUNIT_DECLARE_RefinableMap
561         CDSUNIT_DECLARE_CuckooMap
562
563         CPPUNIT_TEST_SUITE(Map_InsDel_func)
564             CDSUNIT_TEST_MichaelMap
565             CDSUNIT_TEST_SplitList
566             CDSUNIT_TEST_SkipListMap
567             CDSUNIT_TEST_EllenBinTreeMap
568             CDSUNIT_TEST_BronsonAVLTreeMap
569             CDSUNIT_TEST_FeldmanHashMap_fixed
570             CDSUNIT_TEST_FeldmanHashMap_city
571             CDSUNIT_TEST_CuckooMap
572             CDSUNIT_TEST_StripedMap
573             CDSUNIT_TEST_RefinableMap
574         CPPUNIT_TEST_SUITE_END();
575
576     };
577 } // namespace map2