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