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.

625 lines
14 KiB

  1. //========= Copyright � 1996-2005, 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. #include <assert.h>
  13. #include "tier0/platform.h"
  14. #ifdef _PS3
  15. #include "sys/sys_time.h"
  16. #else
  17. inline uint64 sys_time_get_timebase_frequency()
  18. {
  19. DebuggerBreak(); // Error("sys_time_get_timebase_frequency called on non-PS3 platform.");
  20. return 1; // this function should never ever be called.
  21. }
  22. #endif
  23. PLATFORM_INTERFACE uint64 g_ClockSpeed;
  24. PLATFORM_INTERFACE unsigned long g_dwClockSpeed;
  25. PLATFORM_INTERFACE double g_ClockSpeedMicrosecondsMultiplier;
  26. PLATFORM_INTERFACE double g_ClockSpeedMillisecondsMultiplier;
  27. PLATFORM_INTERFACE double g_ClockSpeedSecondsMultiplier;
  28. #ifdef COMPILER_MSVC64
  29. extern "C"
  30. {
  31. unsigned __int64 __rdtsc();
  32. }
  33. #pragma intrinsic(__rdtsc)
  34. #endif
  35. class CCycleCount
  36. {
  37. friend class CFastTimer;
  38. public:
  39. CCycleCount();
  40. CCycleCount( uint64 cycles );
  41. void Sample(); // Sample the clock. This takes about 34 clocks to execute (or 26,000 calls per millisecond on a P900).
  42. void Init(); // Set to zero.
  43. void Init( float initTimeMsec );
  44. void Init( double initTimeMsec ) { Init( (float)initTimeMsec ); }
  45. void Init( uint64 cycles );
  46. bool IsLessThan( CCycleCount const &other ) const; // Compare two counts.
  47. // Convert to other time representations. These functions are slow, so it's preferable to call them
  48. // during display rather than inside a timing block.
  49. unsigned long GetCycles() const;
  50. uint64 GetLongCycles() const;
  51. unsigned long GetMicroseconds() const;
  52. uint64 GetUlMicroseconds() const;
  53. double GetMicrosecondsF() const;
  54. void SetMicroseconds( unsigned long nMicroseconds );
  55. unsigned long GetMilliseconds() const;
  56. double GetMillisecondsF() const;
  57. double GetSeconds() const;
  58. CCycleCount& operator+=( CCycleCount const &other );
  59. // dest = rSrc1 + rSrc2
  60. static void Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together.
  61. // dest = rSrc1 - rSrc2
  62. static void Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest ); // Add two samples together.
  63. static uint64 GetTimestamp();
  64. uint64 m_Int64;
  65. };
  66. class CClockSpeedInit
  67. {
  68. public:
  69. CClockSpeedInit()
  70. {
  71. Init();
  72. }
  73. static void Init()
  74. {
  75. const CPUInformation& pi = GetCPUInformation();
  76. if ( IsX360() )
  77. {
  78. // cycle counter runs as doc'd at 1/64 Xbox 3.2GHz clock speed, thus 50 Mhz
  79. g_ClockSpeed = pi.m_Speed / 64L;
  80. }
  81. else if ( IsPS3() )
  82. {
  83. g_ClockSpeed = sys_time_get_timebase_frequency(); // CPU clock rate is totally unrelated to time base register frequency on PS3
  84. }
  85. else
  86. {
  87. g_ClockSpeed = pi.m_Speed;
  88. }
  89. g_dwClockSpeed = (unsigned long)g_ClockSpeed;
  90. g_ClockSpeedMicrosecondsMultiplier = 1000000.0 / (double)g_ClockSpeed;
  91. g_ClockSpeedMillisecondsMultiplier = 1000.0 / (double)g_ClockSpeed;
  92. g_ClockSpeedSecondsMultiplier = 1.0f / (double)g_ClockSpeed;
  93. }
  94. };
  95. class CFastTimer
  96. {
  97. public:
  98. // These functions are fast to call and should be called from your sampling code.
  99. void Start();
  100. void End();
  101. const CCycleCount & GetDuration() const; // Get the elapsed time between Start and End calls.
  102. CCycleCount GetDurationInProgress() const; // Call without ending. Not that cheap.
  103. // Return number of cycles per second on this processor.
  104. static inline unsigned long GetClockSpeed();
  105. private:
  106. CCycleCount m_Duration;
  107. #ifdef DEBUG_FASTTIMER
  108. bool m_bRunning; // Are we currently running?
  109. #endif
  110. };
  111. // This is a helper class that times whatever block of code it's in
  112. class CTimeScope
  113. {
  114. public:
  115. CTimeScope( CFastTimer *pTimer );
  116. ~CTimeScope();
  117. private:
  118. CFastTimer *m_pTimer;
  119. };
  120. inline CTimeScope::CTimeScope( CFastTimer *pTotal )
  121. {
  122. m_pTimer = pTotal;
  123. m_pTimer->Start();
  124. }
  125. inline CTimeScope::~CTimeScope()
  126. {
  127. m_pTimer->End();
  128. }
  129. // This is a helper class that times whatever block of code it's in and
  130. // adds the total (int microseconds) to a global counter.
  131. class CTimeAdder
  132. {
  133. public:
  134. CTimeAdder( CCycleCount *pTotal );
  135. ~CTimeAdder();
  136. void End();
  137. private:
  138. CCycleCount *m_pTotal;
  139. CFastTimer m_Timer;
  140. };
  141. inline CTimeAdder::CTimeAdder( CCycleCount *pTotal )
  142. {
  143. m_pTotal = pTotal;
  144. m_Timer.Start();
  145. }
  146. inline CTimeAdder::~CTimeAdder()
  147. {
  148. End();
  149. }
  150. inline void CTimeAdder::End()
  151. {
  152. if( m_pTotal )
  153. {
  154. m_Timer.End();
  155. *m_pTotal += m_Timer.GetDuration();
  156. m_pTotal = 0;
  157. }
  158. }
  159. // -------------------------------------------------------------------------- //
  160. // Simple tool to support timing a block of code, and reporting the results on
  161. // program exit or at each iteration
  162. //
  163. // Macros used because dbg.h uses this header, thus Msg() is unavailable
  164. // -------------------------------------------------------------------------- //
  165. #define PROFILE_SCOPE(name) \
  166. class C##name##ACC : public CAverageCycleCounter \
  167. { \
  168. public: \
  169. ~C##name##ACC() \
  170. { \
  171. Msg("%-48s: %6.3f avg (%8.1f total, %7.3f peak, %5d iters)\n", \
  172. #name, \
  173. GetAverageMilliseconds(), \
  174. GetTotalMilliseconds(), \
  175. GetPeakMilliseconds(), \
  176. GetIters() ); \
  177. } \
  178. }; \
  179. static C##name##ACC name##_ACC; \
  180. CAverageTimeMarker name##_ATM( &name##_ACC )
  181. #define TIME_SCOPE(name) \
  182. class CTimeScopeMsg_##name \
  183. { \
  184. public: \
  185. CTimeScopeMsg_##name() { m_Timer.Start(); } \
  186. ~CTimeScopeMsg_##name() \
  187. { \
  188. m_Timer.End(); \
  189. Msg( #name "time: %.4fms\n", m_Timer.GetDuration().GetMillisecondsF() ); \
  190. } \
  191. private: \
  192. CFastTimer m_Timer; \
  193. } name##_TSM;
  194. // -------------------------------------------------------------------------- //
  195. class CAverageCycleCounter
  196. {
  197. public:
  198. CAverageCycleCounter();
  199. void Init();
  200. void MarkIter( const CCycleCount &duration );
  201. unsigned GetIters() const;
  202. double GetAverageMilliseconds() const;
  203. double GetTotalMilliseconds() const;
  204. double GetPeakMilliseconds() const;
  205. private:
  206. unsigned m_nIters;
  207. CCycleCount m_Total;
  208. CCycleCount m_Peak;
  209. bool m_fReport;
  210. const tchar *m_pszName;
  211. };
  212. // -------------------------------------------------------------------------- //
  213. class CAverageTimeMarker
  214. {
  215. public:
  216. CAverageTimeMarker( CAverageCycleCounter *pCounter );
  217. ~CAverageTimeMarker();
  218. private:
  219. CAverageCycleCounter *m_pCounter;
  220. CFastTimer m_Timer;
  221. };
  222. // -------------------------------------------------------------------------- //
  223. // CCycleCount inlines.
  224. // -------------------------------------------------------------------------- //
  225. inline CCycleCount::CCycleCount()
  226. {
  227. Init( (uint64)0 );
  228. }
  229. inline CCycleCount::CCycleCount( uint64 cycles )
  230. {
  231. Init( cycles );
  232. }
  233. inline void CCycleCount::Init()
  234. {
  235. Init( (uint64)0 );
  236. }
  237. inline void CCycleCount::Init( float initTimeMsec )
  238. {
  239. if ( g_ClockSpeedMillisecondsMultiplier > 0 )
  240. Init( (uint64)(initTimeMsec / g_ClockSpeedMillisecondsMultiplier) );
  241. else
  242. Init( (uint64)0 );
  243. }
  244. inline void CCycleCount::Init( uint64 cycles )
  245. {
  246. m_Int64 = cycles;
  247. }
  248. #if !COMPILER_GCC
  249. #pragma warning(push)
  250. #pragma warning(disable : 4189) // warning C4189: local variable is initialized but not referenced
  251. #endif
  252. inline void CCycleCount::Sample()
  253. {
  254. #ifdef COMPILER_MSVC64
  255. unsigned __int64* pSample = (unsigned __int64*)&m_Int64;
  256. *pSample = __rdtsc();
  257. // Msg( "Sample = %I64x", pSample );
  258. #elif defined( _X360 )
  259. // only need lower 32 bits, avoids doc'd read bug and 32 bit rollover is in 85 seconds
  260. m_Int64 = (uint64)__mftb32();
  261. // scale back up, needs to be viewed as 1 cycle/clock
  262. #elif defined( _PS3 )
  263. // only need lower 32 bits, avoids doc'd read bug and 32 bit rollover is in 85 seconds
  264. m_Int64 = (uint64)__mftb();
  265. // scale back up, needs to be viewed as 1 cycle/clock
  266. #elif defined( __GNUC__ )
  267. union
  268. {
  269. unsigned long* pSample;
  270. uint64 * pInt64;
  271. } tmp;
  272. tmp.pInt64 = &m_Int64;
  273. __asm__ __volatile__ (
  274. "rdtsc\n\t"
  275. "movl %%eax, (%0)\n\t"
  276. "movl %%edx, 4(%0)\n\t"
  277. : /* no output regs */
  278. : "D" (tmp.pSample)
  279. : "%eax", "%edx" );
  280. #elif defined( _WIN32 )
  281. unsigned long* pSample = (unsigned long *)&m_Int64;
  282. __asm
  283. {
  284. // force the cpu to synchronize the instruction queue
  285. // NJS: CPUID can really impact performance in tight loops.
  286. //cpuid
  287. //cpuid
  288. //cpuid
  289. mov ecx, pSample
  290. rdtsc
  291. mov [ecx], eax
  292. mov [ecx+4], edx
  293. }
  294. #elif defined( POSIX )
  295. unsigned long* pSample = (unsigned long *)&m_Int64;
  296. __asm__ __volatile__ (
  297. "rdtsc\n\t"
  298. "movl %%eax, (%0)\n\t"
  299. "movl %%edx, 4(%0)\n\t"
  300. : /* no output regs */
  301. : "D" (pSample)
  302. : "%eax", "%edx" );
  303. #endif
  304. }
  305. #if !COMPILER_GCC
  306. #pragma warning(pop)
  307. #endif
  308. inline CCycleCount& CCycleCount::operator+=( CCycleCount const &other )
  309. {
  310. m_Int64 += other.m_Int64;
  311. return *this;
  312. }
  313. inline void CCycleCount::Add( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest )
  314. {
  315. dest.m_Int64 = rSrc1.m_Int64 + rSrc2.m_Int64;
  316. }
  317. inline void CCycleCount::Sub( CCycleCount const &rSrc1, CCycleCount const &rSrc2, CCycleCount &dest )
  318. {
  319. dest.m_Int64 = rSrc1.m_Int64 - rSrc2.m_Int64;
  320. }
  321. inline uint64 CCycleCount::GetTimestamp()
  322. {
  323. CCycleCount c;
  324. c.Sample();
  325. return c.GetLongCycles();
  326. }
  327. inline bool CCycleCount::IsLessThan(CCycleCount const &other) const
  328. {
  329. return m_Int64 < other.m_Int64;
  330. }
  331. inline unsigned long CCycleCount::GetCycles() const
  332. {
  333. return (unsigned long)m_Int64;
  334. }
  335. inline uint64 CCycleCount::GetLongCycles() const
  336. {
  337. return m_Int64;
  338. }
  339. inline unsigned long CCycleCount::GetMicroseconds() const
  340. {
  341. return (unsigned long)((m_Int64 * 1000000) / g_ClockSpeed);
  342. }
  343. inline uint64 CCycleCount::GetUlMicroseconds() const
  344. {
  345. return ((m_Int64 * 1000000) / g_ClockSpeed);
  346. }
  347. inline double CCycleCount::GetMicrosecondsF() const
  348. {
  349. return (double)( m_Int64 * g_ClockSpeedMicrosecondsMultiplier );
  350. }
  351. inline void CCycleCount::SetMicroseconds( unsigned long nMicroseconds )
  352. {
  353. m_Int64 = ((uint64)nMicroseconds * g_ClockSpeed) / 1000000;
  354. }
  355. inline unsigned long CCycleCount::GetMilliseconds() const
  356. {
  357. return (unsigned long)((m_Int64 * 1000) / g_ClockSpeed);
  358. }
  359. inline double CCycleCount::GetMillisecondsF() const
  360. {
  361. return (double)( m_Int64 * g_ClockSpeedMillisecondsMultiplier );
  362. }
  363. inline double CCycleCount::GetSeconds() const
  364. {
  365. return (double)( m_Int64 * g_ClockSpeedSecondsMultiplier );
  366. }
  367. // -------------------------------------------------------------------------- //
  368. // CFastTimer inlines.
  369. // -------------------------------------------------------------------------- //
  370. inline void CFastTimer::Start()
  371. {
  372. m_Duration.Sample();
  373. #ifdef DEBUG_FASTTIMER
  374. m_bRunning = true;
  375. #endif
  376. }
  377. inline void CFastTimer::End()
  378. {
  379. CCycleCount cnt;
  380. cnt.Sample();
  381. if ( IsX360() )
  382. {
  383. // have to handle rollover, hires timer is only accurate to 32 bits
  384. // more than one overflow should not have occurred, otherwise caller should use a slower timer
  385. if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 )
  386. {
  387. // rollover occurred
  388. cnt.m_Int64 += 0x100000000LL;
  389. }
  390. }
  391. m_Duration.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64;
  392. #ifdef DEBUG_FASTTIMER
  393. m_bRunning = false;
  394. #endif
  395. }
  396. inline CCycleCount CFastTimer::GetDurationInProgress() const
  397. {
  398. CCycleCount cnt;
  399. cnt.Sample();
  400. if ( IsX360() )
  401. {
  402. // have to handle rollover, hires timer is only accurate to 32 bits
  403. // more than one overflow should not have occurred, otherwise caller should use a slower timer
  404. if ( (uint64)cnt.m_Int64 <= (uint64)m_Duration.m_Int64 )
  405. {
  406. // rollover occurred
  407. cnt.m_Int64 += 0x100000000LL;
  408. }
  409. }
  410. CCycleCount result;
  411. result.m_Int64 = cnt.m_Int64 - m_Duration.m_Int64;
  412. return result;
  413. }
  414. inline unsigned long CFastTimer::GetClockSpeed()
  415. {
  416. return g_dwClockSpeed;
  417. }
  418. inline CCycleCount const& CFastTimer::GetDuration() const
  419. {
  420. #ifdef DEBUG_FASTTIMER
  421. assert( !m_bRunning );
  422. #endif
  423. return m_Duration;
  424. }
  425. // -------------------------------------------------------------------------- //
  426. // CAverageCycleCounter inlines
  427. inline CAverageCycleCounter::CAverageCycleCounter()
  428. : m_nIters( 0 )
  429. {
  430. }
  431. inline void CAverageCycleCounter::Init()
  432. {
  433. m_Total.Init();
  434. m_Peak.Init();
  435. m_nIters = 0;
  436. }
  437. inline void CAverageCycleCounter::MarkIter( const CCycleCount &duration )
  438. {
  439. ++m_nIters;
  440. m_Total += duration;
  441. if ( m_Peak.IsLessThan( duration ) )
  442. m_Peak = duration;
  443. }
  444. inline unsigned CAverageCycleCounter::GetIters() const
  445. {
  446. return m_nIters;
  447. }
  448. inline double CAverageCycleCounter::GetAverageMilliseconds() const
  449. {
  450. if ( m_nIters )
  451. return (m_Total.GetMillisecondsF() / (double)m_nIters);
  452. else
  453. return 0;
  454. }
  455. inline double CAverageCycleCounter::GetTotalMilliseconds() const
  456. {
  457. return m_Total.GetMillisecondsF();
  458. }
  459. inline double CAverageCycleCounter::GetPeakMilliseconds() const
  460. {
  461. return m_Peak.GetMillisecondsF();
  462. }
  463. // -------------------------------------------------------------------------- //
  464. inline CAverageTimeMarker::CAverageTimeMarker( CAverageCycleCounter *pCounter )
  465. {
  466. m_pCounter = pCounter;
  467. m_Timer.Start();
  468. }
  469. inline CAverageTimeMarker::~CAverageTimeMarker()
  470. {
  471. m_Timer.End();
  472. m_pCounter->MarkIter( m_Timer.GetDuration() );
  473. }
  474. // CLimitTimer
  475. // Use this to time whether a desired interval of time has passed. It's extremely fast
  476. // to check while running.
  477. class CLimitTimer
  478. {
  479. public:
  480. void SetLimit( uint64 m_cMicroSecDuration );
  481. bool BLimitReached( void );
  482. private:
  483. uint64 m_lCycleLimit;
  484. };
  485. //-----------------------------------------------------------------------------
  486. // Purpose: Initializes the limit timer with a period of time to measure.
  487. // Input : cMicroSecDuration - How long a time period to measure
  488. //-----------------------------------------------------------------------------
  489. inline void CLimitTimer::SetLimit( uint64 m_cMicroSecDuration )
  490. {
  491. uint64 dlCycles = ( ( uint64 ) m_cMicroSecDuration * ( uint64 ) g_dwClockSpeed ) / ( uint64 ) 1000000L;
  492. CCycleCount cycleCount;
  493. cycleCount.Sample( );
  494. m_lCycleLimit = cycleCount.GetLongCycles( ) + dlCycles;
  495. }
  496. //-----------------------------------------------------------------------------
  497. // Purpose: Determines whether our specified time period has passed
  498. // Output: true if at least the specified time period has passed
  499. //-----------------------------------------------------------------------------
  500. inline bool CLimitTimer::BLimitReached( )
  501. {
  502. CCycleCount cycleCount;
  503. cycleCount.Sample( );
  504. return ( cycleCount.GetLongCycles( ) >= m_lCycleLimit );
  505. }
  506. #endif // FASTTIMER_H