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.

279 lines
6.9 KiB

  1. /*
  2. * clock.c - Routines for using the cycle counters on x86,
  3. * Alpha, and Sparc boxes.
  4. *
  5. * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved.
  6. * May not be used, modified, or copied without permission.
  7. */
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <unistd.h>
  11. #include <sys/times.h>
  12. #include "clock.h"
  13. /*******************************************************
  14. * Machine dependent functions
  15. *
  16. * Note: the constants __i386__ and __alpha
  17. * are set by GCC when it calls the C preprocessor
  18. * You can verify this for yourself using gcc -v.
  19. *******************************************************/
  20. #if defined(__i386__)
  21. /*******************************************************
  22. * Pentium versions of start_counter() and get_counter()
  23. *******************************************************/
  24. /* $begin x86cyclecounter */
  25. /* Initialize the cycle counter */
  26. static unsigned cyc_hi = 0;
  27. static unsigned cyc_lo = 0;
  28. /* Set *hi and *lo to the high and low order bits of the cycle counter.
  29. Implementation requires assembly code to use the rdtsc instruction. */
  30. void access_counter(unsigned *hi, unsigned *lo)
  31. {
  32. asm("rdtsc; movl %%edx,%0; movl %%eax,%1" /* Read cycle counter */
  33. : "=r" (*hi), "=r" (*lo) /* and move results to */
  34. : /* No input */ /* the two outputs */
  35. : "%edx", "%eax");
  36. }
  37. /* Record the current value of the cycle counter. */
  38. void start_counter()
  39. {
  40. access_counter(&cyc_hi, &cyc_lo);
  41. }
  42. /* Return the number of cycles since the last call to start_counter. */
  43. double get_counter()
  44. {
  45. unsigned ncyc_hi, ncyc_lo;
  46. unsigned hi, lo, borrow;
  47. double result;
  48. /* Get cycle counter */
  49. access_counter(&ncyc_hi, &ncyc_lo);
  50. /* Do double precision subtraction */
  51. lo = ncyc_lo - cyc_lo;
  52. borrow = lo > ncyc_lo;
  53. hi = ncyc_hi - cyc_hi - borrow;
  54. result = (double) hi * (1 << 30) * 4 + lo;
  55. if (result < 0) {
  56. fprintf(stderr, "Error: counter returns neg value: %.0f\n", result);
  57. }
  58. return result;
  59. }
  60. /* $end x86cyclecounter */
  61. #elif defined(__alpha)
  62. /****************************************************
  63. * Alpha versions of start_counter() and get_counter()
  64. ***************************************************/
  65. /* Initialize the cycle counter */
  66. static unsigned cyc_hi = 0;
  67. static unsigned cyc_lo = 0;
  68. /* Use Alpha cycle timer to compute cycles. Then use
  69. measured clock speed to compute seconds
  70. */
  71. /*
  72. * counterRoutine is an array of Alpha instructions to access
  73. * the Alpha's processor cycle counter. It uses the rpcc
  74. * instruction to access the counter. This 64 bit register is
  75. * divided into two parts. The lower 32 bits are the cycles
  76. * used by the current process. The upper 32 bits are wall
  77. * clock cycles. These instructions read the counter, and
  78. * convert the lower 32 bits into an unsigned int - this is the
  79. * user space counter value.
  80. * NOTE: The counter has a very limited time span. With a
  81. * 450MhZ clock the counter can time things for about 9
  82. * seconds. */
  83. static unsigned int counterRoutine[] =
  84. {
  85. 0x601fc000u,
  86. 0x401f0000u,
  87. 0x6bfa8001u
  88. };
  89. /* Cast the above instructions into a function. */
  90. static unsigned int (*counter)(void)= (void *)counterRoutine;
  91. void start_counter()
  92. {
  93. /* Get cycle counter */
  94. cyc_hi = 0;
  95. cyc_lo = counter();
  96. }
  97. double get_counter()
  98. {
  99. unsigned ncyc_hi, ncyc_lo;
  100. unsigned hi, lo, borrow;
  101. double result;
  102. ncyc_lo = counter();
  103. ncyc_hi = 0;
  104. lo = ncyc_lo - cyc_lo;
  105. borrow = lo > ncyc_lo;
  106. hi = ncyc_hi - cyc_hi - borrow;
  107. result = (double) hi * (1 << 30) * 4 + lo;
  108. if (result < 0) {
  109. fprintf(stderr, "Error: Cycle counter returning negative value: %.0f\n", result);
  110. }
  111. return result;
  112. }
  113. #else
  114. /****************************************************************
  115. * All the other platforms for which we haven't implemented cycle
  116. * counter routines. Newer models of sparcs (v8plus) have cycle
  117. * counters that can be accessed from user programs, but since there
  118. * are still many sparc boxes out there that don't support this, we
  119. * haven't provided a Sparc version here.
  120. ***************************************************************/
  121. void start_counter()
  122. {
  123. printf("ERROR: You are trying to use a start_counter routine in clock.c\n");
  124. printf("that has not been implemented yet on this platform.\n");
  125. printf("Please choose another timing package in config.h.\n");
  126. exit(1);
  127. }
  128. double get_counter()
  129. {
  130. printf("ERROR: You are trying to use a get_counter routine in clock.c\n");
  131. printf("that has not been implemented yet on this platform.\n");
  132. printf("Please choose another timing package in config.h.\n");
  133. exit(1);
  134. }
  135. #endif
  136. /*******************************
  137. * Machine-independent functions
  138. ******************************/
  139. double ovhd()
  140. {
  141. /* Do it twice to eliminate cache effects */
  142. int i;
  143. double result;
  144. for (i = 0; i < 2; i++) {
  145. start_counter();
  146. result = get_counter();
  147. }
  148. return result;
  149. }
  150. /* $begin mhz */
  151. /* Estimate the clock rate by measuring the cycles that elapse */
  152. /* while sleeping for sleeptime seconds */
  153. double mhz_full(int verbose, int sleeptime)
  154. {
  155. double rate;
  156. start_counter();
  157. sleep(sleeptime);
  158. rate = get_counter() / (1e6*sleeptime);
  159. if (verbose)
  160. printf("Processor clock rate ~= %.1f MHz\n", rate);
  161. return rate;
  162. }
  163. /* $end mhz */
  164. /* Version using a default sleeptime */
  165. double mhz(int verbose)
  166. {
  167. return mhz_full(verbose, 2);
  168. }
  169. /** Special counters that compensate for timer interrupt overhead */
  170. static double cyc_per_tick = 0.0;
  171. #define NEVENT 100
  172. #define THRESHOLD 1000
  173. #define RECORDTHRESH 3000
  174. /* Attempt to see how much time is used by timer interrupt */
  175. static void callibrate(int verbose)
  176. {
  177. double oldt;
  178. struct tms t;
  179. clock_t oldc;
  180. int e = 0;
  181. times(&t);
  182. oldc = t.tms_utime;
  183. start_counter();
  184. oldt = get_counter();
  185. while (e <NEVENT) {
  186. double newt = get_counter();
  187. if (newt-oldt >= THRESHOLD) {
  188. clock_t newc;
  189. times(&t);
  190. newc = t.tms_utime;
  191. if (newc > oldc) {
  192. double cpt = (newt-oldt)/(newc-oldc);
  193. if ((cyc_per_tick == 0.0 || cyc_per_tick > cpt) && cpt > RECORDTHRESH)
  194. cyc_per_tick = cpt;
  195. /*
  196. if (verbose)
  197. printf("Saw event lasting %.0f cycles and %d ticks. Ratio = %f\n",
  198. newt-oldt, (int) (newc-oldc), cpt);
  199. */
  200. e++;
  201. oldc = newc;
  202. }
  203. oldt = newt;
  204. }
  205. }
  206. if (verbose)
  207. printf("Setting cyc_per_tick to %f\n", cyc_per_tick);
  208. }
  209. static clock_t start_tick = 0;
  210. void start_comp_counter()
  211. {
  212. struct tms t;
  213. if (cyc_per_tick == 0.0)
  214. callibrate(0);
  215. times(&t);
  216. start_tick = t.tms_utime;
  217. start_counter();
  218. }
  219. double get_comp_counter()
  220. {
  221. double time = get_counter();
  222. double ctime;
  223. struct tms t;
  224. clock_t ticks;
  225. times(&t);
  226. ticks = t.tms_utime - start_tick;
  227. ctime = time - ticks*cyc_per_tick;
  228. /*
  229. printf("Measured %.0f cycles. Ticks = %d. Corrected %.0f cycles\n",
  230. time, (int) ticks, ctime);
  231. */
  232. return ctime;
  233. }