////////////////////////////////////////////////////////////////////////////// // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 # ifdef BOOST_MSVC # pragma warning( pop ) # endif #endif #include #include #include #include #include #include #include #include #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 to start the test: "; { std::string input; std::getline( std::cin, input ); } TestAndWriteResults< uint1 >(); TestAndWriteResults< uint2 >(); TestAndWriteResults< uint3 >(); return 0; }