280 lines
16 KiB
C++
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_
|