Added copyright and license
[libcds.git] / tests / unit / alloc / random.cpp
1 /*
2     This file is a part of libcds - Concurrent Data Structures library
3
4     (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
5
6     Source code repo: http://github.com/khizmax/libcds/
7     Download: http://sourceforge.net/projects/libcds/files/
8     
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions are met:
11
12     * Redistributions of source code must retain the above copyright notice, this
13       list of conditions and the following disclaimer.
14
15     * Redistributions in binary form must reproduce the above copyright notice,
16       this list of conditions and the following disclaimer in the documentation
17       and/or other materials provided with the distribution.
18
19     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.     
29 */
30
31 // Random allocator test
32
33 #include "alloc/michael_allocator.h"
34 #include "alloc/random_gen.h"
35
36 #include <cds/os/timer.h>
37 #include <cds/os/topology.h>
38
39 #include "cppunit/thread.h"
40
41 namespace memory {
42
43     static size_t s_nMaxThreadCount = 32;
44     static unsigned int s_nMinBlockSize = 8;
45     static unsigned int s_nMaxBlockSize = 1024;
46     //static size_t s_nBlocksPerThread = 1000;
47     static size_t s_nPassCount = 100000;
48     static size_t s_nDataSize = 1000;
49
50     static size_t s_nPassPerThread;
51
52     struct Item {
53         cds::sync::spin m_access;
54         char *          m_pszBlock;
55
56         Item()
57             : m_access( false )
58             , m_pszBlock( nullptr )
59         {}
60
61         Item& operator =(Item const& i)
62         {
63             m_pszBlock = i.m_pszBlock;
64             return *this;
65         }
66     };
67     typedef std::vector<Item>   item_array;
68
69 #    define TEST_ALLOC(X, CLASS)        void X() { test< CLASS >(false); }
70 #    define TEST_ALLOC_STAT(X, CLASS)   void X() { test< CLASS >(true) ; }
71
72     class Random_Alloc: public CppUnitMini::TestCase
73     {
74         item_array  m_Data;
75
76         template <class ALLOC>
77         class Thread: public CppUnitMini::TestThread
78         {
79             ALLOC&      m_Alloc;
80             typedef typename ALLOC::value_type value_type;
81
82             randomGen<size_t>   m_rndGen;
83
84             virtual Thread *    clone()
85             {
86                 return new Thread( *this );
87             }
88
89         public:
90             Thread( CppUnitMini::ThreadPool& pool, ALLOC& a )
91                 : CppUnitMini::TestThread( pool )
92                 , m_Alloc( a )
93             {}
94             Thread( Thread& src )
95                 : CppUnitMini::TestThread( src )
96                 , m_Alloc( src.m_Alloc )
97             {}
98
99             Random_Alloc&  getTest()
100             {
101                 return reinterpret_cast<Random_Alloc&>( m_Pool.m_Test );
102             }
103
104             virtual void init() { cds::threading::Manager::attachThread()   ; }
105             virtual void fini() { cds::threading::Manager::detachThread()   ; }
106
107             virtual void test()
108             {
109                 item_array& arr = getTest().m_Data;
110                 for ( size_t nPass = 0; nPass < s_nPassPerThread; ) {
111                     size_t nIdx = m_rndGen( size_t(0), s_nDataSize - 1 );
112                     Item & item = arr.at(nIdx);
113                     if ( item.m_access.try_lock() ) {
114                         if ( item.m_pszBlock ) {
115                             m_Alloc.deallocate( item.m_pszBlock, 1 );
116                             item.m_pszBlock = nullptr;
117                         }
118                         else {
119                             size_t nSize;
120                             item.m_pszBlock = m_Alloc.allocate( nSize = m_rndGen( s_nMinBlockSize, s_nMaxBlockSize ), nullptr );
121
122                             if ( nSize < 32 )
123                                 memset( item.m_pszBlock, 0, nSize );
124                             else {
125                                 memset( item.m_pszBlock, 0, 16 );
126                                 memset( ((char *) item.m_pszBlock) + nSize * sizeof(*item.m_pszBlock) - 16, 0, 16 );
127                             }
128                         }
129                         item.m_access.unlock();
130
131                         ++nPass;
132                     }
133                 }
134             }
135         };
136
137         template <class ALLOC>
138         void test( size_t nThreadCount )
139         {
140             ALLOC alloc;
141
142             CPPUNIT_MSG( "Thread count=" << nThreadCount );
143             s_nPassPerThread = s_nPassCount / nThreadCount;
144
145             CppUnitMini::ThreadPool pool( *this );
146             pool.add( new Thread<ALLOC>( pool, alloc ), nThreadCount );
147
148             cds::OS::Timer    timer;
149             pool.run();
150             CPPUNIT_MSG( "  Duration=" << pool.avgDuration() );
151
152             for ( size_t i = 0; i < m_Data.size(); ++i ) {
153                 if ( m_Data[i].m_pszBlock ) {
154                     alloc.deallocate( m_Data[i].m_pszBlock, 1 );
155                     m_Data[i].m_pszBlock = nullptr;
156                 }
157             }
158         }
159
160         template <class ALLOC>
161         void test( bool bStat )
162         {
163             CPPUNIT_MSG( "Block size=" << s_nMinBlockSize << "-" << s_nMaxBlockSize
164                 << ", pass count=" << s_nPassCount << ", data size=" << s_nDataSize );
165
166             m_Data.resize( s_nDataSize );
167
168             for ( size_t nThreadCount = 2; nThreadCount <= s_nMaxThreadCount; nThreadCount *= 2 ) {
169                 summary_stat    stBegin;
170                 if ( bStat )
171                     ALLOC::stat( stBegin );
172
173                 test<ALLOC>( nThreadCount );
174
175                 summary_stat    stEnd;
176                 if ( bStat ) {
177                     ALLOC::stat( stEnd );
178
179                     std::cout << "\nStatistics:\n"
180                         << stEnd;
181                     stEnd -= stBegin;
182                     std::cout << "\nDelta statistics:\n"
183                         << stEnd;
184                 }
185             }
186
187             m_Data.resize(0);
188         }
189
190         void setUpParams( const CppUnitMini::TestCfg& cfg )
191         {
192             s_nDataSize = cfg.getULong( "DataSize", 1000 );
193             s_nPassCount = cfg.getULong( "PassCount", 100000 );
194             s_nMinBlockSize = cfg.getUInt( "MinBlockSize", 8 );
195             s_nMaxBlockSize = cfg.getUInt( "MaxBlockSize", 1024 );
196             s_nMaxThreadCount = cfg.getUInt( "MaxThreadCount", 32 );
197             if ( s_nMaxThreadCount == 0 )
198                 s_nMaxThreadCount = cds::OS::topology::processor_count() * 2;
199             if ( s_nMaxThreadCount < 2 )
200                 s_nMaxThreadCount = 2;
201         }
202
203         typedef MichaelAlignHeap_Stat<char, 32>     t_MichaelAlignHeap_Stat;
204         typedef MichaelAlignHeap_NoStat<char,32>    t_MichaelAlignHeap_NoStat;
205         typedef system_aligned_allocator<char, 32>  t_system_aligned_allocator;
206
207         TEST_ALLOC_STAT( michael_heap_stat, MichaelHeap_Stat<char> )
208         TEST_ALLOC( michael_heap_nostat,    MichaelHeap_NoStat<char> )
209         TEST_ALLOC( std_alloc,              std_allocator<char> )
210
211         TEST_ALLOC_STAT( michael_alignheap_stat,t_MichaelAlignHeap_Stat )
212         TEST_ALLOC( michael_alignheap_nostat,   t_MichaelAlignHeap_NoStat )
213         TEST_ALLOC( system_aligned_alloc,       t_system_aligned_allocator )
214
215         CPPUNIT_TEST_SUITE( Random_Alloc )
216             CPPUNIT_TEST( michael_heap_stat )
217             CPPUNIT_TEST( michael_heap_nostat )
218             CPPUNIT_TEST( std_alloc )
219
220             CPPUNIT_TEST( system_aligned_alloc )
221             CPPUNIT_TEST( michael_alignheap_stat )
222             CPPUNIT_TEST( michael_alignheap_nostat )
223
224         CPPUNIT_TEST_SUITE_END();
225     };
226
227 }   // namespace memory
228 CPPUNIT_TEST_SUITE_REGISTRATION( memory::Random_Alloc );