1 ////////////////////////////////////////////////////////////////////////////////
2 // The Loki Library
3 // Copyright (c) 2005 Peter K�mmel
4 // Copyright (c) 2005 Richard Sposato
5 // Permission to use, copy, modify, distribute and sell this software for any
6 //     purpose is hereby granted without fee, provided that the above copyright
7 //     notice appear in all copies and that both that copyright notice and this
8 //     permission notice appear in supporting documentation.
9 // The authors make no representations about the
10 //     suitability of this software for any purpose. It is provided "as is"
11 //     without express or implied warranty.
12 ////////////////////////////////////////////////////////////////////////////////
13 
14 // $Id: SmallObjBench.cpp 824 2007-05-09 00:57:06Z rich_sposato $
15 
16 
17 // ----------------------------------------------------------------------------
18 
19 #define LOKI_SMALL_OBJECT_USE_NEW_ARRAY
20 
21 #ifndef LOKI_CLASS_LEVEL_THREADING
22 #define LOKI_CLASS_LEVEL_THREADING
23 #endif
24 
25 #include <loki/SmallObj.h>
26 #include "timer.h"
27 
28 #include <iostream>
29 #include <string>
30 
31 //#define COMPARE_BOOST_POOL
32 #ifdef COMPARE_BOOST_POOL
33     #include <boost\pool\object_pool.hpp>
34 #endif
35 
36 using namespace std;
37 
38 
39 // ----------------------------------------------------------------------------
40 
41 template<unsigned int N>
42 class ThisIsASmallObject
43 {
44     char data[N];
45 };
46 
47 template<unsigned int N, class T>
48 struct Base : public ThisIsASmallObject<N>, public T
49 {};
50 
51 template<unsigned int N>
52 struct Base<N, void> : public ThisIsASmallObject<N>
53 {};
54 
55 
56 // ----------------------------------------------------------------------------
57 
58 #ifdef COMPARE_BOOST_POOL
59 
60 template<unsigned int N>
61 class BoostPoolNew : public Base<N,void>
62 {
63 private:
64     static boost::object_pool< BoostPoolNew<N> > BoostPool;
65 
66 public:
67     /// Throwing single-object new throws bad_alloc when allocation fails.
68 #ifdef _MSC_VER
69     /// @note MSVC complains about non-empty exception specification lists.
operator new(std::size_t)70     static void * operator new ( std::size_t )
71 #else
72     static void * operator new ( std::size_t ) throw ( std::bad_alloc )
73 #endif
74     {
75         return BoostPool.malloc();
76     }
77 
78     /// Non-throwing single-object new returns NULL if allocation fails.
operator new(std::size_t,const std::nothrow_t &)79     static void * operator new ( std::size_t, const std::nothrow_t & ) throw ()
80     {
81         return BoostPool.malloc();
82     }
83 
84     /// Placement single-object new merely calls global placement new.
operator new(std::size_t size,void * place)85     inline static void * operator new ( std::size_t size, void * place )
86     {
87         return ::operator new( size, place );
88     }
89 
90     /// Single-object delete.
operator delete(void * p)91     static void operator delete ( void * p ) throw ()
92     {
93         BoostPool.free( reinterpret_cast< BoostPoolNew * >( p ) );
94     }
95 
96     /** Non-throwing single-object delete is only called when nothrow
97         new operator is used, and the constructor throws an exception.
98         */
operator delete(void * p,const std::nothrow_t &)99     static void operator delete ( void * p, const std::nothrow_t & ) throw()
100     {
101         BoostPool.free( reinterpret_cast< BoostPoolNew * >( p ) );
102     }
103 
104     /// Placement single-object delete merely calls global placement delete.
operator delete(void * p,void * place)105     inline static void operator delete ( void * p, void * place )
106     {
107         ::operator delete ( p, place );
108     }
109 
110     /** @note This class does not provide new [] and delete [] operators since
111      the Boost.Pool allocator only works for memory requests of the same size.
112      */
113 };
114 
115 template<unsigned int N>
116 boost::object_pool< BoostPoolNew<N> > BoostPoolNew<N>::BoostPool;
117 
118 #endif
119 
120 // ----------------------------------------------------------------------------
121 
122 
123 int array_test_nr = 0;
124 double t100_new = 0;
125 double t100_delete = 0;
126 
127 #define LOKI_SMALLOBJ_BENCH(FUNC, CODE_LOOP)                                 \
128 template<class T, int TN>                                                    \
129 int FUNC(void**, const int N, int loop, Timer& t, const char* s)             \
130 {                                                                            \
131     t.start();                                                               \
132     /****************************************************************/       \
133     for (int i=0; i<loop; ++i)                                               \
134     {                                                                        \
135         CODE_LOOP                                                            \
136     }                                                                        \
137     /****************************************************************/       \
138     t.stop();                                                                \
139     if(array_test_nr==0)                                                     \
140         t.t100 = t.t();                                                      \
141     array_test_nr++;                                                         \
142     t.print(t.t(),s);                                                        \
143     return t.t();                                                            \
144 }
145 #ifdef COMPARE_BOOST_POOL
146     #define LOKI_BOOST_TEST_NR 3
147 #else
148     #define LOKI_BOOST_TEST_NR -1
149 #endif
150 
151 // ----------------------------------------------------------------------------
152 
153 #define LOKI_SMALLOBJ_BENCH_ARRAY(FUNC, CODE_DECL, CODE_NEW, CODE_DELETE)    \
154 template<class T, int TN>                                                    \
155 int FUNC(void** arrv, const int N, int loop, Timer& t, const char* s)        \
156 {                                                                            \
157                                                                              \
158     CODE_DECL;                                                               \
159     T** arr = reinterpret_cast<T**>(arrv);                                   \
160     t.start();                                                               \
161     /****************************************************************/       \
162     for (int i=0; i<loop; ++i)                                               \
163     {                                                                        \
164         CODE_NEW                                                             \
165     }                                                                        \
166     /****************************************************************/       \
167     t.stop();                                                                \
168     cout << "1. ";                                                           \
169     if(array_test_nr==0)                                                     \
170     {                                                                        \
171         t.t100 = t.t();                                                      \
172         t100_new = t.t100;                                                   \
173     }                                                                        \
174     else                                                                     \
175         t.t100 = t100_new;                                                   \
176     t.print(t.t(),s);                                                        \
177                                                                              \
178     if(array_test_nr==LOKI_BOOST_TEST_NR)                                    \
179     {                                                                        \
180         cout <<                                                              \
181         "2. boost    :\tboost::object_pool is not tested because it's too slow"\
182         << endl << endl;                                                     \
183         array_test_nr++;                                                     \
184         return t.t();                                                        \
185     }                                                                        \
186     t.start();                                                               \
187     /****************************************************************/       \
188     for (int i=0; i<loop; ++i)                                               \
189     {                                                                        \
190         CODE_DELETE                                                          \
191     }                                                                        \
192     /****************************************************************/       \
193     t.stop();                                                                \
194     cout << "2. ";                                                           \
195     if(array_test_nr==0)                                                     \
196     {                                                                        \
197         t.t100 = t.t();                                                      \
198         t100_delete = t.t100;                                                \
199     }                                                                        \
200     else                                                                     \
201         t.t100 = t100_delete;                                                \
202     t.print(t.t(),s);                                                        \
203     array_test_nr++;                                                         \
204     cout << endl;                                                            \
205     return t.t();                                                            \
206 }
207 
208 // ----------------------------------------------------------------------------
209 
210 
LOKI_SMALLOBJ_BENCH(delete_new,delete new T;)211 LOKI_SMALLOBJ_BENCH(delete_new        ,delete new T;)
212 LOKI_SMALLOBJ_BENCH(delete_new_mal    ,std::free(std::malloc(sizeof(T)));)
213 LOKI_SMALLOBJ_BENCH(delete_new_all    ,std::allocator<T> st;st.deallocate(st.allocate(1), 1);)
214 
215 LOKI_SMALLOBJ_BENCH(delete_new_array    ,delete[] new T[N];)
216 LOKI_SMALLOBJ_BENCH(delete_new_array_mal,std::free(std::malloc(sizeof(T[TN])));)
217 LOKI_SMALLOBJ_BENCH(delete_new_array_all,std::allocator<T[TN]> st;st.deallocate(st.allocate(1), 1);)
218 
219 LOKI_SMALLOBJ_BENCH_ARRAY(new_del_on_arr    , , arr[i] = new T; ,
220                                                 delete arr[i];)
221 LOKI_SMALLOBJ_BENCH_ARRAY(new_del_on_arr_mal, , arr[i] = static_cast<T*>(std::malloc(sizeof(T))); ,
222                                                 std::free(arr[i]);)
223 LOKI_SMALLOBJ_BENCH_ARRAY(new_del_on_arr_all,    std::allocator<T> st ,
224                                                 arr[i]=st.allocate(1); ,
225                                                 st.deallocate(arr[i], 1);)
226 
227 LOKI_SMALLOBJ_BENCH_ARRAY(new_del_a_on_a    , , arr[i] = new T[TN]; ,
228                                                 delete[] arr[i];)
229 LOKI_SMALLOBJ_BENCH_ARRAY(new_del_a_on_a_mal, , arr[i] = static_cast<T*>(std::malloc(sizeof(T[TN]))); ,
230                                                 std::free(arr[i]);)
231 LOKI_SMALLOBJ_BENCH_ARRAY(new_del_a_on_a_all,std::allocator<T[TN]> st ,
232                                                 arr[i]=reinterpret_cast<T*>(st.allocate(1)); ,
233                                                 st.deallocate(reinterpret_cast<T(*)[TN]>(arr[i]), 1);)
234 
235 
236 // ----------------------------------------------------------------------------
237 
238 
239 #ifndef COMPARE_BOOST_POOL
240 #define LOKI_SMALL_OBJECT_BENCH_ABCD(FUNC,N,LOOP,TIMER,MESSAGE)              \
241     array_test_nr = 0;                                                 \
242     cout << MESSAGE << endl;                                           \
243     FUNC<A,N>(a,N,LOOP,TIMER,"new      :");                            \
244     FUNC<B,N>(a,N,LOOP,TIMER,"SmallObj :");                            \
245     FUNC<C,N>(a,N,LOOP,TIMER,"ValueObj :");                            \
246     FUNC##_all<A,N>(a,N,LOOP,TIMER,"allocator:");                      \
247     FUNC##_mal<A,N>(a,N,LOOP,TIMER,"malloc   :");                      \
248     cout << endl << endl;
249 #else
250 #define LOKI_SMALL_OBJECT_BENCH_ABCD(FUNC,N,LOOP,TIMER,MESSAGE)              \
251     array_test_nr = 0;                                                 \
252     cout << MESSAGE << endl;                                           \
253     FUNC<A,N>(a,N,LOOP,TIMER,"new      :");                            \
254     FUNC<B,N>(a,N,LOOP,TIMER,"SmallObj :");                            \
255     FUNC<C,N>(a,N,LOOP,TIMER,"ValueObj :");                            \
256     FUNC<D,N>(a,N,LOOP,TIMER,"boost    :");                            \
257     FUNC##_all<A,N>(a,N,LOOP,TIMER,"allocator:");                      \
258     FUNC##_mal<A,N>(a,N,LOOP,TIMER,"malloc   :");                      \
259     cout << endl << endl;
260 #endif
261 
262 // ----------------------------------------------------------------------------
263 
264 template<
265     unsigned int Size,
266     int loop,
267     template <class, class> class ThreadingModel,
268     std::size_t chunkSize,
269     std::size_t maxSmallObjectSize,
270     std::size_t objectAlignSize,
271     template <class> class LifetimePolicy,
272     class MutexPolicy
273 >
274 void testSize()
275 {
276 
277     typedef Base<Size, void> A;
278     typedef Base<Size, Loki::SmallObject< ThreadingModel, chunkSize,
279         maxSmallObjectSize, objectAlignSize, LifetimePolicy, MutexPolicy > > B;
280     typedef Base<Size, Loki::SmallValueObject< ThreadingModel, chunkSize,
281         maxSmallObjectSize, objectAlignSize, LifetimePolicy, MutexPolicy > > C;
282     typedef Loki::AllocatorSingleton< ThreadingModel, chunkSize,
283         maxSmallObjectSize, objectAlignSize, LifetimePolicy, MutexPolicy > AllocatorSingleton;
284 
285 #ifdef COMPARE_BOOST_POOL
286     typedef BoostPoolNew<Size> D;
287 #endif
288 
289     assert( (!AllocatorSingleton::IsCorrupted()) );
290     cout << endl << endl;
291     cout << "Allocator Benchmark Tests with " << Size << " bytes big objects " << endl;
292     cout << endl;
293     cout << "new      = global new/delete     \tsizeof(A) = " << sizeof(A) << endl;
294     cout << "SmallObj = Loki::SmallObject     \tsizeof(B) = " << sizeof(B) << endl;
295     cout << "ValueObj = Loki::SmallValueObject\tsizeof(C) = " << sizeof(C) << endl;
296 #ifdef COMPARE_BOOST_POOL
297     cout << "boost    = boost::object_pool    \tsizeof(D) = " << sizeof(D) << endl;
298 #endif
299     cout << "allocator= std::allocator        \tsizeof(A) = " << sizeof(A) << endl;
300     cout << "malloc   = std::malloc/free      \tsizeof(A) = " << sizeof(A) << endl;
301     cout << endl << endl;
302 
303     Timer t;
304 
305     const int N = 3;
306     int Narr = 1000*1000;
307 
308     void** a= new void*[Narr];
309 
310     cout << loop  << " times ";
311     LOKI_SMALL_OBJECT_BENCH_ABCD(delete_new        ,0,loop,t,"'delete new T'");
312     assert( (!AllocatorSingleton::IsCorrupted()) );
313 
314     cout << "N=" << N <<" :  " << loop  << " times ";
315     LOKI_SMALL_OBJECT_BENCH_ABCD(delete_new_array    ,N,loop,t,"'delete[] new T[N]'");
316     assert( (!AllocatorSingleton::IsCorrupted()) );
317 
318     cout << "i=0..." << Narr << " :  ";
319     LOKI_SMALL_OBJECT_BENCH_ABCD(new_del_on_arr    ,0,Narr,t,"1. 'arr[i] = new T'   2. 'delete arr[i]'");
320     assert( (!AllocatorSingleton::IsCorrupted()) );
321 
322     cout << "i=0..." << Narr << ",  N=" << N <<" :  ";
323     LOKI_SMALL_OBJECT_BENCH_ABCD(new_del_a_on_a    ,N,Narr,t,"1. 'arr[i] = new T[N]'   2. 'delete[] arr[i]'");
324     assert( (!AllocatorSingleton::IsCorrupted()) );
325 
326 
327     delete [] a;
328 
329     cout << "_________________________________________________________________" << endl;
330     assert( (!AllocatorSingleton::IsCorrupted()) );
331     AllocatorSingleton::ClearExtraMemory();
332 }
333 
334 // ----------------------------------------------------------------------------
335 
DoSingleThreadTest(void)336 void DoSingleThreadTest (void)
337 {
338     const int loop = 1000*1000;
339     cout << endl;
340     testSize<  2, loop, ::Loki::SingleThreaded, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
341     testSize<  3, loop, ::Loki::SingleThreaded, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
342     testSize<  8, loop, ::Loki::SingleThreaded, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
343     testSize<  9, loop, ::Loki::SingleThreaded, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
344     testSize< 16, loop, ::Loki::SingleThreaded, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
345     testSize< 17, loop, ::Loki::SingleThreaded, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
346 
347 #if defined(__BORLANDC__) || defined(_MSC_VER)
348     system("PAUSE");
349 #endif
350 }
351 
352 // ----------------------------------------------------------------------------
353 
354 #if defined(LOKI_CLASS_LEVEL_THREADING)
355 
DoClassLockTest(void)356 void DoClassLockTest (void)
357 {
358     const int loop = 1000*1000;
359     cout << endl;
360     testSize<  2, loop, ::Loki::ClassLevelLockable, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
361     testSize<  3, loop, ::Loki::ClassLevelLockable, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
362     testSize<  8, loop, ::Loki::ClassLevelLockable, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
363     testSize<  9, loop, ::Loki::ClassLevelLockable, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
364     testSize< 16, loop, ::Loki::ClassLevelLockable, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
365     testSize< 17, loop, ::Loki::ClassLevelLockable, 4096, 128, 4, ::Loki::NoDestroy, ::Loki::Mutex >();
366 
367 #if defined(__BORLANDC__) || defined(_MSC_VER)
368     system("PAUSE");
369 #endif
370 }
371 
372 #endif
373 
374 // ----------------------------------------------------------------------------
375 
main()376 int main()
377 {
378     DoSingleThreadTest();
379 
380 #if defined(LOKI_CLASS_LEVEL_THREADING)
381     DoClassLockTest();
382 #endif
383 
384     return 0;
385 }
386 
387 // ----------------------------------------------------------------------------
388