Leaked source code of windows server 2003
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.

255 lines
6.8 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: Perf.c
  3. *
  4. * Performance counter functions. Uses the Pentium performance counters
  5. * if they are available, otherwise falls back to the system QueryPerformance
  6. * api's.
  7. *
  8. * InitPerfCounter MUST be called before using the QUERY_PERFORMANCE_XXX macros
  9. * as it initializes the two global functions pointers.
  10. *
  11. *
  12. *
  13. * Created: 13-10-95
  14. * Author: Stephen Estrop [StephenE]
  15. *
  16. * Copyright (c) 1994 - 1995 Microsoft Corporation. All Rights Reserved.
  17. \**************************************************************************/
  18. #include <windows.h>
  19. #include "Perf.h"
  20. PERFFUNCTION lpQueryPerfCounter;
  21. PERFFUNCTION lpQueryPerfFreqency;
  22. void
  23. GetFrequencyEstimate(
  24. LARGE_INTEGER *li
  25. );
  26. #ifdef TEST
  27. #include <stdio.h>
  28. /******************************Public*Routine******************************\
  29. * main
  30. *
  31. * Program entry point.
  32. *
  33. * History:
  34. * dd-mm-95 - StephenE - Created
  35. *
  36. \**************************************************************************/
  37. int __cdecl main( void )
  38. {
  39. LARGE_INTEGER liP1;
  40. LARGE_INTEGER liP2;
  41. LARGE_INTEGER liPf;
  42. InitPerfCounter();
  43. QUERY_PERFORMANCE_FREQUENCY(&liPf);
  44. // Time a 50 milli second sleep
  45. QUERY_PERFORMANCE_COUNTER(&liP1);
  46. Sleep(50);
  47. QUERY_PERFORMANCE_COUNTER(&liP2);
  48. printf("Pentium counter frequency = %u\n", liPf.LowPart );
  49. printf("Pentium counter %#X%X - %#X%X = %u\n",
  50. liP2.HighPart, liP2.LowPart, liP1.HighPart, liP1.LowPart,
  51. liP2.LowPart - liP1.LowPart
  52. );
  53. printf("Time taken = %6.6f seconds\n",
  54. (double)(liP2.LowPart - liP1.LowPart) / (double)liPf.QuadPart);
  55. return 0;
  56. }
  57. #endif
  58. /******************************Public*Routine******************************\
  59. * InitPerfCounter
  60. *
  61. * Determine (at runtime) if it is possible to use the Pentium performance
  62. * counter. If it is not fall back to the system performance counter.
  63. *
  64. * History:
  65. * dd-mm-95 - StephenE - Created
  66. *
  67. \**************************************************************************/
  68. void
  69. InitPerfCounter(
  70. void
  71. )
  72. {
  73. SYSTEM_INFO sysInfo;
  74. GetSystemInfo(&sysInfo);
  75. if (sysInfo.dwProcessorType == PROCESSOR_INTEL_PENTIUM) {
  76. lpQueryPerfFreqency = QueryPerfFrequency;
  77. lpQueryPerfCounter = QueryPerfCounter;
  78. }
  79. else {
  80. lpQueryPerfFreqency = (PERFFUNCTION)QueryPerformanceFrequency;
  81. lpQueryPerfCounter = (PERFFUNCTION)QueryPerformanceCounter;
  82. }
  83. }
  84. /******************************Public*Routine******************************\
  85. * QueryPerfFrequency
  86. *
  87. * Determines the clock frequency of a (Pentium) microprocessor. Takes an
  88. * averaged estimate of the clk frequency and then matches it to known
  89. * Pentium clock frequencies. Returns the estimate if a match is not found.
  90. *
  91. * This is an expensive call in cpu terms as it takes at least 16 milli seconds
  92. * just to calculate an averaged estimate of the clock speed. You only need
  93. * to call this function once, make sure you don't call it more times.
  94. *
  95. * History:
  96. * 13-10-95 - StephenE - Created
  97. *
  98. \**************************************************************************/
  99. void WINAPI
  100. QueryPerfFrequency(
  101. LARGE_INTEGER *li
  102. )
  103. {
  104. #ifdef _X86_
  105. #define SAMPLE_SIZE 8
  106. LARGE_INTEGER est;
  107. int i;
  108. li->QuadPart = 0;
  109. for (i = 0; i < SAMPLE_SIZE; i++) {
  110. GetFrequencyEstimate(&est);
  111. li->QuadPart += est.QuadPart;
  112. }
  113. li->QuadPart /= SAMPLE_SIZE;
  114. //
  115. // At the moment Pentiums come in 60, 66, 75, 90, 100, 120 and 133 MHz
  116. // clock speeds. So use the above estimation of the clock frequency
  117. // to determine the real clock frequency.
  118. //
  119. // 59Mhz to 61Mhz assume its a 60 Mhz
  120. if (li->QuadPart >= 59000000 && li->QuadPart < 61000000) {
  121. li->QuadPart = 60000000;
  122. }
  123. // 65Mhz to 67Mhz assume its a 66 Mhz
  124. else if (li->QuadPart >= 65000000 && li->QuadPart < 67000000) {
  125. li->QuadPart = 66000000;
  126. }
  127. // 74Mhz to 76Mhz assume its a 75 Mhz
  128. else if (li->QuadPart >= 74000000 && li->QuadPart < 76000000) {
  129. li->QuadPart = 75000000;
  130. }
  131. // 89Mhz to 91Mhz assume its a 90 Mhz
  132. else if (li->QuadPart >= 89000000 && li->QuadPart < 91000000) {
  133. li->QuadPart = 90000000;
  134. }
  135. // 99Mhz to 101Mhz assume its a 100 Mhz
  136. else if (li->QuadPart >= 99000000 && li->QuadPart < 101000000) {
  137. li->QuadPart = 100000000;
  138. }
  139. // 119Mhz to 121Mhz assume its a 120 Mhz
  140. else if (li->QuadPart >= 119000000 && li->QuadPart < 121000000) {
  141. li->QuadPart = 120000000;
  142. }
  143. // 132Mhz to 134Mhz assume its a 133 Mhz
  144. else if (li->QuadPart >= 132000000 && li->QuadPart < 134000000) {
  145. li->QuadPart = 133000000;
  146. }
  147. // if use our estimate.
  148. #else
  149. li->QuadPart = -1;
  150. #endif
  151. }
  152. /*****************************Private*Routine******************************\
  153. * GetFrequencyEstimate
  154. *
  155. * Uses the system QueryPerformance counter to estimate the Pentium
  156. * cpu clock * frequency
  157. *
  158. * History:
  159. * 13-10-95 - StephenE - Created
  160. *
  161. \**************************************************************************/
  162. void
  163. GetFrequencyEstimate(
  164. LARGE_INTEGER *li
  165. )
  166. {
  167. LARGE_INTEGER liP1; // Pentium clk start
  168. LARGE_INTEGER liP2; // Pentium clk end
  169. LARGE_INTEGER liS1; // System clk end
  170. LARGE_INTEGER liS2; // System clk end
  171. LARGE_INTEGER liSf; // System clk frequency
  172. QueryPerformanceFrequency(&liSf);
  173. QueryPerformanceCounter(&liS1);
  174. QueryPerfCounter(&liP1);
  175. Sleep(2); // Sleep for approx 2 milli- seconds
  176. QueryPerfCounter(&liP2);
  177. QueryPerformanceCounter(&liS2);
  178. //
  179. // Determine the time recorded by both clocks.
  180. //
  181. liP2.QuadPart = liP2.QuadPart - liP1.QuadPart;
  182. liS2.QuadPart = liS2.QuadPart - liS1.QuadPart;
  183. li->QuadPart = (liP2.QuadPart * liSf.QuadPart) / liS2.QuadPart;
  184. }
  185. /******************************Public*Routine******************************\
  186. * QueryPerfCounter
  187. *
  188. * Query the internal clock counter on the Pentium, uses the undocumented
  189. * rdtsc instruction, which copies the current 64 bit clock count into
  190. * edx:eax.
  191. *
  192. * History:
  193. * 13-10-95 - StephenE - Created
  194. *
  195. \**************************************************************************/
  196. void WINAPI
  197. QueryPerfCounter(
  198. LARGE_INTEGER *li
  199. )
  200. {
  201. #ifdef _X86_
  202. _asm mov ecx, dword ptr li // copy li pointer value to ecx
  203. _asm _emit 0x0f // opcode 0x0F31 is rdtsc
  204. _asm _emit 0x31
  205. _asm mov dword ptr [ecx], eax // save result in li->LowPart
  206. _asm mov dword ptr [ecx+4], edx // and li->HighPart
  207. #else
  208. ;
  209. #endif
  210. }