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