1 //////////////////////////////////////////////////////////////////////////////
2 // Copyright 2005-2008 Andreas Huber Doenni
3 // Distributed under the Boost Software License, Version 1.0. (See accompany-
4 // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 //////////////////////////////////////////////////////////////////////////////
6
7
8
9 //////////////////////////////////////////////////////////////////////////////
10 // #define CUSTOMIZE_MEMORY_MANAGEMENT
11 // #define BOOST_STATECHART_USE_NATIVE_RTTI
12 //////////////////////////////////////////////////////////////////////////////
13 // This program measures event processing performance of the BitMachine
14 // (see BitMachine example for more information) with a varying number of
15 // states. Also, a varying number of transitions are replaced with in-state
16 // reactions. This allows us to calculate how much time is spent for state-
17 // entry and state-exit during a transition. All measurements are written to
18 // comma-separated-values files, one file for each individual BitMachine.
19 //////////////////////////////////////////////////////////////////////////////
20
21
22
23 #include <boost/statechart/event.hpp>
24 #include <boost/statechart/simple_state.hpp>
25 #include <boost/statechart/state_machine.hpp>
26 #include <boost/statechart/transition.hpp>
27 #include <boost/statechart/in_state_reaction.hpp>
28
29 #include <boost/mpl/list.hpp>
30 #include <boost/mpl/front_inserter.hpp>
31 #include <boost/mpl/transform_view.hpp>
32 #include <boost/mpl/copy.hpp>
33 #include <boost/mpl/range_c.hpp>
34 #include <boost/mpl/integral_c.hpp>
35 #include <boost/mpl/shift_left.hpp>
36 #include <boost/mpl/bitxor.hpp>
37 #include <boost/mpl/for_each.hpp>
38 #include <boost/mpl/placeholders.hpp>
39 #include <boost/mpl/if.hpp>
40 #include <boost/mpl/less.hpp>
41 #include <boost/mpl/aux_/lambda_support.hpp>
42
43 #include <boost/intrusive_ptr.hpp>
44 #include <boost/config.hpp>
45 #include <boost/assert.hpp>
46
47 #ifdef CUSTOMIZE_MEMORY_MANAGEMENT
48 # ifdef BOOST_MSVC
49 # pragma warning( push )
50 # pragma warning( disable: 4127 ) // conditional expression is constant
51 # pragma warning( disable: 4800 ) // forcing value to bool 'true' or 'false'
52 # endif
53 # define BOOST_NO_MT
54 # include <boost/pool/pool_alloc.hpp>
55 # ifdef BOOST_MSVC
56 # pragma warning( pop )
57 # endif
58 #endif
59
60 #include <vector>
61 #include <ctime>
62 #include <iostream>
63 #include <fstream>
64 #include <iomanip>
65 #include <ios>
66 #include <string>
67 #include <algorithm>
68
69 #ifdef BOOST_NO_STDC_NAMESPACE
70 namespace std
71 {
72 using ::clock_t;
73 using ::clock;
74 }
75 #endif
76
77 #ifdef BOOST_INTEL
78 # pragma warning( disable: 304 ) // access control not specified
79 # pragma warning( disable: 444 ) // destructor for base is not virtual
80 # pragma warning( disable: 981 ) // operands are evaluated in unspecified order
81 #endif
82
83
84
85 namespace sc = boost::statechart;
86 namespace mpl = boost::mpl;
87
88
89
90 //////////////////////////////////////////////////////////////////////////////
91 typedef mpl::integral_c< unsigned int, 0 > uint0;
92 typedef mpl::integral_c< unsigned int, 1 > uint1;
93 typedef mpl::integral_c< unsigned int, 2 > uint2;
94 typedef mpl::integral_c< unsigned int, 3 > uint3;
95 typedef mpl::integral_c< unsigned int, 4 > uint4;
96 typedef mpl::integral_c< unsigned int, 5 > uint5;
97 typedef mpl::integral_c< unsigned int, 6 > uint6;
98 typedef mpl::integral_c< unsigned int, 7 > uint7;
99 typedef mpl::integral_c< unsigned int, 8 > uint8;
100 typedef mpl::integral_c< unsigned int, 9 > uint9;
101
102 //////////////////////////////////////////////////////////////////////////////
103 template< class BitNo >
104 struct EvFlipBit : sc::event< EvFlipBit< BitNo > > {};
105
106 boost::intrusive_ptr< const sc::event_base > pFlipBitEvents[] =
107 {
108 new EvFlipBit< uint0 >,
109 new EvFlipBit< uint1 >,
110 new EvFlipBit< uint2 >,
111 new EvFlipBit< uint3 >,
112 new EvFlipBit< uint4 >,
113 new EvFlipBit< uint5 >,
114 new EvFlipBit< uint6 >,
115 new EvFlipBit< uint7 >,
116 new EvFlipBit< uint8 >,
117 new EvFlipBit< uint9 >
118 };
119
120
121 //////////////////////////////////////////////////////////////////////////////
122 template<
123 class StateNo,
124 class NoOfBits,
125 class FirstTransitionBit >
126 struct BitState;
127
128 template< class NoOfBits, class FirstTransitionBit >
129 struct BitMachine : sc::state_machine<
130 BitMachine< NoOfBits, FirstTransitionBit >,
131 BitState< uint0, NoOfBits, FirstTransitionBit >
132 #ifdef CUSTOMIZE_MEMORY_MANAGEMENT
133 , boost::fast_pool_allocator< int >
134 #endif
135 >
136 {
137 public:
BitMachineBitMachine138 BitMachine() : inStateReactions_( 0 ), transitions_( 0 ) {}
139
140 // GCC 3.4.2 doesn't seem to instantiate a function template despite the
141 // fact that an address of the instantiation is passed as a non-type
142 // template argument. This leads to linker errors when a function template
143 // is defined instead of the overloads below.
InStateReactionBitMachine144 void InStateReaction( const EvFlipBit< uint0 > & )
145 {
146 ++inStateReactions_;
147 }
148
InStateReactionBitMachine149 void InStateReaction( const EvFlipBit< uint1 > & )
150 {
151 ++inStateReactions_;
152 }
153
InStateReactionBitMachine154 void InStateReaction( const EvFlipBit< uint2 > & )
155 {
156 ++inStateReactions_;
157 }
158
InStateReactionBitMachine159 void InStateReaction( const EvFlipBit< uint3 > & )
160 {
161 ++inStateReactions_;
162 }
163
InStateReactionBitMachine164 void InStateReaction( const EvFlipBit< uint4 > & )
165 {
166 ++inStateReactions_;
167 }
168
InStateReactionBitMachine169 void InStateReaction( const EvFlipBit< uint5 > & )
170 {
171 ++inStateReactions_;
172 }
173
InStateReactionBitMachine174 void InStateReaction( const EvFlipBit< uint6 > & )
175 {
176 ++inStateReactions_;
177 }
178
InStateReactionBitMachine179 void InStateReaction( const EvFlipBit< uint7 > & )
180 {
181 ++inStateReactions_;
182 }
183
InStateReactionBitMachine184 void InStateReaction( const EvFlipBit< uint8 > & )
185 {
186 ++inStateReactions_;
187 }
188
InStateReactionBitMachine189 void InStateReaction( const EvFlipBit< uint9 > & )
190 {
191 ++inStateReactions_;
192 }
193
TransitionBitMachine194 void Transition( const EvFlipBit< uint0 > & )
195 {
196 ++transitions_;
197 }
198
TransitionBitMachine199 void Transition( const EvFlipBit< uint1 > & )
200 {
201 ++transitions_;
202 }
203
TransitionBitMachine204 void Transition( const EvFlipBit< uint2 > & )
205 {
206 ++transitions_;
207 }
208
TransitionBitMachine209 void Transition( const EvFlipBit< uint3 > & )
210 {
211 ++transitions_;
212 }
213
TransitionBitMachine214 void Transition( const EvFlipBit< uint4 > & )
215 {
216 ++transitions_;
217 }
218
TransitionBitMachine219 void Transition( const EvFlipBit< uint5 > & )
220 {
221 ++transitions_;
222 }
223
TransitionBitMachine224 void Transition( const EvFlipBit< uint6 > & )
225 {
226 ++transitions_;
227 }
228
TransitionBitMachine229 void Transition( const EvFlipBit< uint7 > & )
230 {
231 ++transitions_;
232 }
233
TransitionBitMachine234 void Transition( const EvFlipBit< uint8 > & )
235 {
236 ++transitions_;
237 }
238
TransitionBitMachine239 void Transition( const EvFlipBit< uint9 > & )
240 {
241 ++transitions_;
242 }
243
GetNoOfInStateReactionsBitMachine244 unsigned int GetNoOfInStateReactions() const
245 {
246 return inStateReactions_;
247 }
248
GetNoOfTransitionsBitMachine249 unsigned int GetNoOfTransitions() const
250 {
251 return transitions_;
252 }
253
254 private:
255 unsigned int inStateReactions_;
256 unsigned int transitions_;
257 };
258
259 //////////////////////////////////////////////////////////////////////////////
260 template<
261 class BitNo, class StateNo, class NoOfBits, class FirstTransitionBit >
262 struct FlipTransition
263 {
264 private:
265 typedef typename mpl::bitxor_<
266 StateNo,
267 mpl::shift_left< uint1, BitNo >
268 >::type NextStateNo;
269
270 public:
271 typedef typename mpl::if_<
272 mpl::less< BitNo, FirstTransitionBit >,
273 sc::in_state_reaction<
274 EvFlipBit< BitNo >,
275 BitMachine< NoOfBits, FirstTransitionBit >,
276 &BitMachine< NoOfBits, FirstTransitionBit >::InStateReaction >,
277 sc::transition<
278 EvFlipBit< BitNo >,
279 BitState< NextStateNo, NoOfBits, FirstTransitionBit >,
280 BitMachine< NoOfBits, FirstTransitionBit >,
281 &BitMachine< NoOfBits, FirstTransitionBit >::Transition >
282 >::type type;
283
284 BOOST_MPL_AUX_LAMBDA_SUPPORT(
285 3, FlipTransition, (BitNo, StateNo, FirstTransitionBit) );
286 };
287
288 //////////////////////////////////////////////////////////////////////////////
289 template<
290 class StateNo,
291 class NoOfBits,
292 class FirstTransitionBit >
293 struct BitState : sc::simple_state<
294 BitState< StateNo, NoOfBits, FirstTransitionBit >,
295 BitMachine< NoOfBits, FirstTransitionBit > >
296 {
297 typedef typename mpl::copy<
298 typename mpl::transform_view<
299 mpl::range_c< unsigned int, 0, NoOfBits::value >,
300 FlipTransition<
301 mpl::placeholders::_, StateNo, NoOfBits, FirstTransitionBit >
302 >::type,
303 mpl::front_inserter< mpl::list<> >
304 >::type reactions;
305 };
306
307 // GCC 3.4.2 doesn't seem to instantiate a class template member function
308 // despite the fact that an address of the function is passed as a non-type
309 // template argument. This leads to linker errors when the class template
310 // defining the functions is not explicitly instantiated.
311 template struct BitMachine< uint1, uint0 >;
312 template struct BitMachine< uint1, uint1 >;
313
314 template struct BitMachine< uint2, uint0 >;
315 template struct BitMachine< uint2, uint1 >;
316 template struct BitMachine< uint2, uint2 >;
317
318 template struct BitMachine< uint3, uint0 >;
319 template struct BitMachine< uint3, uint1 >;
320 template struct BitMachine< uint3, uint2 >;
321 template struct BitMachine< uint3, uint3 >;
322
323 template struct BitMachine< uint4, uint0 >;
324 template struct BitMachine< uint4, uint1 >;
325 template struct BitMachine< uint4, uint2 >;
326 template struct BitMachine< uint4, uint3 >;
327 template struct BitMachine< uint4, uint4 >;
328
329 template struct BitMachine< uint5, uint0 >;
330 template struct BitMachine< uint5, uint1 >;
331 template struct BitMachine< uint5, uint2 >;
332 template struct BitMachine< uint5, uint3 >;
333 template struct BitMachine< uint5, uint4 >;
334 template struct BitMachine< uint5, uint5 >;
335
336 template struct BitMachine< uint6, uint0 >;
337 template struct BitMachine< uint6, uint1 >;
338 template struct BitMachine< uint6, uint2 >;
339 template struct BitMachine< uint6, uint3 >;
340 template struct BitMachine< uint6, uint4 >;
341 template struct BitMachine< uint6, uint5 >;
342 template struct BitMachine< uint6, uint6 >;
343
344 template struct BitMachine< uint7, uint0 >;
345 template struct BitMachine< uint7, uint1 >;
346 template struct BitMachine< uint7, uint2 >;
347 template struct BitMachine< uint7, uint3 >;
348 template struct BitMachine< uint7, uint4 >;
349 template struct BitMachine< uint7, uint5 >;
350 template struct BitMachine< uint7, uint6 >;
351 template struct BitMachine< uint7, uint7 >;
352
353
354 ////////////////////////////////////////////////////////////////////////////
355 struct PerfResult
356 {
PerfResultPerfResult357 PerfResult( double inStateRatio, double nanoSecondsPerReaction ) :
358 inStateRatio_( inStateRatio ),
359 nanoSecondsPerReaction_( nanoSecondsPerReaction )
360 {
361 }
362
363 double inStateRatio_;
364 double nanoSecondsPerReaction_;
365 };
366
367 template< class NoOfBits, class FirstTransitionBit >
368 class PerformanceTester
369 {
370 public:
371 ////////////////////////////////////////////////////////////////////////
Test()372 static PerfResult Test()
373 {
374 eventsSent_ = 0;
375 BitMachine< NoOfBits, FirstTransitionBit > machine;
376 machine.initiate();
377 const std::clock_t startTime = std::clock();
378
379 const unsigned int laps = eventsToSend_ / ( GetNoOfStates() - 1 );
380
381 for ( unsigned int lap = 0; lap < laps; ++lap )
382 {
383 VisitAllStatesImpl( machine, NoOfBits::value - 1 );
384 }
385
386 const std::clock_t elapsedTime = std::clock() - startTime;
387
388 BOOST_ASSERT( eventsSent_ == eventsToSend_ );
389 BOOST_ASSERT(
390 machine.GetNoOfInStateReactions() +
391 machine.GetNoOfTransitions() == eventsSent_ );
392
393 return PerfResult(
394 static_cast< double >( machine.GetNoOfInStateReactions() ) /
395 eventsSent_,
396 static_cast< double >( elapsedTime ) /
397 CLOCKS_PER_SEC * 1000.0 * 1000.0 * 1000.0 / eventsSent_ );
398 }
399
GetNoOfStates()400 static unsigned int GetNoOfStates()
401 {
402 return 1 << NoOfBits::value;
403 }
404
GetNoOfReactions()405 static unsigned int GetNoOfReactions()
406 {
407 return GetNoOfStates() * NoOfBits::value;
408 }
409
410 private:
411 ////////////////////////////////////////////////////////////////////////
VisitAllStatesImpl(BitMachine<NoOfBits,FirstTransitionBit> & machine,unsigned int bit)412 static void VisitAllStatesImpl(
413 BitMachine< NoOfBits, FirstTransitionBit > & machine,
414 unsigned int bit )
415 {
416 if ( bit > 0 )
417 {
418 PerformanceTester< NoOfBits, FirstTransitionBit >::
419 VisitAllStatesImpl( machine, bit - 1 );
420 }
421
422 machine.process_event( *pFlipBitEvents[ bit ] );
423 ++PerformanceTester< NoOfBits, FirstTransitionBit >::eventsSent_;
424
425 if ( bit > 0 )
426 {
427 PerformanceTester< NoOfBits, FirstTransitionBit >::
428 VisitAllStatesImpl( machine, bit - 1 );
429 }
430 }
431
432 // common prime factors of 2^n-1 for n in [1,8]
433 static const unsigned int eventsToSend_ = 3 * 3 * 5 * 7 * 17 * 31 * 127;
434 static unsigned int eventsSent_;
435 };
436
437 template< class NoOfBits, class FirstTransitionBit >
438 unsigned int PerformanceTester< NoOfBits, FirstTransitionBit >::eventsSent_;
439
440
441 //////////////////////////////////////////////////////////////////////////////
442 typedef std::vector< PerfResult > PerfResultList;
443
444 template< class NoOfBits >
445 struct PerfResultBackInserter
446 {
447 public:
PerfResultBackInserterPerfResultBackInserter448 PerfResultBackInserter( PerfResultList & perfResultList ) :
449 perfResultList_( perfResultList )
450 {
451 }
452
453 template< class FirstTransitionBit >
operator ()PerfResultBackInserter454 void operator()( const FirstTransitionBit & )
455 {
456 perfResultList_.push_back(
457 PerformanceTester< NoOfBits, FirstTransitionBit >::Test() );
458 }
459
460 private:
461 // avoids C4512 (assignment operator could not be generated)
462 PerfResultBackInserter & operator=( const PerfResultBackInserter & );
463
464 PerfResultList & perfResultList_;
465 };
466
467 template< class NoOfBits >
TestMachine()468 std::vector< PerfResult > TestMachine()
469 {
470 PerfResultList result;
471
472 mpl::for_each< mpl::range_c< unsigned int, 0, NoOfBits::value + 1 > >(
473 PerfResultBackInserter< NoOfBits >( result ) );
474
475 return result;
476 }
477
478 template< class NoOfBits >
TestAndWriteResults()479 void TestAndWriteResults()
480 {
481 PerfResultList results = TestMachine< NoOfBits >();
482
483 std::fstream output;
484 output.exceptions(
485 std::ios_base::badbit | std::ios_base::eofbit | std::ios_base::failbit );
486
487 std::string prefix = std::string( BOOST_COMPILER ) + "__";
488 std::replace( prefix.begin(), prefix.end(), ' ', '_' );
489
490 output.open(
491 ( prefix + std::string( 1, '0' + static_cast< char >( NoOfBits::value ) )
492 + ".txt" ).c_str(),
493 std::ios_base::out );
494
495 for ( PerfResultList::const_iterator pResult = results.begin();
496 pResult != results.end(); ++pResult )
497 {
498 output << std::fixed << std::setprecision( 0 ) <<
499 std::setw( 8 ) << pResult->inStateRatio_ * 100 << ',' <<
500 std::setw( 8 ) << pResult->nanoSecondsPerReaction_ << "\n";
501 }
502 }
503
504
505 //////////////////////////////////////////////////////////////////////////////
main()506 int main()
507 {
508 std::cout <<
509 "Boost.Statechart in-state reaction vs. transition performance test\n\n";
510 std::cout << "Press <CR> to start the test: ";
511
512 {
513 std::string input;
514 std::getline( std::cin, input );
515 }
516
517 TestAndWriteResults< uint1 >();
518 TestAndWriteResults< uint2 >();
519 TestAndWriteResults< uint3 >();
520
521 return 0;
522 }
523