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