/*************************************************************************\ * SPDX-License-Identifier: EPICS * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ #include #include #include #include #include "epicsInterrupt.h" #include "epicsAtomic.h" #include "epicsTime.h" #include "epicsUnitTest.h" #include "testMain.h" using std :: size_t; using namespace epics; using namespace atomic; class RefCtr { public: RefCtr (); ~RefCtr (); void reference (); void unreference (); private: size_t m_cnt; }; class Ownership { public: Ownership (); Ownership ( RefCtr & refCtr ); Ownership ( const Ownership & ); ~Ownership (); Ownership & operator = ( const Ownership & ); private: RefCtr * _pRefCtr; static RefCtr m_noOwnership; }; inline RefCtr :: RefCtr () { epicsAtomicSetSizeT ( & m_cnt, 0 ); } inline RefCtr :: ~RefCtr () { unsigned cnt = epicsAtomicGetSizeT ( & m_cnt ); assert ( cnt == 0u ); } inline void RefCtr :: reference () { epicsAtomicIncrSizeT ( & m_cnt ); } inline void RefCtr :: unreference () { epicsAtomicDecrSizeT ( & m_cnt ); } RefCtr Ownership :: m_noOwnership; inline Ownership :: Ownership () : _pRefCtr ( & m_noOwnership ) { m_noOwnership.reference (); } inline Ownership :: Ownership ( RefCtr & refCtr ) : _pRefCtr ( & refCtr ) { refCtr.reference (); } inline Ownership :: Ownership ( const Ownership & ownership ) : _pRefCtr ( ownership._pRefCtr ) { _pRefCtr->reference (); } inline Ownership :: ~Ownership () { _pRefCtr->unreference (); } inline Ownership & Ownership :: operator = ( const Ownership & ownership ) { RefCtr * const pOldRefCtr = _pRefCtr; _pRefCtr = ownership._pRefCtr; _pRefCtr->reference (); pOldRefCtr->unreference (); return *this; } inline Ownership retOwnership ( const Ownership & ownership ) { return Ownership ( ownership ); } inline Ownership recurRetOwner10 ( const Ownership & ownershipIn ) { Ownership ownership = retOwnership ( retOwnership ( retOwnership ( retOwnership ( retOwnership ( ownershipIn ) ) ) ) ); return retOwnership ( retOwnership ( retOwnership ( retOwnership ( retOwnership ( ownership ) ) ) ) ); } inline Ownership recurRetOwner100 ( const Ownership & ownershipIn ) { Ownership ownership = recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( ownershipIn ) ) ) ) ); return recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( recurRetOwner10 ( ownership ) ) ) ) ); } inline Ownership recurRetOwner1000 ( const Ownership & ownershipIn ) { Ownership ownership = recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( ownershipIn ) ) ) ) ); return recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( recurRetOwner100 ( ownership ) ) ) ) ); } inline void passRefOwnership ( const Ownership & ownershipIn, Ownership & ownershipOut ) { ownershipOut = ownershipIn; } inline void passRefOwnership10 ( const Ownership & ownershipIn, Ownership & ownershipOut ) { Ownership ownershipTmp0; passRefOwnership ( ownershipIn, ownershipTmp0 ); Ownership ownershipTmp1; passRefOwnership ( ownershipTmp0, ownershipTmp1 ); Ownership ownershipTmp2; passRefOwnership ( ownershipTmp1, ownershipTmp2 ); Ownership ownershipTmp3; passRefOwnership ( ownershipTmp2, ownershipTmp3 ); Ownership ownershipTmp4; passRefOwnership ( ownershipTmp3, ownershipTmp4 ); Ownership ownershipTmp5; passRefOwnership ( ownershipTmp4, ownershipTmp5 ); Ownership ownershipTmp6; passRefOwnership ( ownershipTmp5, ownershipTmp6 ); Ownership ownershipTmp7; passRefOwnership ( ownershipTmp6, ownershipTmp7 ); Ownership ownershipTmp8; passRefOwnership ( ownershipTmp7, ownershipTmp8 ); passRefOwnership ( ownershipTmp8, ownershipOut ); } inline void passRefOwnership100 ( const Ownership & ownershipIn, Ownership & ownershipOut ) { Ownership ownershipTmp0; passRefOwnership10 ( ownershipIn, ownershipTmp0 ); Ownership ownershipTmp1; passRefOwnership10 ( ownershipTmp0, ownershipTmp1 ); Ownership ownershipTmp2; passRefOwnership10 ( ownershipTmp1, ownershipTmp2 ); Ownership ownershipTmp3; passRefOwnership10 ( ownershipTmp2, ownershipTmp3 ); Ownership ownershipTmp4; passRefOwnership10 ( ownershipTmp3, ownershipTmp4 ); Ownership ownershipTmp5; passRefOwnership10 ( ownershipTmp4, ownershipTmp5 ); Ownership ownershipTmp6; passRefOwnership10 ( ownershipTmp5, ownershipTmp6 ); Ownership ownershipTmp7; passRefOwnership10 ( ownershipTmp6, ownershipTmp7 ); Ownership ownershipTmp8; passRefOwnership10 ( ownershipTmp7, ownershipTmp8 ); passRefOwnership10 ( ownershipTmp8, ownershipOut ); } inline void passRefOwnership1000 ( const Ownership & ownershipIn, Ownership & ownershipOut ) { Ownership ownershipTmp0; passRefOwnership100 ( ownershipIn, ownershipTmp0 ); Ownership ownershipTmp1; passRefOwnership100 ( ownershipTmp0, ownershipTmp1 ); Ownership ownershipTmp2; passRefOwnership100 ( ownershipTmp1, ownershipTmp2 ); Ownership ownershipTmp3; passRefOwnership100 ( ownershipTmp2, ownershipTmp3 ); Ownership ownershipTmp4; passRefOwnership100 ( ownershipTmp3, ownershipTmp4 ); Ownership ownershipTmp5; passRefOwnership100 ( ownershipTmp4, ownershipTmp5 ); Ownership ownershipTmp6; passRefOwnership100 ( ownershipTmp5, ownershipTmp6 ); Ownership ownershipTmp7; passRefOwnership100 ( ownershipTmp6, ownershipTmp7 ); Ownership ownershipTmp8; passRefOwnership100 ( ownershipTmp7, ownershipTmp8 ); passRefOwnership100 ( ownershipTmp8, ownershipOut ); } time_t extTime = 0; template < class T > class OrdinaryIncr { public: OrdinaryIncr () : m_target ( 0 ) {} void run (); void diagnostic ( double delay ); private: T m_target; }; // tests the time it takes to perform a call to an external // function and also increment an integer word. The // epicsInterruptIsInterruptContext function is an // out-of-line function implemented in a shareable library // so hopefully it won't be optimized away. template < class T > inline void OrdinaryIncr < T > :: run () { m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); m_target += epicsInterruptIsInterruptContext (); } template < class T > void OrdinaryIncr < T > :: diagnostic ( double delay ) { delay /= 10.0; delay *= 1e6; const char * const pName = typeid ( T ) . name (); testDiag ( "raw incr of \"%s\" and a NOOP function call takes %f microseconds", pName, delay ); } template < class T > class AtomicIncr { public: AtomicIncr () : m_target ( 0 ) {} void run (); void diagnostic ( double delay ); private: T m_target; }; template < class T > inline void AtomicIncr < T > :: run () { increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); increment ( m_target ); } template < class T > void AtomicIncr < T > :: diagnostic ( double delay ) { delay /= 10.0; delay *= 1e6; const char * const pName = typeid ( T ) . name (); testDiag ( "epicsAtomicIncr \"%s\" takes %f microseconds", pName, delay ); } template < class T > T trueValue (); template < class T > T falseValue (); // int template <> inline int trueValue < int > () { return 1; } template <> inline int falseValue < int > () { return 0; } // size_t template <> inline size_t trueValue < size_t > () { return 1u; } template <> inline size_t falseValue < size_t > () { return 0u; } // EpicsAtomicPtrT template <> inline EpicsAtomicPtrT trueValue < EpicsAtomicPtrT > () { static char c; return & c; } template <> inline EpicsAtomicPtrT falseValue < EpicsAtomicPtrT > () { return 0u; } template < class T > class AtomicCmpAndSwap { public: AtomicCmpAndSwap () : m_target ( 0 ) {} void run (); void diagnostic ( double delay ); private: T m_target; }; template < class T > inline void AtomicCmpAndSwap < T > :: run () { compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); compareAndSwap ( m_target, falseValue < T > (), trueValue < T > () ); } template < class T > void AtomicCmpAndSwap < T > :: diagnostic ( double delay ) { delay /= 10.0; delay *= 1e6; const char * const pName = typeid ( T ) . name (); testDiag ( "epicsAtomicCmpAndSwap of \"%s\" takes %f microseconds", pName, delay ); } template < class T > class AtomicSet { public: AtomicSet () : m_target ( 0 ) {} void run (); void diagnostic ( double delay ); private: T m_target; }; template < class T > inline void AtomicSet < T > :: run () { set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); set ( m_target, 0 ); } template < class T > void AtomicSet < T > :: diagnostic ( double delay ) { delay /= 10.0; delay *= 1e6; const char * const pName = typeid ( T ) . name (); testDiag ( "epicsAtomicSet of \"%s\" takes %f microseconds", pName, delay ); } static const unsigned N = 10000; void recursiveOwnershipRetPerformance () { RefCtr refCtr; epicsTime begin = epicsTime::getMonotonic (); for ( size_t i = 0; i < N; i++ ) { Ownership ownership ( refCtr ); recurRetOwner1000 ( ownership ); } double delay = epicsTime::getMonotonic () - begin; delay /= N * 1000u; // convert to delay per call delay *= 1e6; // convert to micro seconds testDiag ( "retOwnership() takes %f microseconds", delay ); } void ownershipPassRefPerformance () { RefCtr refCtr; epicsTime begin = epicsTime::getMonotonic (); for ( size_t i = 0; i < N; i++ ) { Ownership ownershipSrc ( refCtr ); Ownership ownershipDest; passRefOwnership1000 ( ownershipSrc, ownershipDest ); } double delay = epicsTime::getMonotonic () - begin; delay /= N * 1000u; // convert to delay per call delay *= 1e6; // convert to micro seconds testDiag ( "passRefOwnership() takes %f microseconds", delay ); } template < class T > class Ten { public: void run (); void diagnostic ( double delay ); typedef Ten < Ten < T > > Hundred; typedef Ten < Hundred > Thousand; private: T m_target; }; template < class T > inline void Ten < T > :: run () { m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); m_target.run (); } template < class T > void Ten < T > :: diagnostic ( double delay ) { m_target.diagnostic ( delay / 10.0 ); } template < class T > void measurePerformance () { epicsTime begin = epicsTime::getMonotonic (); T target; for ( size_t i = 0; i < N; i++ ) { target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); target.run (); } double delay = epicsTime::getMonotonic () - begin; delay /= ( N * 10u ); // convert to delay per call target.diagnostic ( delay ); } template < class T > void measure () { measurePerformance < typename Ten < T > :: Hundred > (); } // template instances, needed for vxWorks 5.5.2 #ifdef _MSC_VER # pragma warning ( push ) # pragma warning ( disable:4660 ) #endif template class AtomicSet < int >; template class AtomicSet < size_t >; template class AtomicSet < EpicsAtomicPtrT >; template class OrdinaryIncr < int >; template class OrdinaryIncr < size_t >; template class AtomicIncr < int >; template class AtomicIncr < size_t >; template class AtomicCmpAndSwap < int >; template class AtomicCmpAndSwap < size_t >; template class AtomicCmpAndSwap < EpicsAtomicPtrT >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template class Ten > >; template void measurePerformance < Ten < Ten < AtomicSet < int > > > >(void); template void measurePerformance < Ten < Ten < AtomicSet < size_t > > > >(void); template void measurePerformance < Ten < Ten < AtomicSet < EpicsAtomicPtrT > > > >(void); template void measurePerformance < Ten < Ten < OrdinaryIncr < int > > > >(void); template void measurePerformance < Ten < Ten < OrdinaryIncr < size_t > > > >(void); template void measurePerformance < Ten < Ten < AtomicIncr < int > > > >(void); template void measurePerformance < Ten < Ten < AtomicIncr < size_t > > > >(void); template void measurePerformance < Ten < Ten < AtomicCmpAndSwap < int > > > >(void); template void measurePerformance < Ten < Ten < AtomicCmpAndSwap < size_t > > > >(void); template void measurePerformance < Ten < Ten < AtomicCmpAndSwap < EpicsAtomicPtrT > > > >(void); template void measure < AtomicSet < int > > (void); template void measure < AtomicSet < size_t > > (void); template void measure < AtomicSet < EpicsAtomicPtrT > > (void); template void measure < OrdinaryIncr < int > > (void); template void measure < OrdinaryIncr < size_t > > (void); template void measure < AtomicIncr < int > > (void); template void measure < AtomicIncr < size_t > > (void); template void measure < AtomicCmpAndSwap < int > > (void); template void measure < AtomicCmpAndSwap < size_t > > (void); template void measure < AtomicCmpAndSwap < EpicsAtomicPtrT > > (void); #ifdef _MSC_VER # pragma warning ( pop ) #endif MAIN ( epicsAtomicPerform ) { testPlan ( 0 ); // // The tests running here are measuring fast // functions so they tend to be impacted // by where the cache lines are wrt to the // virtual pages perhaps // measure < AtomicSet < int > > (); measure < AtomicSet < size_t > > (); measure < AtomicSet < void * > > (); measure < OrdinaryIncr < int > > (); measure < OrdinaryIncr < size_t > > (); measure < AtomicIncr < int > > (); measure < AtomicIncr < size_t > > (); measure < AtomicCmpAndSwap < int > > (); measure < AtomicCmpAndSwap < size_t > > (); measure < AtomicCmpAndSwap < void * > > (); recursiveOwnershipRetPerformance (); ownershipPassRefPerformance (); return testDone(); }