#ifndef _GEAR_TESTING__PERFTEST_DELEGATE_H_ #define _GEAR_TESTING__PERFTEST_DELEGATE_H_ #include "perftest_traits.h" namespace G4 { // // *** FunctionDelegate *** // // Magic! The lynchpin for performance testing, around which all of the other code revolves. // // This is a template class whose sole purpose is to invoke some function or class method. // The main loop of the performance-testing framework invokes functions generically through their delegate. // Functions and methods differ based on their signature. This tells us how to invoke the function when // it is being tested. // We can set the delegate up as a template class, but it requires specialization based on four elements: // * The actual function being invoked // * The number of arguments required to invoke the function, including 'this' for non-static class methods // * Whether or not the function returns a non-void result // * The calling convention, based on whether or not the function is a non-static class method // Note that the last three elements can be deduced from the first element. // // The actual implementation is somewhat uglier than it needs to be, because we need to tell the delegate exactly what function // to invoke. In theory, if we know the function signature, then we can use a function pointer as a template parameter, // and perform the actual invocation through the function pointer. We ideally want to declare a delegate class as follows... // // template < typename Signature, // Signature Function, // unsigned int NumArgs = FunctionTraits< Signature >::NumArgs, // bool IsClassMethod = FunctionTraits< Signature >::IsClassMethod, // bool HasReturnValue = FunctionTraits< Signature >::HasReturnValue > // struct FunctionDelegate; // // ...and then specialize. For example, if the delegate is a non-static class method with 2 arguments that returns a non-void value: // // template < typename Signature, Signature Function > // struct FunctionDelegate< Signature, Function, 3, true, true > // { // typedef FunctionTraits< Signature > Traits; // G4_FORCEINLINE static void Invoke( typename Traits::ArgList& a_TestVals ) // { // G4::get< Traits::NumArgs >( a_TestVals ) = (G4::get< 0 >( a_TestVals ).*Function)( G4::get< 1 >, G4::get< 2 > ); // } // }; // // This actually works! But there is unfortunately a major gotcha... // If a function was declared as 'inline', then a compiler may not be smart enough actually to actually generate inline // code when it is invoked through a templated function pointer. // Our tests indicate that the Microsoft compilers and Clang are able to generate inline code of this sort, // while the Sony (SNC) and Nintendo (Green Hills) compilers cannot. GCC is unconfirmed, but probably supports it. // This would not normally be a problem, but since we are doing performance testing, we want the inline hint to be // respected whenever possible, as this most closely resembles how the functions will be invoked 'in the wild'. // // How do we resolve this? // Since invoking the function through a templated pointer forces it non-inline (on some platforms), we must fall back to // invoking the function directly. We already use macros to make specialization easier, so we can add the name of the // function name as an extra macro parameter. // The big drawback of this approach is that in effect we are explicitly specializing GenericDelegate for every function. // This makes things somewhat messier, not to mention more verbose, but it solves our problem. // #define G4_CAN_CAST_FUNCTION_POINTERS_AT_COMPILE_TIME G4_PLATFORM_MS // We use a different delegate creation method on platforms that cannot inline functions that are used as template parameters // Code enabled by this macro can be deprecated/removed when we drop support for these platforms. #define G4_CAN_INLINE_FUNCTION_TEMPLATES \ !( ( G4_PLATFORM == G4_PLATFORM_PS3_PPU ) || ( G4_PLATFORM == G4_PLATFORM_VITA ) || ( G4_PLATFORM == G4_PLATFORM_CAFE ) ) #define PERFTEST_USE_FUNCTION_POINTERS ( G4_CAN_INLINE_FUNCTION_TEMPLATES && G4_CAN_CAST_FUNCTION_POINTERS_AT_COMPILE_TIME ) // Set this value to 1 to make performance tests take into account the time required to copy a function's return value into a destination variable #define PERFTEST_COPY_RESULTS 1 #if PERFTEST_COPY_RESULTS #define DELEGATE_USE_RESULT( _X ) \ G4::get< Traits::NumArgs >( a_TestVals ) = _X; #else /// This struct is used to prevent compiler from optimizing whole perftest away template < typename _TYPE > struct ResultHolder { // This static variable makes impossible to optimize variables away. // However, the value it is pointing to is temporary, so it should never be used anywhere else. static const _TYPE* s_pResult; G4_FORCEINLINE static const _TYPE& Hold( const _TYPE& a_Value ) { s_pResult = &a_Value; return( *s_pResult ); } }; template < typename _TYPE > const _TYPE* ResultHolder< _TYPE >::s_pResult; template < typename _TYPE > G4_FORCEINLINE const _TYPE& PerfTestUnused( const _TYPE& a_Value ) { return ResultHolder< _TYPE >::Hold( a_Value ); } #define DELEGATE_USE_RESULT( _X ) \ G4_UNUSED( a_TestVals ); \ PerfTestUnused( _X ); #endif #define FUNC_ARGS_STUB( _X ) G4::get< _X >( a_TestVals ) #define FUNC_ARGS( _X ) LIST ## _X ( FUNC_ARGS_STUB ) #define METHOD_ARGS_STUB( _X ) G4::get< _X + 1 >( a_TestVals ) #define METHOD_ARGS( _X ) LIST ## _X( METHOD_ARGS_STUB ) #if ( !PERFTEST_USE_FUNCTION_POINTERS ) #define DELEGATE_DECLARATION( _NAME ) \ template < typename SIGNATURE, \ unsigned int NumArgs = FunctionTraits< SIGNATURE >::NumArgs, \ bool HasReturnValue = FunctionTraits< SIGNATURE >::HasReturnValue > \ struct FunctionDelegate ## _NAME; \ template < typename SIGNATURE > \ FunctionDelegate ## _NAME< SIGNATURE > MakeFunctionDelegate ## _NAME( SIGNATURE ) \ { \ return( FunctionDelegate ## _NAME< SIGNATURE >() ); \ } #define METHOD_DELEGATE_SPECIALIZATION( _N, _NAME, _FUNCTION ) \ template < typename SIGNATURE > \ struct FunctionDelegate ## _NAME< SIGNATURE, _N+1, true > \ { \ typedef FunctionTraits< SIGNATURE > Traits; \ G4_FORCEINLINE static void Invoke( typename Traits::ArgList& a_TestVals ) \ { \ DELEGATE_USE_RESULT( G4::get< 0 >( a_TestVals )._FUNCTION( METHOD_ARGS( _N ) ) ); \ } \ }; \ template < typename SIGNATURE > \ struct FunctionDelegate ## _NAME< SIGNATURE, _N+1, false > \ { \ typedef FunctionTraits< SIGNATURE > Traits; \ G4_FORCEINLINE static void Invoke( typename Traits::ArgList& a_TestVals ) \ { \ G4::get< 0 >( a_TestVals )._FUNCTION( METHOD_ARGS( _N ) ); \ } \ }; #define FUNCTION_DELEGATE_SPECIALIZATION( _N, _NAME, _FUNCTION ) \ template < typename SIGNATURE > \ struct FunctionDelegate ## _NAME< SIGNATURE, _N, true > \ { \ typedef FunctionTraits< SIGNATURE > Traits; \ G4_FORCEINLINE static void Invoke( typename Traits::ArgList& a_TestVals ) \ { \ DELEGATE_USE_RESULT( (*static_cast< SIGNATURE >(&_FUNCTION))( FUNC_ARGS( _N ) ) ); \ } \ }; \ template < typename SIGNATURE > \ struct FunctionDelegate ## _NAME< SIGNATURE, _N, false > \ { \ typedef FunctionTraits< SIGNATURE > Traits; \ G4_FORCEINLINE static void Invoke( typename Traits::ArgList& a_TestVals ) \ { \ (*static_cast< SIGNATURE >(&_FUNCTION))( FUNC_ARGS( _N ) ); \ } \ }; #define DECLARE_METHOD_DELEGATE( _NAME, _FUNCTION ) \ namespace G4 { \ DELEGATE_DECLARATION( _NAME ) \ METHOD_DELEGATE_SPECIALIZATION( 0, _NAME, _FUNCTION ) \ METHOD_DELEGATE_SPECIALIZATION( 1, _NAME, _FUNCTION ) \ METHOD_DELEGATE_SPECIALIZATION( 2, _NAME, _FUNCTION ) \ METHOD_DELEGATE_SPECIALIZATION( 3, _NAME, _FUNCTION ) \ METHOD_DELEGATE_SPECIALIZATION( 4, _NAME, _FUNCTION ) \ METHOD_DELEGATE_SPECIALIZATION( 5, _NAME, _FUNCTION ) \ METHOD_DELEGATE_SPECIALIZATION( 6, _NAME, _FUNCTION ) \ METHOD_DELEGATE_SPECIALIZATION( 7, _NAME, _FUNCTION ) \ METHOD_DELEGATE_SPECIALIZATION( 8, _NAME, _FUNCTION ) \ METHOD_DELEGATE_SPECIALIZATION( 9, _NAME, _FUNCTION ) } #define DECLARE_FUNCTION_DELEGATE( _NAME, _FUNCTION ) \ namespace G4 { \ DELEGATE_DECLARATION( _NAME ) \ FUNCTION_DELEGATE_SPECIALIZATION( 0, _NAME, _FUNCTION ) \ FUNCTION_DELEGATE_SPECIALIZATION( 1, _NAME, _FUNCTION ) \ FUNCTION_DELEGATE_SPECIALIZATION( 2, _NAME, _FUNCTION ) \ FUNCTION_DELEGATE_SPECIALIZATION( 3, _NAME, _FUNCTION ) \ FUNCTION_DELEGATE_SPECIALIZATION( 4, _NAME, _FUNCTION ) \ FUNCTION_DELEGATE_SPECIALIZATION( 5, _NAME, _FUNCTION ) \ FUNCTION_DELEGATE_SPECIALIZATION( 6, _NAME, _FUNCTION ) \ FUNCTION_DELEGATE_SPECIALIZATION( 7, _NAME, _FUNCTION ) \ FUNCTION_DELEGATE_SPECIALIZATION( 8, _NAME, _FUNCTION ) \ FUNCTION_DELEGATE_SPECIALIZATION( 9, _NAME, _FUNCTION ) } #endif // ( PERFTEST_USE_FUNCTION_POINTERS ) // ---------------- template < typename SIGNATURE, SIGNATURE FUNCTION, unsigned int NumArgs = FunctionTraits< SIGNATURE >::NumArgs, bool IsClassMethod = FunctionTraits< SIGNATURE >::IsClassMethod, bool HasReturnValue = FunctionTraits< SIGNATURE >::HasReturnValue > struct FunctionDelegate; #define GENERIC_DELEGATE( _N ) \ template < typename SIGNATURE, SIGNATURE FUNCTION > \ struct FunctionDelegate< SIGNATURE, FUNCTION, _N+1, true, true > \ { \ typedef FunctionTraits< SIGNATURE > Traits; \ G4_FORCEINLINE static void Invoke( typename Traits::ArgList& a_TestVals ) \ { \ DELEGATE_USE_RESULT( (G4::get< 0 >( a_TestVals ).*FUNCTION)( METHOD_ARGS( _N ) ) ); \ } \ }; \ template < typename SIGNATURE, SIGNATURE FUNCTION > \ struct FunctionDelegate< SIGNATURE, FUNCTION, _N, false, true > \ { \ typedef FunctionTraits< SIGNATURE > Traits; \ G4_FORCEINLINE static void Invoke( typename Traits::ArgList& a_TestVals ) \ { \ DELEGATE_USE_RESULT( (*FUNCTION)( FUNC_ARGS( _N ) ) ); \ } \ }; \ template < typename SIGNATURE, SIGNATURE FUNCTION > \ struct FunctionDelegate< SIGNATURE, FUNCTION, _N+1, true, false > \ { \ typedef FunctionTraits< SIGNATURE > Traits; \ G4_FORCEINLINE static void Invoke( typename Traits::ArgList& a_TestVals ) \ { \ (G4::get< 0 >( a_TestVals ).*FUNCTION)( METHOD_ARGS( _N ) ); \ } \ }; \ template < typename SIGNATURE, SIGNATURE FUNCTION > \ struct FunctionDelegate< SIGNATURE, FUNCTION, _N, false, false > \ { \ typedef FunctionTraits< SIGNATURE > Traits; \ G4_FORCEINLINE static void Invoke( typename Traits::ArgList& a_TestVals ) \ { \ (*FUNCTION)( FUNC_ARGS( _N ) ); \ } \ }; GENERIC_DELEGATE( 0 ) GENERIC_DELEGATE( 1 ) GENERIC_DELEGATE( 2 ) GENERIC_DELEGATE( 3 ) GENERIC_DELEGATE( 4 ) GENERIC_DELEGATE( 5 ) GENERIC_DELEGATE( 6 ) GENERIC_DELEGATE( 7 ) GENERIC_DELEGATE( 8 ) GENERIC_DELEGATE( 9 ) // // *** DelegateFactory *** // // Creates a delegate for a specific function template < typename SIGNATURE > struct DelegateFactory { template < SIGNATURE METHOD > static FunctionDelegate< SIGNATURE, METHOD > CreateDelegate( void ) { return( FunctionDelegate< SIGNATURE, METHOD >() ); } }; template < typename SIGNATURE > DelegateFactory< SIGNATURE > GetDelegateFactory( SIGNATURE ) { return( DelegateFactory< SIGNATURE >() ); } } // namespace G4 #endif // _GEAR_TESTING__PERFTEST_DELEGATE_H_