r5sdk/r5dev/public/tier0/fasttimer.h
Kawe Mazidjatari 9f428f1567 Move tier0 lib headers to public
This commit does not change any logic or behavior of the code.
2023-04-06 23:50:48 +02:00

578 lines
18 KiB
C++

//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#ifndef FASTTIMER_H
#define FASTTIMER_H
#include "tier0/platform.h"
#include "tier0/cpu.h"
/******************************************************************************/
// -------------------------------------------------------------------------- //
// CClockSpeedInit
// -------------------------------------------------------------------------- //
class CClockSpeed
{
public:
CClockSpeed(void)
{
const CPUInformation& pi = GetCPUInformation();
m_nClockSpeed = pi.m_Speed;
m_dwClockSpeed = (unsigned long)m_nClockSpeed;
m_dClockSpeedMicrosecondsMultiplier = 1000000.0 / (double)m_nClockSpeed;
m_dClockSpeedMillisecondsMultiplier = 1000.0 / (double)m_nClockSpeed;
m_dClockSpeedSecondsMultiplier = 1.0f / (double)m_nClockSpeed;
}
uint64_t m_nClockSpeed;
uint32_t m_dwClockSpeed;
double m_dClockSpeedMicrosecondsMultiplier;
double m_dClockSpeedMillisecondsMultiplier;
double m_dClockSpeedSecondsMultiplier;
};
extern CClockSpeed* g_pClockSpeed;
/******************************************************************************/
// -------------------------------------------------------------------------- //
// CCycleCount
// -------------------------------------------------------------------------- //
class CCycleCount
{
friend class CFastTimer;
public:
CCycleCount(void);
CCycleCount(uint64_t cycles);
void Sample(void); // Sample the clock. This takes about 34 clocks to execute (or 26,000 calls per millisecond on a P900).
void Init(void); // Set to zero.
void Init(float initTimeMsec);
void Init(double initTimeMsec) { Init((float)initTimeMsec); }
void Init(uint64_t 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(void) const;
uint64_t GetLongCycles(void) const;
unsigned long GetMicroseconds(void) const;
uint64_t GetUlMicroseconds(void) const;
double GetMicrosecondsF(void) const;
void SetMicroseconds(unsigned long nMicroseconds);
unsigned long GetMilliseconds(void) const;
double GetMillisecondsF(void) const;
double GetSeconds(void) 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_t GetTimestamp(void);
private:
uint64_t m_Int64{};
};
/******************************************************************************/
// -------------------------------------------------------------------------- //
// CFastTimer
// These functions are fast to call and should be called from your sampling code.
// -------------------------------------------------------------------------- //
class CFastTimer
{
public:
void Start(void);
void End(void);
const CCycleCount& GetDuration(void) const; // Get the elapsed time between Start and End calls.
CCycleCount GetDurationInProgress(void) const; // Call without ending. Not that cheap.
// Return number of cycles per second on this processor.
static inline unsigned long GetClockSpeed(void);
private:
CCycleCount m_Duration;
#ifdef DEBUG_FASTTIMER
bool m_bRunning; // Are we currently running?
#endif
};
/******************************************************************************/
// -------------------------------------------------------------------------- //
// CTimeScope
// This is a helper class that times whatever block of code it's in.
// -------------------------------------------------------------------------- //
class CTimeScope
{
public:
CTimeScope(CFastTimer* pTimer);
~CTimeScope(void);
private:
CFastTimer* m_pTimer;
};
/******************************************************************************/
// -------------------------------------------------------------------------- //
// CTimeScope
// 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);
void End(void);
private:
CCycleCount* m_pTotal;
CFastTimer m_Timer;
};
/******************************************************************************/
// -------------------------------------------------------------------------- //
// 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(void) { }
CLimitTimer(uint64_t cMicroSecDuration) { SetLimit(cMicroSecDuration); }
void SetLimit(uint64_t m_cMicroSecDuration);
bool BLimitReached(void) const;
int CMicroSecOverage(void) const;
uint64_t CMicroSecLeft(void) const;
private:
uint64_t m_lCycleLimit{};
};
/******************************************************************************/
// -------------------------------------------------------------------------- //
// CAverageCycleCounter
// -------------------------------------------------------------------------- //
class CAverageCycleCounter
{
public:
CAverageCycleCounter(void);
void Init(void);
void MarkIter(const CCycleCount& duration);
unsigned GetIters(void) const;
double GetAverageMilliseconds(void) const;
double GetTotalMilliseconds(void) const;
double GetPeakMilliseconds(void) const;
private:
unsigned m_nIters {};
CCycleCount m_Total {};
CCycleCount m_Peak {};
bool m_fReport{};
const char* m_pszName{};
};
/******************************************************************************/
// -------------------------------------------------------------------------- //
// CAverageTimeMarker
// -------------------------------------------------------------------------- //
class CAverageTimeMarker
{
public:
CAverageTimeMarker(CAverageCycleCounter* pCounter);
~CAverageTimeMarker(void);
private:
CAverageCycleCounter* m_pCounter;
CFastTimer m_Timer;
};
/******************************************************************************/
// -------------------------------------------------------------------------- //
// CCycleCount inlines.
// -------------------------------------------------------------------------- //
inline CCycleCount::CCycleCount(void)
{
Init((uint64_t)0);
}
inline CCycleCount::CCycleCount(uint64_t cycles)
{
Init(cycles);
}
inline void CCycleCount::Init(void)
{
Init((uint64_t)0);
}
inline void CCycleCount::Init(float initTimeMsec)
{
if (g_pClockSpeed->m_dClockSpeedMillisecondsMultiplier > 0)
Init((uint64_t)(initTimeMsec / g_pClockSpeed->m_dClockSpeedMillisecondsMultiplier));
else
Init((uint64_t)0);
}
inline void CCycleCount::Init(uint64_t cycles)
{
m_Int64 = cycles;
}
inline void CCycleCount::Sample(void)
{
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_t CCycleCount::GetTimestamp(void)
{
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(void) const
{
return (unsigned long)m_Int64;
}
inline uint64_t CCycleCount::GetLongCycles(void) const
{
return m_Int64;
}
inline unsigned long CCycleCount::GetMicroseconds(void) const
{
return (unsigned long)((m_Int64 * 1000000) / g_pClockSpeed->m_nClockSpeed);
}
inline uint64_t CCycleCount::GetUlMicroseconds(void) const
{
return ((m_Int64 * 1000000) / g_pClockSpeed->m_nClockSpeed);
}
inline double CCycleCount::GetMicrosecondsF(void) const
{
return (double)(m_Int64 * g_pClockSpeed->m_dClockSpeedMicrosecondsMultiplier);
}
inline void CCycleCount::SetMicroseconds(unsigned long nMicroseconds)
{
m_Int64 = ((uint64_t)nMicroseconds * g_pClockSpeed->m_nClockSpeed) / 1000000;
}
inline unsigned long CCycleCount::GetMilliseconds(void) const
{
return (unsigned long)((m_Int64 * 1000) / g_pClockSpeed->m_nClockSpeed);
}
inline double CCycleCount::GetMillisecondsF(void) const
{
return (double)(m_Int64 * g_pClockSpeed->m_dClockSpeedMillisecondsMultiplier);
}
inline double CCycleCount::GetSeconds(void) const
{
return (double)(m_Int64 * g_pClockSpeed->m_dClockSpeedSecondsMultiplier);
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// CFastTimer inlines.
// -------------------------------------------------------------------------- //
inline void CFastTimer::Start(void)
{
m_Duration.Sample();
#ifdef DEBUG_FASTTIMER
m_bRunning = true;
#endif
}
inline void CFastTimer::End(void)
{
CCycleCount cnt;
cnt.Sample();
m_Duration.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64;
#ifdef DEBUG_FASTTIMER
m_bRunning = false;
#endif
}
inline CCycleCount CFastTimer::GetDurationInProgress(void) const
{
CCycleCount cnt;
cnt.Sample();
CCycleCount result;
result.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64;
return result;
}
inline unsigned long CFastTimer::GetClockSpeed(void)
{
return g_pClockSpeed->m_dwClockSpeed;
}
inline CCycleCount const& CFastTimer::GetDuration(void) const
{
#ifdef DEBUG_FASTTIMER
assert(!m_bRunning);
#endif
return m_Duration;
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// CTimeScope inlines.
// -------------------------------------------------------------------------- //
inline CTimeScope::CTimeScope(CFastTimer* pTotal)
{
m_pTimer = pTotal;
m_pTimer->Start();
}
inline CTimeScope::~CTimeScope(void)
{
m_pTimer->End();
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// CTimeAdder inlines.
// -------------------------------------------------------------------------- //
inline CTimeAdder::CTimeAdder(CCycleCount* pTotal)
{
m_pTotal = pTotal;
m_Timer.Start();
}
inline CTimeAdder::~CTimeAdder(void)
{
End();
}
inline void CTimeAdder::End(void)
{
if (m_pTotal)
{
m_Timer.End();
*m_pTotal += m_Timer.GetDuration();
m_pTotal = 0;
}
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// CAverageCycleCounter inlines
// -------------------------------------------------------------------------- //
inline CAverageCycleCounter::CAverageCycleCounter(void)
: m_nIters(0)
{
}
inline void CAverageCycleCounter::Init(void)
{
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(void) const
{
return m_nIters;
}
inline double CAverageCycleCounter::GetAverageMilliseconds(void) const
{
if (m_nIters)
return (m_Total.GetMillisecondsF() / (double)m_nIters);
else
return 0;
}
inline double CAverageCycleCounter::GetTotalMilliseconds(void) const
{
return m_Total.GetMillisecondsF();
}
inline double CAverageCycleCounter::GetPeakMilliseconds(void) const
{
return m_Peak.GetMillisecondsF();
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// CAverageTimeMarker inlines
// -------------------------------------------------------------------------- //
inline CAverageTimeMarker::CAverageTimeMarker(CAverageCycleCounter* pCounter)
{
m_pCounter = pCounter;
m_Timer.Start();
}
inline CAverageTimeMarker::~CAverageTimeMarker(void)
{
m_Timer.End();
m_pCounter->MarkIter(m_Timer.GetDuration());
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// CLimitTimer inlines
// -------------------------------------------------------------------------- //
// 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_t cMicroSecDuration)
{
uint64_t dlCycles = ((uint64_t)cMicroSecDuration * (uint64_t)g_pClockSpeed->m_dwClockSpeed) / (uint64_t)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(void) 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(void) const
{
CCycleCount cycleCount;
cycleCount.Sample();
uint64_t lcCycles = cycleCount.GetLongCycles();
if (lcCycles < m_lCycleLimit)
return 0;
return((int)((lcCycles - m_lCycleLimit) * (uint64_t)1000000L / g_pClockSpeed->m_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_t CLimitTimer::CMicroSecLeft(void) const
{
CCycleCount cycleCount;
cycleCount.Sample();
uint64_t lcCycles = cycleCount.GetLongCycles();
if (lcCycles >= m_lCycleLimit)
return 0;
return((uint64_t)((m_lCycleLimit - lcCycles) * (uint64_t)1000000L / g_pClockSpeed->m_dwClockSpeed));
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
// 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;
// -------------------------------------------------------------------------- //
#endif // FASTTIMER_H