Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

569 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #ifndef FASTTIMER_H
  8. #define FASTTIMER_H
  9. #ifdef _WIN32
  10. #pragma once
  11. #endif
  12. #ifdef _WIN32
  13. #include <intrin.h>
  14. #endif
  15. #include <assert.h>
  16. #include "tier0/platform.h"
  17. PLATFORM_INTERFACE uint64 g_ClockSpeed;
  18. #if defined( _X360 ) && defined( _CERT )
  19. PLATFORM_INTERFACE unsigned long g_dwFakeFastCounter;
  20. #endif
  21. PLATFORM_INTERFACE double g_ClockSpeedMicrosecondsMultiplier;
  22. PLATFORM_INTERFACE double g_ClockSpeedMillisecondsMultiplier;
  23. PLATFORM_INTERFACE double g_ClockSpeedSecondsMultiplier;
  24. class CCycleCount
  25. {
  26. friend class CFastTimer;
  27. public:
  28. CCycleCount();
  29. CCycleCount( uint64 cycles );
  30. void Sample(); // Sample the clock. This takes about 34 clocks to execute (or 26,000 calls per millisecond on a P900).
  31. void Init(); // Set to zero.
  32. void Init( float initTimeMsec );
  33. void Init( double initTimeMsec ) { Init( (float)initTimeMsec ); }
  34. void Init( uint64 cycles );
  35. bool IsLessThan( CCycleCount const &other ) const; // Compare two counts.
  36. // Convert to other time representations. These functions are slow, so it's preferable to call them
  37. // during display rather than inside a timing block.
  38. unsigned long GetCycles() const;
  39. uint64 GetLongCycles() const;
  40. unsigned long GetMicroseconds() const;
  41. uint64 GetUlMicroseconds() const;
  42. double GetMicrosecondsF() const;
  43. void SetMicroseconds( unsigned long nMicroseconds );
  44. unsigned long GetMilliseconds() const;
  45. double GetMillisecondsF() const;
  46. double GetSeconds() const;
  47. CCycleCount& operator+=( CCycleCount const &other );
  48. // dest = rSrc1 + rSrc2
  49. static void Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together.
  50. // dest = rSrc1 - rSrc2
  51. static void Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together.
  52. static uint64 GetTimestamp();
  53. uint64 m_Int64;
  54. };
  55. class PLATFORM_CLASS CClockSpeedInit
  56. {
  57. public:
  58. CClockSpeedInit()
  59. {
  60. Init();
  61. }
  62. static void Init();
  63. };
  64. class CFastTimer
  65. {
  66. public:
  67. // These functions are fast to call and should be called from your sampling code.
  68. void Start();
  69. void End();
  70. const CCycleCount & GetDuration() const; // Get the elapsed time between Start and End calls.
  71. CCycleCount GetDurationInProgress() const; // Call without ending. Not that cheap.
  72. // Return number of cycles per second on this processor.
  73. static inline int64 GetClockSpeed();
  74. private:
  75. CCycleCount m_Duration;
  76. #ifdef DEBUG_FASTTIMER
  77. bool m_bRunning; // Are we currently running?
  78. #endif
  79. };
  80. // This is a helper class that times whatever block of code it's in
  81. class CTimeScope
  82. {
  83. public:
  84. CTimeScope( CFastTimer *pTimer );
  85. ~CTimeScope();
  86. private:
  87. CFastTimer *m_pTimer;
  88. };
  89. inline CTimeScope::CTimeScope( CFastTimer *pTotal )
  90. {
  91. m_pTimer = pTotal;
  92. m_pTimer->Start();
  93. }
  94. inline CTimeScope::~CTimeScope()
  95. {
  96. m_pTimer->End();
  97. }
  98. // This is a helper class that times whatever block of code it's in and
  99. // adds the total (int microseconds) to a global counter.
  100. class CTimeAdder
  101. {
  102. public:
  103. CTimeAdder( CCycleCount *pTotal );
  104. ~CTimeAdder();
  105. void End();
  106. private:
  107. CCycleCount *m_pTotal;
  108. CFastTimer m_Timer;
  109. };
  110. inline CTimeAdder::CTimeAdder( CCycleCount *pTotal )
  111. {
  112. m_pTotal = pTotal;
  113. m_Timer.Start();
  114. }
  115. inline CTimeAdder::~CTimeAdder()
  116. {
  117. End();
  118. }
  119. inline void CTimeAdder::End()
  120. {
  121. if( m_pTotal )
  122. {
  123. m_Timer.End();
  124. *m_pTotal += m_Timer.GetDuration();
  125. m_pTotal = 0;
  126. }
  127. }
  128. // -------------------------------------------------------------------------- //
  129. // Simple tool to support timing a block of code, and reporting the results on
  130. // program exit or at each iteration
  131. //
  132. // Macros used because dbg.h uses this header, thus Msg() is unavailable
  133. // -------------------------------------------------------------------------- //
  134. #define PROFILE_SCOPE(name) \
  135. class C##name##ACC : public CAverageCycleCounter \
  136. { \
  137. public: \
  138. ~C##name##ACC() \
  139. { \
  140. Msg("%-48s: %6.3f avg (%8.1f total, %7.3f peak, %5d iters)\n", \
  141. #name, \
  142. GetAverageMilliseconds(), \
  143. GetTotalMilliseconds(), \
  144. GetPeakMilliseconds(), \
  145. GetIters() ); \
  146. } \
  147. }; \
  148. static C##name##ACC name##_ACC; \
  149. CAverageTimeMarker name##_ATM( &name##_ACC )
  150. #define TIME_SCOPE(name) \
  151. class CTimeScopeMsg_##name \
  152. { \
  153. public: \
  154. CTimeScopeMsg_##name() { m_Timer.Start(); } \
  155. ~CTimeScopeMsg_##name() \
  156. { \
  157. m_Timer.End(); \
  158. Msg( #name "time: %.4fms\n", m_Timer.GetDuration().GetMillisecondsF() ); \
  159. } \
  160. private: \
  161. CFastTimer m_Timer; \
  162. } name##_TSM;
  163. // -------------------------------------------------------------------------- //
  164. class CAverageCycleCounter
  165. {
  166. public:
  167. CAverageCycleCounter();
  168. void Init();
  169. void MarkIter( const CCycleCount &duration );
  170. unsigned GetIters() const;
  171. double GetAverageMilliseconds() const;
  172. double GetTotalMilliseconds() const;
  173. double GetPeakMilliseconds() const;
  174. private:
  175. unsigned m_nIters;
  176. CCycleCount m_Total;
  177. CCycleCount m_Peak;
  178. };
  179. // -------------------------------------------------------------------------- //
  180. class CAverageTimeMarker
  181. {
  182. public:
  183. CAverageTimeMarker( CAverageCycleCounter *pCounter );
  184. ~CAverageTimeMarker();
  185. private:
  186. CAverageCycleCounter *m_pCounter;
  187. CFastTimer m_Timer;
  188. };
  189. // -------------------------------------------------------------------------- //
  190. // CCycleCount inlines.
  191. // -------------------------------------------------------------------------- //
  192. inline CCycleCount::CCycleCount()
  193. {
  194. Init( (uint64)0 );
  195. }
  196. inline CCycleCount::CCycleCount( uint64 cycles )
  197. {
  198. Init( cycles );
  199. }
  200. inline void CCycleCount::Init()
  201. {
  202. Init( (uint64)0 );
  203. }
  204. inline void CCycleCount::Init( float initTimeMsec )
  205. {
  206. if ( g_ClockSpeedMillisecondsMultiplier > 0 )
  207. Init( (uint64)(initTimeMsec / g_ClockSpeedMillisecondsMultiplier) );
  208. else
  209. Init( (uint64)0 );
  210. }
  211. inline void CCycleCount::Init( uint64 cycles )
  212. {
  213. m_Int64 = cycles;
  214. }
  215. inline void CCycleCount::Sample()
  216. {
  217. m_Int64 = Plat_Rdtsc();
  218. }
  219. inline CCycleCount& CCycleCount::operator+=( CCycleCount const &other )
  220. {
  221. m_Int64 += other.m_Int64;
  222. return *this;
  223. }
  224. inline void CCycleCount::Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest )
  225. {
  226. dest.m_Int64 = rSrc1.m_Int64 + rSrc2.m_Int64;
  227. }
  228. inline void CCycleCount::Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest )
  229. {
  230. dest.m_Int64 = rSrc1.m_Int64 - rSrc2.m_Int64;
  231. }
  232. inline uint64 CCycleCount::GetTimestamp()
  233. {
  234. CCycleCount c;
  235. c.Sample();
  236. return c.GetLongCycles();
  237. }
  238. inline bool CCycleCount::IsLessThan(CCycleCount const &other) const
  239. {
  240. return m_Int64 < other.m_Int64;
  241. }
  242. inline unsigned long CCycleCount::GetCycles() const
  243. {
  244. return (unsigned long)m_Int64;
  245. }
  246. inline uint64 CCycleCount::GetLongCycles() const
  247. {
  248. return m_Int64;
  249. }
  250. inline unsigned long CCycleCount::GetMicroseconds() const
  251. {
  252. return (unsigned long)((m_Int64 * 1000000) / g_ClockSpeed);
  253. }
  254. inline uint64 CCycleCount::GetUlMicroseconds() const
  255. {
  256. return ((m_Int64 * 1000000) / g_ClockSpeed);
  257. }
  258. inline double CCycleCount::GetMicrosecondsF() const
  259. {
  260. return (double)( m_Int64 * g_ClockSpeedMicrosecondsMultiplier );
  261. }
  262. inline void CCycleCount::SetMicroseconds( unsigned long nMicroseconds )
  263. {
  264. m_Int64 = ((uint64)nMicroseconds * g_ClockSpeed) / 1000000;
  265. }
  266. inline unsigned long CCycleCount::GetMilliseconds() const
  267. {
  268. return (unsigned long)((m_Int64 * 1000) / g_ClockSpeed);
  269. }
  270. inline double CCycleCount::GetMillisecondsF() const
  271. {
  272. return (double)( m_Int64 * g_ClockSpeedMillisecondsMultiplier );
  273. }
  274. inline double CCycleCount::GetSeconds() const
  275. {
  276. return (double)( m_Int64 * g_ClockSpeedSecondsMultiplier );
  277. }
  278. // -------------------------------------------------------------------------- //
  279. // CFastTimer inlines.
  280. // -------------------------------------------------------------------------- //
  281. inline void CFastTimer::Start()
  282. {
  283. m_Duration.Sample();
  284. #ifdef DEBUG_FASTTIMER
  285. m_bRunning = true;
  286. #endif
  287. }
  288. inline void CFastTimer::End()
  289. {
  290. CCycleCount cnt;
  291. cnt.Sample();
  292. if ( IsX360() )
  293. {
  294. // have to handle rollover, hires timer is only accurate to 32 bits
  295. // more than one overflow should not have occurred, otherwise caller should use a slower timer
  296. if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 )
  297. {
  298. // rollover occurred
  299. cnt.m_Int64 += 0x100000000LL;
  300. }
  301. }
  302. m_Duration.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64;
  303. #ifdef DEBUG_FASTTIMER
  304. m_bRunning = false;
  305. #endif
  306. }
  307. inline CCycleCount CFastTimer::GetDurationInProgress() const
  308. {
  309. CCycleCount cnt;
  310. cnt.Sample();
  311. if ( IsX360() )
  312. {
  313. // have to handle rollover, hires timer is only accurate to 32 bits
  314. // more than one overflow should not have occurred, otherwise caller should use a slower timer
  315. if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 )
  316. {
  317. // rollover occurred
  318. cnt.m_Int64 += 0x100000000LL;
  319. }
  320. }
  321. CCycleCount result;
  322. result.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64;
  323. return result;
  324. }
  325. inline int64 CFastTimer::GetClockSpeed()
  326. {
  327. return g_ClockSpeed;
  328. }
  329. inline CCycleCount const& CFastTimer::GetDuration() const
  330. {
  331. #ifdef DEBUG_FASTTIMER
  332. assert( !m_bRunning );
  333. #endif
  334. return m_Duration;
  335. }
  336. // -------------------------------------------------------------------------- //
  337. // CAverageCycleCounter inlines
  338. inline CAverageCycleCounter::CAverageCycleCounter()
  339. : m_nIters( 0 )
  340. {
  341. }
  342. inline void CAverageCycleCounter::Init()
  343. {
  344. m_Total.Init();
  345. m_Peak.Init();
  346. m_nIters = 0;
  347. }
  348. inline void CAverageCycleCounter::MarkIter( const CCycleCount &duration )
  349. {
  350. ++m_nIters;
  351. m_Total += duration;
  352. if ( m_Peak.IsLessThan( duration ) )
  353. m_Peak = duration;
  354. }
  355. inline unsigned CAverageCycleCounter::GetIters() const
  356. {
  357. return m_nIters;
  358. }
  359. inline double CAverageCycleCounter::GetAverageMilliseconds() const
  360. {
  361. if ( m_nIters )
  362. return (m_Total.GetMillisecondsF() / (double)m_nIters);
  363. else
  364. return 0;
  365. }
  366. inline double CAverageCycleCounter::GetTotalMilliseconds() const
  367. {
  368. return m_Total.GetMillisecondsF();
  369. }
  370. inline double CAverageCycleCounter::GetPeakMilliseconds() const
  371. {
  372. return m_Peak.GetMillisecondsF();
  373. }
  374. // -------------------------------------------------------------------------- //
  375. inline CAverageTimeMarker::CAverageTimeMarker( CAverageCycleCounter *pCounter )
  376. {
  377. m_pCounter = pCounter;
  378. m_Timer.Start();
  379. }
  380. inline CAverageTimeMarker::~CAverageTimeMarker()
  381. {
  382. m_Timer.End();
  383. m_pCounter->MarkIter( m_Timer.GetDuration() );
  384. }
  385. // CLimitTimer
  386. // Use this to time whether a desired interval of time has passed. It's extremely fast
  387. // to check while running. NOTE: CMicroSecOverage() and CMicroSecLeft() are not as fast to check.
  388. class CLimitTimer
  389. {
  390. public:
  391. CLimitTimer() {}
  392. CLimitTimer( uint64 cMicroSecDuration ) { SetLimit( cMicroSecDuration ); }
  393. void SetLimit( uint64 m_cMicroSecDuration );
  394. bool BLimitReached() const;
  395. int CMicroSecOverage() const;
  396. uint64 CMicroSecLeft() const;
  397. private:
  398. uint64 m_lCycleLimit;
  399. };
  400. //-----------------------------------------------------------------------------
  401. // Purpose: Initializes the limit timer with a period of time to measure.
  402. // Input : cMicroSecDuration - How long a time period to measure
  403. //-----------------------------------------------------------------------------
  404. inline void CLimitTimer::SetLimit( uint64 cMicroSecDuration )
  405. {
  406. uint64 dlCycles = ( ( uint64 ) cMicroSecDuration * g_ClockSpeed ) / ( uint64 ) 1000000L;
  407. CCycleCount cycleCount;
  408. cycleCount.Sample( );
  409. m_lCycleLimit = cycleCount.GetLongCycles( ) + dlCycles;
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose: Determines whether our specified time period has passed
  413. // Output: true if at least the specified time period has passed
  414. //-----------------------------------------------------------------------------
  415. inline bool CLimitTimer::BLimitReached() const
  416. {
  417. CCycleCount cycleCount;
  418. cycleCount.Sample( );
  419. return ( cycleCount.GetLongCycles( ) >= m_lCycleLimit );
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose: If we're over our specified time period, return the amount of the overage.
  423. // Output: # of microseconds since we reached our specified time period.
  424. //-----------------------------------------------------------------------------
  425. inline int CLimitTimer::CMicroSecOverage() const
  426. {
  427. CCycleCount cycleCount;
  428. cycleCount.Sample();
  429. uint64 lcCycles = cycleCount.GetLongCycles();
  430. if ( lcCycles < m_lCycleLimit )
  431. return 0;
  432. return( ( int ) ( ( lcCycles - m_lCycleLimit ) * ( uint64 ) 1000000L / g_ClockSpeed ) );
  433. }
  434. //-----------------------------------------------------------------------------
  435. // Purpose: If we're under our specified time period, return the amount under.
  436. // Output: # of microseconds until we reached our specified time period, 0 if we've passed it
  437. //-----------------------------------------------------------------------------
  438. inline uint64 CLimitTimer::CMicroSecLeft() const
  439. {
  440. CCycleCount cycleCount;
  441. cycleCount.Sample();
  442. uint64 lcCycles = cycleCount.GetLongCycles();
  443. if ( lcCycles >= m_lCycleLimit )
  444. return 0;
  445. return( ( uint64 ) ( ( m_lCycleLimit - lcCycles ) * ( uint64 ) 1000000L / g_ClockSpeed ) );
  446. }
  447. #endif // FASTTIMER_H