StripedMap: replace ensure() with update(), find(key) with contains(key)
[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 //#   define TEST_MAP(IMPL, C, X)          void C::X() { test<map_type<IMPL, key_type, value_type>::X >()    ; }
16 //#   define TEST_MAP_EXTRACT(IMPL, C, X)  TEST_MAP(IMPL, C, X)
17 //#   define TEST_MAP_NOLF(IMPL, C, X)     void C::X() { test_nolf<map_type<IMPL, key_type, value_type>::X >()    ; }
18 //#   define TEST_MAP_NOLF_EXTRACT(IMPL, C, X) TEST_MAP_NOLF(IMPL, C, X)
19
20     class Map_InsDel_func: public CppUnitMini::TestCase
21     {
22     public:
23         size_t c_nMapSize = 1000000;      // map size
24         size_t c_nInsertThreadCount = 4;  // count of insertion thread
25         size_t c_nDeleteThreadCount = 4;  // count of deletion thread
26         size_t c_nUpdateThreadCount = 4;  // count of updating thread
27         size_t c_nThreadPassCount   = 4;  // pass count for each thread
28         size_t c_nMaxLoadFactor = 8;      // maximum load factor
29         bool   c_bPrintGCState = true;
30
31         size_t c_nCuckooInitialSize = 1024;// initial size for CuckooMap
32         size_t c_nCuckooProbesetSize = 16; // CuckooMap probeset size (only for list-based probeset)
33         size_t c_nCuckooProbesetThreshold = 0; // CUckooMap probeset threshold (o - use default)
34
35         size_t c_nMultiLevelMap_HeadBits = 10;
36         size_t c_nMultiLevelMap_ArrayBits = 4;
37
38         size_t  c_nLoadFactor;  // current load factor
39
40     private:
41         typedef size_t  key_type;
42         struct value_type {
43             size_t      nKey;
44             size_t      nData;
45             atomics::atomic<size_t> nUpdateCall;
46             atomics::atomic<bool>   bInitialized;
47             cds::OS::ThreadId          threadId     ;   // insert thread id
48
49             typedef cds::sync::spin_lock< cds::backoff::pause > lock_type;
50             mutable lock_type   m_access;
51
52             value_type()
53                 : nKey(0)
54                 , nData(0)
55                 , nUpdateCall(0)
56                 , bInitialized( false )
57                 , threadId( cds::OS::get_current_thread_id() )
58             {}
59
60             value_type( value_type const& s )
61                 : nKey(s.nKey)
62                 , nData(s.nData)
63                 , nUpdateCall(s.nUpdateCall.load(atomics::memory_order_relaxed))
64                 , bInitialized( s.bInitialized.load(atomics::memory_order_relaxed) )
65                 , threadId( cds::OS::get_current_thread_id() )
66             {}
67
68             // boost::container::flat_map requires operator =
69             value_type& operator=( value_type const& v )
70             {
71                 nKey = v.nKey;
72                 nData = v.nData;
73                 nUpdateCall.store( v.nUpdateCall.load(atomics::memory_order_relaxed), atomics::memory_order_relaxed );
74                 bInitialized.store(v.bInitialized.load(atomics::memory_order_relaxed), atomics::memory_order_relaxed);
75
76                 return *this;
77             }
78         };
79
80         typedef std::vector<key_type>   key_array;
81         key_array                       m_arrValues;
82
83         template <class Map>
84         class Inserter: public CppUnitMini::TestThread
85         {
86             Map&     m_Map;
87
88             virtual Inserter *    clone()
89             {
90                 return new Inserter( *this );
91             }
92
93             struct insert_functor {
94                 size_t nTestFunctorRef;
95
96                 insert_functor()
97                     : nTestFunctorRef(0)
98                 {}
99
100                 template <typename Pair>
101                 void operator()( Pair& val )
102                 {
103                     operator()( val.first, val.second );
104                 }
105
106                 template <typename Key, typename Val >
107                 void operator()( Key const& key, Val& v )
108                 {
109                     std::unique_lock< typename value_type::lock_type>    ac( v.m_access );
110
111                     v.nKey  = key;
112                     v.nData = key * 8;
113
114                     ++nTestFunctorRef;
115                     v.bInitialized.store( true, atomics::memory_order_relaxed);
116                 }
117             };
118
119         public:
120             size_t  m_nInsertSuccess;
121             size_t  m_nInsertFailed;
122
123             size_t  m_nTestFunctorRef;
124
125         public:
126             Inserter( CppUnitMini::ThreadPool& pool, Map& rMap )
127                 : CppUnitMini::TestThread( pool )
128                 , m_Map( rMap )
129             {}
130             Inserter( Inserter& src )
131                 : CppUnitMini::TestThread( src )
132                 , m_Map( src.m_Map )
133             {}
134
135             Map_InsDel_func&  getTest()
136             {
137                 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
138             }
139
140             virtual void init() { cds::threading::Manager::attachThread()   ; }
141             virtual void fini() { cds::threading::Manager::detachThread()   ; }
142
143             virtual void test()
144             {
145                 Map& rMap = m_Map;
146
147                 m_nInsertSuccess =
148                     m_nInsertFailed =
149                     m_nTestFunctorRef = 0;
150
151                 // func is passed by reference
152                 insert_functor  func;
153                 key_array const& arr = getTest().m_arrValues;
154                 size_t const nPassCount = getTest().c_nThreadPassCount;
155
156                 if ( m_nThreadNo & 1 ) {
157                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
158                         for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
159                             if ( rMap.insert_with( *it, std::ref(func) ) )
160                                 ++m_nInsertSuccess;
161                             else
162                                 ++m_nInsertFailed;
163                         }
164                     }
165                 }
166                 else {
167                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
168                         for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
169                             if ( rMap.insert_with( *it, std::ref(func) ) )
170                                 ++m_nInsertSuccess;
171                             else
172                                 ++m_nInsertFailed;
173                         }
174                     }
175                 }
176
177                 m_nTestFunctorRef = func.nTestFunctorRef;
178             }
179         };
180
181         template <class Map>
182         class Updater: public CppUnitMini::TestThread
183         {
184             Map&     m_Map;
185
186             virtual Updater *    clone()
187             {
188                 return new Updater( *this );
189             }
190
191             struct update_functor {
192                 size_t  nCreated;
193                 size_t  nModified;
194
195                 update_functor()
196                     : nCreated(0)
197                     , nModified(0)
198                 {}
199
200                 template <typename Key, typename Val>
201                 void operator()( bool bNew, Key const& key, Val& v )
202                 {
203                     std::unique_lock<typename value_type::lock_type>    ac( v.m_access );
204                     if ( bNew ) {
205                         ++nCreated;
206                         v.nKey = key;
207                         v.nData = key * 8;
208                         v.bInitialized.store( true, atomics::memory_order_relaxed);
209                     }
210                     else {
211                         v.nUpdateCall.fetch_add( 1, atomics::memory_order_relaxed );
212                         ++nModified;
213                     }
214                 }
215
216                 template <typename Pair>
217                 void operator()( bool bNew, Pair& val )
218                 {
219                     operator()( bNew, val.first, val.second );
220                 }
221
222                 // For MultiLevelHashMap
223                 template <typename Val>
224                 void operator()( Val& cur, Val * old )
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                         //for ( size_t nItem = 0; nItem < c_nMapSize; ++nItem ) {
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 nTestFunctorRef = 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                     nTestFunctorRef += 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             // nTestFunctorRef is call count of insert functor
506             CPPUNIT_CHECK_EX( nTestFunctorRef == nInsertSuccess, "nInsertSuccess=" << nInsertSuccess << " functor nTestFunctorRef=" << nTestFunctorRef );
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 ( size_t 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         typedef CppUnitMini::TestCase Base;
551         void setUpParams( const CppUnitMini::TestCfg& cfg );
552
553 #   include "map2/map_defs.h"
554         CDSUNIT_DECLARE_MichaelMap
555         CDSUNIT_DECLARE_SplitList
556         CDSUNIT_DECLARE_SkipListMap
557         CDSUNIT_DECLARE_EllenBinTreeMap
558         CDSUNIT_DECLARE_BronsonAVLTreeMap
559         CDSUNIT_DECLARE_MultiLevelHashMap
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_MultiLevelHashMap
571             CDSUNIT_TEST_CuckooMap
572             CDSUNIT_TEST_StripedMap
573             CDSUNIT_TEST_RefinableMap
574         CPPUNIT_TEST_SUITE_END();
575
576     };
577 } // namespace map2