JD2022-TU1/main/extern/gear4/gear_testing/performance/perftest_delegate.h

280 lines
16 KiB
C++

#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_