| ////////////////////////////////////////////////////////////////////////////// |
| // Copyright 2005-2008 Andreas Huber Doenni |
| // Distributed under the Boost Software License, Version 1.0. (See accompany- |
| // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // #define CUSTOMIZE_MEMORY_MANAGEMENT |
| // #define BOOST_STATECHART_USE_NATIVE_RTTI |
| ////////////////////////////////////////////////////////////////////////////// |
| // This program measures event processing performance of the BitMachine |
| // (see BitMachine example for more information) with a varying number of |
| // states. Also, a varying number of transitions are replaced with in-state |
| // reactions. This allows us to calculate how much time is spent for state- |
| // entry and state-exit during a transition. All measurements are written to |
| // comma-separated-values files, one file for each individual BitMachine. |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| |
| |
| #include <boost/statechart/event.hpp> |
| #include <boost/statechart/simple_state.hpp> |
| #include <boost/statechart/state_machine.hpp> |
| #include <boost/statechart/transition.hpp> |
| #include <boost/statechart/in_state_reaction.hpp> |
| |
| #include <boost/mpl/list.hpp> |
| #include <boost/mpl/front_inserter.hpp> |
| #include <boost/mpl/transform_view.hpp> |
| #include <boost/mpl/copy.hpp> |
| #include <boost/mpl/range_c.hpp> |
| #include <boost/mpl/integral_c.hpp> |
| #include <boost/mpl/shift_left.hpp> |
| #include <boost/mpl/bitxor.hpp> |
| #include <boost/mpl/for_each.hpp> |
| #include <boost/mpl/placeholders.hpp> |
| #include <boost/mpl/if.hpp> |
| #include <boost/mpl/less.hpp> |
| #include <boost/mpl/aux_/lambda_support.hpp> |
| |
| #include <boost/intrusive_ptr.hpp> |
| #include <boost/config.hpp> |
| #include <boost/assert.hpp> |
| |
| #ifdef CUSTOMIZE_MEMORY_MANAGEMENT |
| # ifdef BOOST_MSVC |
| # pragma warning( push ) |
| # pragma warning( disable: 4127 ) // conditional expression is constant |
| # pragma warning( disable: 4800 ) // forcing value to bool 'true' or 'false' |
| # endif |
| # define BOOST_NO_MT |
| # include <boost/pool/pool_alloc.hpp> |
| # ifdef BOOST_MSVC |
| # pragma warning( pop ) |
| # endif |
| #endif |
| |
| #include <vector> |
| #include <ctime> |
| #include <iostream> |
| #include <fstream> |
| #include <iomanip> |
| #include <ios> |
| #include <string> |
| #include <algorithm> |
| |
| #ifdef BOOST_NO_STDC_NAMESPACE |
| namespace std |
| { |
| using ::clock_t; |
| using ::clock; |
| } |
| #endif |
| |
| #ifdef BOOST_INTEL |
| # pragma warning( disable: 304 ) // access control not specified |
| # pragma warning( disable: 444 ) // destructor for base is not virtual |
| # pragma warning( disable: 981 ) // operands are evaluated in unspecified order |
| #endif |
| |
| |
| |
| namespace sc = boost::statechart; |
| namespace mpl = boost::mpl; |
| |
| |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| typedef mpl::integral_c< unsigned int, 0 > uint0; |
| typedef mpl::integral_c< unsigned int, 1 > uint1; |
| typedef mpl::integral_c< unsigned int, 2 > uint2; |
| typedef mpl::integral_c< unsigned int, 3 > uint3; |
| typedef mpl::integral_c< unsigned int, 4 > uint4; |
| typedef mpl::integral_c< unsigned int, 5 > uint5; |
| typedef mpl::integral_c< unsigned int, 6 > uint6; |
| typedef mpl::integral_c< unsigned int, 7 > uint7; |
| typedef mpl::integral_c< unsigned int, 8 > uint8; |
| typedef mpl::integral_c< unsigned int, 9 > uint9; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| template< class BitNo > |
| struct EvFlipBit : sc::event< EvFlipBit< BitNo > > {}; |
| |
| boost::intrusive_ptr< const sc::event_base > pFlipBitEvents[] = |
| { |
| new EvFlipBit< uint0 >, |
| new EvFlipBit< uint1 >, |
| new EvFlipBit< uint2 >, |
| new EvFlipBit< uint3 >, |
| new EvFlipBit< uint4 >, |
| new EvFlipBit< uint5 >, |
| new EvFlipBit< uint6 >, |
| new EvFlipBit< uint7 >, |
| new EvFlipBit< uint8 >, |
| new EvFlipBit< uint9 > |
| }; |
| |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| template< |
| class StateNo, |
| class NoOfBits, |
| class FirstTransitionBit > |
| struct BitState; |
| |
| template< class NoOfBits, class FirstTransitionBit > |
| struct BitMachine : sc::state_machine< |
| BitMachine< NoOfBits, FirstTransitionBit >, |
| BitState< uint0, NoOfBits, FirstTransitionBit > |
| #ifdef CUSTOMIZE_MEMORY_MANAGEMENT |
| , boost::fast_pool_allocator< int > |
| #endif |
| > |
| { |
| public: |
| BitMachine() : inStateReactions_( 0 ), transitions_( 0 ) {} |
| |
| // GCC 3.4.2 doesn't seem to instantiate a function template despite the |
| // fact that an address of the instantiation is passed as a non-type |
| // template argument. This leads to linker errors when a function template |
| // is defined instead of the overloads below. |
| void InStateReaction( const EvFlipBit< uint0 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void InStateReaction( const EvFlipBit< uint1 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void InStateReaction( const EvFlipBit< uint2 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void InStateReaction( const EvFlipBit< uint3 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void InStateReaction( const EvFlipBit< uint4 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void InStateReaction( const EvFlipBit< uint5 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void InStateReaction( const EvFlipBit< uint6 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void InStateReaction( const EvFlipBit< uint7 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void InStateReaction( const EvFlipBit< uint8 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void InStateReaction( const EvFlipBit< uint9 > & ) |
| { |
| ++inStateReactions_; |
| } |
| |
| void Transition( const EvFlipBit< uint0 > & ) |
| { |
| ++transitions_; |
| } |
| |
| void Transition( const EvFlipBit< uint1 > & ) |
| { |
| ++transitions_; |
| } |
| |
| void Transition( const EvFlipBit< uint2 > & ) |
| { |
| ++transitions_; |
| } |
| |
| void Transition( const EvFlipBit< uint3 > & ) |
| { |
| ++transitions_; |
| } |
| |
| void Transition( const EvFlipBit< uint4 > & ) |
| { |
| ++transitions_; |
| } |
| |
| void Transition( const EvFlipBit< uint5 > & ) |
| { |
| ++transitions_; |
| } |
| |
| void Transition( const EvFlipBit< uint6 > & ) |
| { |
| ++transitions_; |
| } |
| |
| void Transition( const EvFlipBit< uint7 > & ) |
| { |
| ++transitions_; |
| } |
| |
| void Transition( const EvFlipBit< uint8 > & ) |
| { |
| ++transitions_; |
| } |
| |
| void Transition( const EvFlipBit< uint9 > & ) |
| { |
| ++transitions_; |
| } |
| |
| unsigned int GetNoOfInStateReactions() const |
| { |
| return inStateReactions_; |
| } |
| |
| unsigned int GetNoOfTransitions() const |
| { |
| return transitions_; |
| } |
| |
| private: |
| unsigned int inStateReactions_; |
| unsigned int transitions_; |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| template< |
| class BitNo, class StateNo, class NoOfBits, class FirstTransitionBit > |
| struct FlipTransition |
| { |
| private: |
| typedef typename mpl::bitxor_< |
| StateNo, |
| mpl::shift_left< uint1, BitNo > |
| >::type NextStateNo; |
| |
| public: |
| typedef typename mpl::if_< |
| mpl::less< BitNo, FirstTransitionBit >, |
| sc::in_state_reaction< |
| EvFlipBit< BitNo >, |
| BitMachine< NoOfBits, FirstTransitionBit >, |
| &BitMachine< NoOfBits, FirstTransitionBit >::InStateReaction >, |
| sc::transition< |
| EvFlipBit< BitNo >, |
| BitState< NextStateNo, NoOfBits, FirstTransitionBit >, |
| BitMachine< NoOfBits, FirstTransitionBit >, |
| &BitMachine< NoOfBits, FirstTransitionBit >::Transition > |
| >::type type; |
| |
| BOOST_MPL_AUX_LAMBDA_SUPPORT( |
| 3, FlipTransition, (BitNo, StateNo, FirstTransitionBit) ); |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| template< |
| class StateNo, |
| class NoOfBits, |
| class FirstTransitionBit > |
| struct BitState : sc::simple_state< |
| BitState< StateNo, NoOfBits, FirstTransitionBit >, |
| BitMachine< NoOfBits, FirstTransitionBit > > |
| { |
| typedef typename mpl::copy< |
| typename mpl::transform_view< |
| mpl::range_c< unsigned int, 0, NoOfBits::value >, |
| FlipTransition< |
| mpl::placeholders::_, StateNo, NoOfBits, FirstTransitionBit > |
| >::type, |
| mpl::front_inserter< mpl::list<> > |
| >::type reactions; |
| }; |
| |
| // GCC 3.4.2 doesn't seem to instantiate a class template member function |
| // despite the fact that an address of the function is passed as a non-type |
| // template argument. This leads to linker errors when the class template |
| // defining the functions is not explicitly instantiated. |
| template struct BitMachine< uint1, uint0 >; |
| template struct BitMachine< uint1, uint1 >; |
| |
| template struct BitMachine< uint2, uint0 >; |
| template struct BitMachine< uint2, uint1 >; |
| template struct BitMachine< uint2, uint2 >; |
| |
| template struct BitMachine< uint3, uint0 >; |
| template struct BitMachine< uint3, uint1 >; |
| template struct BitMachine< uint3, uint2 >; |
| template struct BitMachine< uint3, uint3 >; |
| |
| template struct BitMachine< uint4, uint0 >; |
| template struct BitMachine< uint4, uint1 >; |
| template struct BitMachine< uint4, uint2 >; |
| template struct BitMachine< uint4, uint3 >; |
| template struct BitMachine< uint4, uint4 >; |
| |
| template struct BitMachine< uint5, uint0 >; |
| template struct BitMachine< uint5, uint1 >; |
| template struct BitMachine< uint5, uint2 >; |
| template struct BitMachine< uint5, uint3 >; |
| template struct BitMachine< uint5, uint4 >; |
| template struct BitMachine< uint5, uint5 >; |
| |
| template struct BitMachine< uint6, uint0 >; |
| template struct BitMachine< uint6, uint1 >; |
| template struct BitMachine< uint6, uint2 >; |
| template struct BitMachine< uint6, uint3 >; |
| template struct BitMachine< uint6, uint4 >; |
| template struct BitMachine< uint6, uint5 >; |
| template struct BitMachine< uint6, uint6 >; |
| |
| template struct BitMachine< uint7, uint0 >; |
| template struct BitMachine< uint7, uint1 >; |
| template struct BitMachine< uint7, uint2 >; |
| template struct BitMachine< uint7, uint3 >; |
| template struct BitMachine< uint7, uint4 >; |
| template struct BitMachine< uint7, uint5 >; |
| template struct BitMachine< uint7, uint6 >; |
| template struct BitMachine< uint7, uint7 >; |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| struct PerfResult |
| { |
| PerfResult( double inStateRatio, double nanoSecondsPerReaction ) : |
| inStateRatio_( inStateRatio ), |
| nanoSecondsPerReaction_( nanoSecondsPerReaction ) |
| { |
| } |
| |
| double inStateRatio_; |
| double nanoSecondsPerReaction_; |
| }; |
| |
| template< class NoOfBits, class FirstTransitionBit > |
| class PerformanceTester |
| { |
| public: |
| //////////////////////////////////////////////////////////////////////// |
| static PerfResult Test() |
| { |
| eventsSent_ = 0; |
| BitMachine< NoOfBits, FirstTransitionBit > machine; |
| machine.initiate(); |
| const std::clock_t startTime = std::clock(); |
| |
| const unsigned int laps = eventsToSend_ / ( GetNoOfStates() - 1 ); |
| |
| for ( unsigned int lap = 0; lap < laps; ++lap ) |
| { |
| VisitAllStatesImpl( machine, NoOfBits::value - 1 ); |
| } |
| |
| const std::clock_t elapsedTime = std::clock() - startTime; |
| |
| BOOST_ASSERT( eventsSent_ == eventsToSend_ ); |
| BOOST_ASSERT( |
| machine.GetNoOfInStateReactions() + |
| machine.GetNoOfTransitions() == eventsSent_ ); |
| |
| return PerfResult( |
| static_cast< double >( machine.GetNoOfInStateReactions() ) / |
| eventsSent_, |
| static_cast< double >( elapsedTime ) / |
| CLOCKS_PER_SEC * 1000.0 * 1000.0 * 1000.0 / eventsSent_ ); |
| } |
| |
| static unsigned int GetNoOfStates() |
| { |
| return 1 << NoOfBits::value; |
| } |
| |
| static unsigned int GetNoOfReactions() |
| { |
| return GetNoOfStates() * NoOfBits::value; |
| } |
| |
| private: |
| //////////////////////////////////////////////////////////////////////// |
| static void VisitAllStatesImpl( |
| BitMachine< NoOfBits, FirstTransitionBit > & machine, |
| unsigned int bit ) |
| { |
| if ( bit > 0 ) |
| { |
| PerformanceTester< NoOfBits, FirstTransitionBit >:: |
| VisitAllStatesImpl( machine, bit - 1 ); |
| } |
| |
| machine.process_event( *pFlipBitEvents[ bit ] ); |
| ++PerformanceTester< NoOfBits, FirstTransitionBit >::eventsSent_; |
| |
| if ( bit > 0 ) |
| { |
| PerformanceTester< NoOfBits, FirstTransitionBit >:: |
| VisitAllStatesImpl( machine, bit - 1 ); |
| } |
| } |
| |
| // common prime factors of 2^n-1 for n in [1,8] |
| static const unsigned int eventsToSend_ = 3 * 3 * 5 * 7 * 17 * 31 * 127; |
| static unsigned int eventsSent_; |
| }; |
| |
| template< class NoOfBits, class FirstTransitionBit > |
| unsigned int PerformanceTester< NoOfBits, FirstTransitionBit >::eventsSent_; |
| |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| typedef std::vector< PerfResult > PerfResultList; |
| |
| template< class NoOfBits > |
| struct PerfResultBackInserter |
| { |
| public: |
| PerfResultBackInserter( PerfResultList & perfResultList ) : |
| perfResultList_( perfResultList ) |
| { |
| } |
| |
| template< class FirstTransitionBit > |
| void operator()( const FirstTransitionBit & ) |
| { |
| perfResultList_.push_back( |
| PerformanceTester< NoOfBits, FirstTransitionBit >::Test() ); |
| } |
| |
| private: |
| // avoids C4512 (assignment operator could not be generated) |
| PerfResultBackInserter & operator=( const PerfResultBackInserter & ); |
| |
| PerfResultList & perfResultList_; |
| }; |
| |
| template< class NoOfBits > |
| std::vector< PerfResult > TestMachine() |
| { |
| PerfResultList result; |
| |
| mpl::for_each< mpl::range_c< unsigned int, 0, NoOfBits::value + 1 > >( |
| PerfResultBackInserter< NoOfBits >( result ) ); |
| |
| return result; |
| } |
| |
| template< class NoOfBits > |
| void TestAndWriteResults() |
| { |
| PerfResultList results = TestMachine< NoOfBits >(); |
| |
| std::fstream output; |
| output.exceptions( |
| std::ios_base::badbit | std::ios_base::eofbit | std::ios_base::failbit ); |
| |
| std::string prefix = std::string( BOOST_COMPILER ) + "__"; |
| std::replace( prefix.begin(), prefix.end(), ' ', '_' ); |
| |
| output.open( |
| ( prefix + std::string( 1, '0' + static_cast< char >( NoOfBits::value ) ) |
| + ".txt" ).c_str(), |
| std::ios_base::out ); |
| |
| for ( PerfResultList::const_iterator pResult = results.begin(); |
| pResult != results.end(); ++pResult ) |
| { |
| output << std::fixed << std::setprecision( 0 ) << |
| std::setw( 8 ) << pResult->inStateRatio_ * 100 << ',' << |
| std::setw( 8 ) << pResult->nanoSecondsPerReaction_ << "\n"; |
| } |
| } |
| |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| int main() |
| { |
| std::cout << |
| "Boost.Statechart in-state reaction vs. transition performance test\n\n"; |
| std::cout << "Press <CR> to start the test: "; |
| |
| { |
| std::string input; |
| std::getline( std::cin, input ); |
| } |
| |
| TestAndWriteResults< uint1 >(); |
| TestAndWriteResults< uint2 >(); |
| TestAndWriteResults< uint3 >(); |
| |
| return 0; |
| } |