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.

177 lines
3.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: determine CPU speed under linux
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <sys/types.h>
  10. #include <sys/sysctl.h>
  11. #include <sys/time.h>
  12. #include <unistd.h>
  13. #include <tier0/platform.h>
  14. #include <errno.h>
  15. #define rdtsc(x) \
  16. __asm__ __volatile__ ("rdtsc" : "=A" (x))
  17. class TimeVal
  18. {
  19. public:
  20. TimeVal() {}
  21. TimeVal& operator=(const TimeVal &val) { m_TimeVal = val.m_TimeVal; return *this; }
  22. inline double operator-(const TimeVal &left)
  23. {
  24. uint64 left_us = (uint64) left.m_TimeVal.tv_sec * 1000000 + left.m_TimeVal.tv_usec;
  25. uint64 right_us = (uint64) m_TimeVal.tv_sec * 1000000 + m_TimeVal.tv_usec;
  26. uint64 diff_us = right_us - left_us;
  27. return diff_us * ( 1.0 / 1000000.0 );
  28. }
  29. timeval m_TimeVal;
  30. };
  31. // Compute the positive difference between two 64 bit numbers.
  32. static inline uint64 diff(uint64 v1, uint64 v2)
  33. {
  34. int64 d = v1 - v2;
  35. if (d >= 0)
  36. return d;
  37. else
  38. return -d;
  39. }
  40. #ifdef OSX
  41. // Mac
  42. uint64 GetCPUFreqFromPROC()
  43. {
  44. int mib[2] = {CTL_HW, HW_CPU_FREQ};
  45. uint64 frequency = 0;
  46. size_t len = sizeof(frequency);
  47. if (sysctl(mib, 2, &frequency, &len, NULL, 0) == -1)
  48. return 0;
  49. return frequency;
  50. }
  51. #else
  52. // Linux
  53. uint64 GetCPUFreqFromPROC()
  54. {
  55. double mhz = 0;
  56. char line[1024], *s, search_str[] = "cpu MHz";
  57. /* open proc/cpuinfo */
  58. FILE *fp = fopen( "/proc/cpuinfo", "r" );
  59. if (fp == NULL)
  60. {
  61. return 0;
  62. }
  63. /* ignore all lines until we reach MHz information */
  64. while (fgets(line, 1024, fp) != NULL)
  65. {
  66. if (strstr(line, search_str) != NULL)
  67. {
  68. /* ignore all characters in line up to : */
  69. for (s = line; *s && (*s != ':'); ++s)
  70. ;
  71. /* get MHz number */
  72. if ( *s && ( sscanf( s + 1, "%lf", &mhz) == 1 ) )
  73. break;
  74. }
  75. }
  76. fclose(fp);
  77. return ( uint64 )( mhz * 1000000 );
  78. }
  79. #endif
  80. uint64 CalculateCPUFreq()
  81. {
  82. #ifdef LINUX
  83. char const *pFreq = getenv( "CPU_MHZ" );
  84. if ( pFreq )
  85. {
  86. uint64 retVal = 1000000;
  87. return retVal * atoi( pFreq );
  88. }
  89. #endif
  90. // Try to open cpuinfo_max_freq. If the kernel was built with cpu scaling support disabled, this will fail.
  91. FILE *fp = fopen( "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", "r" );
  92. if ( fp )
  93. {
  94. char buf[ 256 ];
  95. uint64 retVal = 0;
  96. buf[ 0 ] = 0;
  97. if( fread( buf, 1, ARRAYSIZE( buf ), fp ) )
  98. {
  99. retVal = ( uint64 )atoll( buf );
  100. }
  101. fclose(fp);
  102. if( retVal )
  103. {
  104. return retVal * 1000;
  105. }
  106. }
  107. // Compute the period. Loop until we get 3 consecutive periods that
  108. // are the same to within a small error. The error is chosen
  109. // to be +/- 0.02% on a P-200.
  110. const uint64 error = 40000;
  111. const int max_iterations = 600;
  112. int count;
  113. uint64 period, period1 = error * 2, period2 = 0, period3 = 0;
  114. for (count = 0; count < max_iterations; count++)
  115. {
  116. TimeVal start_time, end_time;
  117. uint64 start_tsc, end_tsc;
  118. gettimeofday( &start_time.m_TimeVal, 0 );
  119. rdtsc( start_tsc );
  120. usleep( 5000 ); // sleep for 5 msec
  121. gettimeofday( &end_time.m_TimeVal, 0 );
  122. rdtsc( end_tsc );
  123. // end_time - start_time calls into the overloaded TimeVal operator- way above, and returns a double.
  124. period3 = ( end_tsc - start_tsc ) / ( end_time - start_time );
  125. if (diff ( period1, period2 ) <= error &&
  126. diff ( period2, period3 ) <= error &&
  127. diff ( period1, period3 ) <= error )
  128. {
  129. break;
  130. }
  131. period1 = period2;
  132. period2 = period3;
  133. }
  134. if ( count == max_iterations )
  135. {
  136. return GetCPUFreqFromPROC(); // fall back to /proc
  137. }
  138. // Set the period to the average period measured.
  139. period = ( period1 + period2 + period3 ) / 3;
  140. // Some Pentiums have broken TSCs that increment very
  141. // slowly or unevenly.
  142. if (period < 10000000)
  143. {
  144. return GetCPUFreqFromPROC(); // fall back to /proc
  145. }
  146. return period;
  147. }