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