597 lines
14 KiB
C++
597 lines
14 KiB
C++
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
|
||
//
|
||
// Purpose:
|
||
//
|
||
// $NoKeywords: $
|
||
//===========================================================================//
|
||
|
||
#ifndef FASTTIMER_H
|
||
#define FASTTIMER_H
|
||
#ifdef _WIN32
|
||
#pragma once
|
||
#endif
|
||
|
||
#include <assert.h>
|
||
#include "tier0/platform.h"
|
||
#ifdef _PS3
|
||
#include "sys/sys_time.h"
|
||
#else
|
||
inline uint64 sys_time_get_timebase_frequency()
|
||
{
|
||
DebuggerBreak(); // Error("sys_time_get_timebase_frequency called on non-PS3 platform.");
|
||
return 1; // this function should never ever be called.
|
||
}
|
||
#endif
|
||
|
||
PLATFORM_INTERFACE uint64 g_ClockSpeed;
|
||
PLATFORM_INTERFACE unsigned long g_dwClockSpeed;
|
||
|
||
PLATFORM_INTERFACE double g_ClockSpeedMicrosecondsMultiplier;
|
||
PLATFORM_INTERFACE double g_ClockSpeedMillisecondsMultiplier;
|
||
PLATFORM_INTERFACE double g_ClockSpeedSecondsMultiplier;
|
||
|
||
class CCycleCount
|
||
{
|
||
friend class CFastTimer;
|
||
|
||
public:
|
||
CCycleCount();
|
||
CCycleCount( uint64 cycles );
|
||
|
||
void Sample(); // Sample the clock. This takes about 34 clocks to execute (or 26,000 calls per millisecond on a P900).
|
||
|
||
void Init(); // Set to zero.
|
||
void Init( float initTimeMsec );
|
||
void Init( double initTimeMsec ) { Init( (float)initTimeMsec ); }
|
||
void Init( uint64 cycles );
|
||
bool IsLessThan( CCycleCount const &other ) const; // Compare two counts.
|
||
|
||
// Convert to other time representations. These functions are slow, so it's preferable to call them
|
||
// during display rather than inside a timing block.
|
||
unsigned long GetCycles() const;
|
||
uint64 GetLongCycles() const;
|
||
|
||
unsigned long GetMicroseconds() const;
|
||
uint64 GetUlMicroseconds() const;
|
||
double GetMicrosecondsF() const;
|
||
void SetMicroseconds( unsigned long nMicroseconds );
|
||
|
||
unsigned long GetMilliseconds() const;
|
||
double GetMillisecondsF() const;
|
||
|
||
double GetSeconds() const;
|
||
|
||
CCycleCount& operator+=( CCycleCount const &other );
|
||
|
||
// dest = rSrc1 + rSrc2
|
||
static void Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together.
|
||
|
||
// dest = rSrc1 - rSrc2
|
||
static void Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together.
|
||
|
||
static uint64 GetTimestamp();
|
||
|
||
uint64 m_Int64;
|
||
};
|
||
|
||
class CClockSpeedInit
|
||
{
|
||
public:
|
||
CClockSpeedInit()
|
||
{
|
||
Init();
|
||
}
|
||
|
||
static void Init()
|
||
{
|
||
const CPUInformation& pi = GetCPUInformation();
|
||
|
||
if ( IsX360() )
|
||
{
|
||
// cycle counter runs as doc'd at 1/64 Xbox 3.2GHz clock speed, thus 50 Mhz
|
||
g_ClockSpeed = pi.m_Speed / 64L;
|
||
}
|
||
else if ( IsPS3() )
|
||
{
|
||
g_ClockSpeed = sys_time_get_timebase_frequency(); // CPU clock rate is totally unrelated to time base register frequency on PS3
|
||
}
|
||
else
|
||
{
|
||
g_ClockSpeed = pi.m_Speed;
|
||
}
|
||
g_dwClockSpeed = (unsigned long)g_ClockSpeed;
|
||
|
||
g_ClockSpeedMicrosecondsMultiplier = 1000000.0 / (double)g_ClockSpeed;
|
||
g_ClockSpeedMillisecondsMultiplier = 1000.0 / (double)g_ClockSpeed;
|
||
g_ClockSpeedSecondsMultiplier = 1.0f / (double)g_ClockSpeed;
|
||
}
|
||
};
|
||
|
||
class CFastTimer
|
||
{
|
||
public:
|
||
// These functions are fast to call and should be called from your sampling code.
|
||
void Start();
|
||
void End();
|
||
|
||
const CCycleCount & GetDuration() const; // Get the elapsed time between Start and End calls.
|
||
CCycleCount GetDurationInProgress() const; // Call without ending. Not that cheap.
|
||
|
||
// Return number of cycles per second on this processor.
|
||
static inline unsigned long GetClockSpeed();
|
||
|
||
private:
|
||
CCycleCount m_Duration;
|
||
#ifdef DEBUG_FASTTIMER
|
||
bool m_bRunning; // Are we currently running?
|
||
#endif
|
||
};
|
||
|
||
|
||
// This is a helper class that times whatever block of code it's in
|
||
class CTimeScope
|
||
{
|
||
public:
|
||
CTimeScope( CFastTimer *pTimer );
|
||
~CTimeScope();
|
||
|
||
private:
|
||
CFastTimer *m_pTimer;
|
||
};
|
||
|
||
inline CTimeScope::CTimeScope( CFastTimer *pTotal )
|
||
{
|
||
m_pTimer = pTotal;
|
||
m_pTimer->Start();
|
||
}
|
||
|
||
inline CTimeScope::~CTimeScope()
|
||
{
|
||
m_pTimer->End();
|
||
}
|
||
|
||
// This is a helper class that times whatever block of code it's in and
|
||
// adds the total (int microseconds) to a global counter.
|
||
class CTimeAdder
|
||
{
|
||
public:
|
||
CTimeAdder( CCycleCount *pTotal );
|
||
~CTimeAdder();
|
||
|
||
void End();
|
||
|
||
private:
|
||
CCycleCount *m_pTotal;
|
||
CFastTimer m_Timer;
|
||
};
|
||
|
||
inline CTimeAdder::CTimeAdder( CCycleCount *pTotal )
|
||
{
|
||
m_pTotal = pTotal;
|
||
m_Timer.Start();
|
||
}
|
||
|
||
inline CTimeAdder::~CTimeAdder()
|
||
{
|
||
End();
|
||
}
|
||
|
||
inline void CTimeAdder::End()
|
||
{
|
||
if( m_pTotal )
|
||
{
|
||
m_Timer.End();
|
||
*m_pTotal += m_Timer.GetDuration();
|
||
m_pTotal = 0;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// -------------------------------------------------------------------------- //
|
||
// Simple tool to support timing a block of code, and reporting the results on
|
||
// program exit or at each iteration
|
||
//
|
||
// Macros used because dbg.h uses this header, thus Msg() is unavailable
|
||
// -------------------------------------------------------------------------- //
|
||
|
||
#define PROFILE_SCOPE(name) \
|
||
class C##name##ACC : public CAverageCycleCounter \
|
||
{ \
|
||
public: \
|
||
~C##name##ACC() \
|
||
{ \
|
||
Msg("%-48s: %6.3f avg (%8.1f total, %7.3f peak, %5d iters)\n", \
|
||
#name, \
|
||
GetAverageMilliseconds(), \
|
||
GetTotalMilliseconds(), \
|
||
GetPeakMilliseconds(), \
|
||
GetIters() ); \
|
||
} \
|
||
}; \
|
||
static C##name##ACC name##_ACC; \
|
||
CAverageTimeMarker name##_ATM( &name##_ACC )
|
||
|
||
#define TIME_SCOPE(name) \
|
||
class CTimeScopeMsg_##name \
|
||
{ \
|
||
public: \
|
||
CTimeScopeMsg_##name() { m_Timer.Start(); } \
|
||
~CTimeScopeMsg_##name() \
|
||
{ \
|
||
m_Timer.End(); \
|
||
Msg( #name "time: %.4fms\n", m_Timer.GetDuration().GetMillisecondsF() ); \
|
||
} \
|
||
private: \
|
||
CFastTimer m_Timer; \
|
||
} name##_TSM;
|
||
|
||
|
||
// -------------------------------------------------------------------------- //
|
||
|
||
class CAverageCycleCounter
|
||
{
|
||
public:
|
||
CAverageCycleCounter();
|
||
|
||
void Init();
|
||
void MarkIter( const CCycleCount &duration );
|
||
|
||
unsigned GetIters() const;
|
||
|
||
double GetAverageMilliseconds() const;
|
||
double GetTotalMilliseconds() const;
|
||
double GetPeakMilliseconds() const;
|
||
|
||
private:
|
||
unsigned m_nIters;
|
||
CCycleCount m_Total;
|
||
CCycleCount m_Peak;
|
||
bool m_fReport;
|
||
const tchar *m_pszName;
|
||
};
|
||
|
||
// -------------------------------------------------------------------------- //
|
||
|
||
class CAverageTimeMarker
|
||
{
|
||
public:
|
||
CAverageTimeMarker( CAverageCycleCounter *pCounter );
|
||
~CAverageTimeMarker();
|
||
|
||
private:
|
||
CAverageCycleCounter *m_pCounter;
|
||
CFastTimer m_Timer;
|
||
};
|
||
|
||
|
||
// -------------------------------------------------------------------------- //
|
||
// CCycleCount inlines.
|
||
// -------------------------------------------------------------------------- //
|
||
|
||
inline CCycleCount::CCycleCount()
|
||
{
|
||
Init( (uint64)0 );
|
||
}
|
||
|
||
inline CCycleCount::CCycleCount( uint64 cycles )
|
||
{
|
||
Init( cycles );
|
||
}
|
||
|
||
inline void CCycleCount::Init()
|
||
{
|
||
Init( (uint64)0 );
|
||
}
|
||
|
||
inline void CCycleCount::Init( float initTimeMsec )
|
||
{
|
||
if ( g_ClockSpeedMillisecondsMultiplier > 0 )
|
||
Init( (uint64)(initTimeMsec / g_ClockSpeedMillisecondsMultiplier) );
|
||
else
|
||
Init( (uint64)0 );
|
||
}
|
||
|
||
inline void CCycleCount::Init( uint64 cycles )
|
||
{
|
||
m_Int64 = cycles;
|
||
}
|
||
|
||
inline void CCycleCount::Sample()
|
||
{
|
||
m_Int64 = Plat_Rdtsc();
|
||
}
|
||
|
||
inline CCycleCount& CCycleCount::operator+=( CCycleCount const &other )
|
||
{
|
||
m_Int64 += other.m_Int64;
|
||
return *this;
|
||
}
|
||
|
||
|
||
inline void CCycleCount::Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest )
|
||
{
|
||
dest.m_Int64 = rSrc1.m_Int64 + rSrc2.m_Int64;
|
||
}
|
||
|
||
inline void CCycleCount::Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest )
|
||
{
|
||
dest.m_Int64 = rSrc1.m_Int64 - rSrc2.m_Int64;
|
||
}
|
||
|
||
inline uint64 CCycleCount::GetTimestamp()
|
||
{
|
||
CCycleCount c;
|
||
c.Sample();
|
||
return c.GetLongCycles();
|
||
}
|
||
|
||
inline bool CCycleCount::IsLessThan(CCycleCount const &other) const
|
||
{
|
||
return m_Int64 < other.m_Int64;
|
||
}
|
||
|
||
|
||
inline unsigned long CCycleCount::GetCycles() const
|
||
{
|
||
return (unsigned long)m_Int64;
|
||
}
|
||
|
||
inline uint64 CCycleCount::GetLongCycles() const
|
||
{
|
||
return m_Int64;
|
||
}
|
||
|
||
inline unsigned long CCycleCount::GetMicroseconds() const
|
||
{
|
||
return (unsigned long)((m_Int64 * 1000000) / g_ClockSpeed);
|
||
}
|
||
|
||
inline uint64 CCycleCount::GetUlMicroseconds() const
|
||
{
|
||
return ((m_Int64 * 1000000) / g_ClockSpeed);
|
||
}
|
||
|
||
|
||
inline double CCycleCount::GetMicrosecondsF() const
|
||
{
|
||
return (double)( m_Int64 * g_ClockSpeedMicrosecondsMultiplier );
|
||
}
|
||
|
||
|
||
inline void CCycleCount::SetMicroseconds( unsigned long nMicroseconds )
|
||
{
|
||
m_Int64 = ((uint64)nMicroseconds * g_ClockSpeed) / 1000000;
|
||
}
|
||
|
||
|
||
inline unsigned long CCycleCount::GetMilliseconds() const
|
||
{
|
||
return (unsigned long)((m_Int64 * 1000) / g_ClockSpeed);
|
||
}
|
||
|
||
|
||
inline double CCycleCount::GetMillisecondsF() const
|
||
{
|
||
return (double)( m_Int64 * g_ClockSpeedMillisecondsMultiplier );
|
||
}
|
||
|
||
|
||
inline double CCycleCount::GetSeconds() const
|
||
{
|
||
return (double)( m_Int64 * g_ClockSpeedSecondsMultiplier );
|
||
}
|
||
|
||
|
||
// -------------------------------------------------------------------------- //
|
||
// CFastTimer inlines.
|
||
// -------------------------------------------------------------------------- //
|
||
inline void CFastTimer::Start()
|
||
{
|
||
m_Duration.Sample();
|
||
#ifdef DEBUG_FASTTIMER
|
||
m_bRunning = true;
|
||
#endif
|
||
}
|
||
|
||
|
||
inline void CFastTimer::End()
|
||
{
|
||
CCycleCount cnt;
|
||
cnt.Sample();
|
||
if ( IsX360() )
|
||
{
|
||
// have to handle rollover, hires timer is only accurate to 32 bits
|
||
// more than one overflow should not have occurred, otherwise caller should use a slower timer
|
||
if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 )
|
||
{
|
||
// rollover occurred
|
||
cnt.m_Int64 += 0x100000000LL;
|
||
}
|
||
}
|
||
|
||
m_Duration.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64;
|
||
|
||
#ifdef DEBUG_FASTTIMER
|
||
m_bRunning = false;
|
||
#endif
|
||
}
|
||
|
||
inline CCycleCount CFastTimer::GetDurationInProgress() const
|
||
{
|
||
CCycleCount cnt;
|
||
cnt.Sample();
|
||
if ( IsX360() )
|
||
{
|
||
// have to handle rollover, hires timer is only accurate to 32 bits
|
||
// more than one overflow should not have occurred, otherwise caller should use a slower timer
|
||
if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 )
|
||
{
|
||
// rollover occurred
|
||
cnt.m_Int64 += 0x100000000LL;
|
||
}
|
||
}
|
||
|
||
CCycleCount result;
|
||
result.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64;
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
inline unsigned long CFastTimer::GetClockSpeed()
|
||
{
|
||
return g_dwClockSpeed;
|
||
}
|
||
|
||
|
||
inline CCycleCount const& CFastTimer::GetDuration() const
|
||
{
|
||
#ifdef DEBUG_FASTTIMER
|
||
assert( !m_bRunning );
|
||
#endif
|
||
return m_Duration;
|
||
}
|
||
|
||
|
||
// -------------------------------------------------------------------------- //
|
||
// CAverageCycleCounter inlines
|
||
|
||
inline CAverageCycleCounter::CAverageCycleCounter()
|
||
: m_nIters( 0 )
|
||
{
|
||
}
|
||
|
||
inline void CAverageCycleCounter::Init()
|
||
{
|
||
m_Total.Init();
|
||
m_Peak.Init();
|
||
m_nIters = 0;
|
||
}
|
||
|
||
inline void CAverageCycleCounter::MarkIter( const CCycleCount &duration )
|
||
{
|
||
++m_nIters;
|
||
m_Total += duration;
|
||
if ( m_Peak.IsLessThan( duration ) )
|
||
m_Peak = duration;
|
||
}
|
||
|
||
inline unsigned CAverageCycleCounter::GetIters() const
|
||
{
|
||
return m_nIters;
|
||
}
|
||
|
||
inline double CAverageCycleCounter::GetAverageMilliseconds() const
|
||
{
|
||
if ( m_nIters )
|
||
return (m_Total.GetMillisecondsF() / (double)m_nIters);
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
inline double CAverageCycleCounter::GetTotalMilliseconds() const
|
||
{
|
||
return m_Total.GetMillisecondsF();
|
||
}
|
||
|
||
inline double CAverageCycleCounter::GetPeakMilliseconds() const
|
||
{
|
||
return m_Peak.GetMillisecondsF();
|
||
}
|
||
|
||
// -------------------------------------------------------------------------- //
|
||
|
||
inline CAverageTimeMarker::CAverageTimeMarker( CAverageCycleCounter *pCounter )
|
||
{
|
||
m_pCounter = pCounter;
|
||
m_Timer.Start();
|
||
}
|
||
|
||
inline CAverageTimeMarker::~CAverageTimeMarker()
|
||
{
|
||
m_Timer.End();
|
||
m_pCounter->MarkIter( m_Timer.GetDuration() );
|
||
}
|
||
|
||
|
||
// CLimitTimer
|
||
// Use this to time whether a desired interval of time has passed. It's extremely fast
|
||
// to check while running. NOTE: CMicroSecOverage() and CMicroSecLeft() are not as fast to check.
|
||
class CLimitTimer
|
||
{
|
||
public:
|
||
CLimitTimer() {}
|
||
CLimitTimer( uint64 cMicroSecDuration ) { SetLimit( cMicroSecDuration ); }
|
||
void SetLimit( uint64 m_cMicroSecDuration );
|
||
bool BLimitReached() const;
|
||
|
||
int CMicroSecOverage() const;
|
||
uint64 CMicroSecLeft() const;
|
||
|
||
private:
|
||
uint64 m_lCycleLimit;
|
||
};
|
||
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose: Initializes the limit timer with a period of time to measure.
|
||
// Input : cMicroSecDuration - How long a time period to measure
|
||
//-----------------------------------------------------------------------------
|
||
inline void CLimitTimer::SetLimit( uint64 cMicroSecDuration )
|
||
{
|
||
uint64 dlCycles = ( ( uint64 ) cMicroSecDuration * ( uint64 ) g_dwClockSpeed ) / ( uint64 ) 1000000L;
|
||
CCycleCount cycleCount;
|
||
cycleCount.Sample();
|
||
m_lCycleLimit = cycleCount.GetLongCycles() + dlCycles;
|
||
}
|
||
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose: Determines whether our specified time period has passed
|
||
// Output: true if at least the specified time period has passed
|
||
//-----------------------------------------------------------------------------
|
||
inline bool CLimitTimer::BLimitReached() const
|
||
{
|
||
CCycleCount cycleCount;
|
||
cycleCount.Sample();
|
||
return ( cycleCount.GetLongCycles() >= m_lCycleLimit );
|
||
}
|
||
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose: If we're over our specified time period, return the amount of the overage.
|
||
// Output: # of microseconds since we reached our specified time period.
|
||
//-----------------------------------------------------------------------------
|
||
inline int CLimitTimer::CMicroSecOverage() const
|
||
{
|
||
CCycleCount cycleCount;
|
||
cycleCount.Sample();
|
||
uint64 lcCycles = cycleCount.GetLongCycles();
|
||
|
||
if ( lcCycles < m_lCycleLimit )
|
||
return 0;
|
||
|
||
return( ( int ) ( ( lcCycles - m_lCycleLimit ) * ( uint64 ) 1000000L / g_dwClockSpeed ) );
|
||
}
|
||
|
||
|
||
//-----------------------------------------------------------------------------
|
||
// Purpose: If we're under our specified time period, return the amount under.
|
||
// Output: # of microseconds until we reached our specified time period, 0 if we've passed it
|
||
//-----------------------------------------------------------------------------
|
||
inline uint64 CLimitTimer::CMicroSecLeft() const
|
||
{
|
||
CCycleCount cycleCount;
|
||
cycleCount.Sample();
|
||
uint64 lcCycles = cycleCount.GetLongCycles();
|
||
|
||
if ( lcCycles >= m_lCycleLimit )
|
||
return 0;
|
||
|
||
return( ( uint64 ) ( ( m_lCycleLimit - lcCycles ) * ( uint64 ) 1000000L / g_dwClockSpeed ) );
|
||
}
|
||
|
||
|
||
#endif // FASTTIMER_H
|