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