/* * clock.c - Routines for using the cycle counters on x86, * Alpha, and Sparc boxes. * * Copyright (c) 2002, R. Bryant and D. O'Hallaron, All rights reserved. * May not be used, modified, or copied without permission. */ #include #include #include #include #include "clock.h" /******************************************************* * Machine dependent functions * * Note: the constants __i386__ and __alpha * are set by GCC when it calls the C preprocessor * You can verify this for yourself using gcc -v. *******************************************************/ #if defined(__i386__) /******************************************************* * Pentium versions of start_counter() and get_counter() *******************************************************/ /* $begin x86cyclecounter */ /* Initialize the cycle counter */ static unsigned cyc_hi = 0; static unsigned cyc_lo = 0; /* Set *hi and *lo to the high and low order bits of the cycle counter. Implementation requires assembly code to use the rdtsc instruction. */ void access_counter(unsigned *hi, unsigned *lo) { asm("rdtsc; movl %%edx,%0; movl %%eax,%1" /* Read cycle counter */ : "=r" (*hi), "=r" (*lo) /* and move results to */ : /* No input */ /* the two outputs */ : "%edx", "%eax"); } /* Record the current value of the cycle counter. */ void start_counter() { access_counter(&cyc_hi, &cyc_lo); } /* Return the number of cycles since the last call to start_counter. */ double get_counter() { unsigned ncyc_hi, ncyc_lo; unsigned hi, lo, borrow; double result; /* Get cycle counter */ access_counter(&ncyc_hi, &ncyc_lo); /* Do double precision subtraction */ lo = ncyc_lo - cyc_lo; borrow = lo > ncyc_lo; hi = ncyc_hi - cyc_hi - borrow; result = (double) hi * (1 << 30) * 4 + lo; if (result < 0) { fprintf(stderr, "Error: counter returns neg value: %.0f\n", result); } return result; } /* $end x86cyclecounter */ #elif defined(__alpha) /**************************************************** * Alpha versions of start_counter() and get_counter() ***************************************************/ /* Initialize the cycle counter */ static unsigned cyc_hi = 0; static unsigned cyc_lo = 0; /* Use Alpha cycle timer to compute cycles. Then use measured clock speed to compute seconds */ /* * counterRoutine is an array of Alpha instructions to access * the Alpha's processor cycle counter. It uses the rpcc * instruction to access the counter. This 64 bit register is * divided into two parts. The lower 32 bits are the cycles * used by the current process. The upper 32 bits are wall * clock cycles. These instructions read the counter, and * convert the lower 32 bits into an unsigned int - this is the * user space counter value. * NOTE: The counter has a very limited time span. With a * 450MhZ clock the counter can time things for about 9 * seconds. */ static unsigned int counterRoutine[] = { 0x601fc000u, 0x401f0000u, 0x6bfa8001u }; /* Cast the above instructions into a function. */ static unsigned int (*counter)(void)= (void *)counterRoutine; void start_counter() { /* Get cycle counter */ cyc_hi = 0; cyc_lo = counter(); } double get_counter() { unsigned ncyc_hi, ncyc_lo; unsigned hi, lo, borrow; double result; ncyc_lo = counter(); ncyc_hi = 0; lo = ncyc_lo - cyc_lo; borrow = lo > ncyc_lo; hi = ncyc_hi - cyc_hi - borrow; result = (double) hi * (1 << 30) * 4 + lo; if (result < 0) { fprintf(stderr, "Error: Cycle counter returning negative value: %.0f\n", result); } return result; } #else /**************************************************************** * All the other platforms for which we haven't implemented cycle * counter routines. Newer models of sparcs (v8plus) have cycle * counters that can be accessed from user programs, but since there * are still many sparc boxes out there that don't support this, we * haven't provided a Sparc version here. ***************************************************************/ void start_counter() { printf("ERROR: You are trying to use a start_counter routine in clock.c\n"); printf("that has not been implemented yet on this platform.\n"); printf("Please choose another timing package in config.h.\n"); exit(1); } double get_counter() { printf("ERROR: You are trying to use a get_counter routine in clock.c\n"); printf("that has not been implemented yet on this platform.\n"); printf("Please choose another timing package in config.h.\n"); exit(1); } #endif /******************************* * Machine-independent functions ******************************/ double ovhd() { /* Do it twice to eliminate cache effects */ int i; double result; for (i = 0; i < 2; i++) { start_counter(); result = get_counter(); } return result; } /* $begin mhz */ /* Estimate the clock rate by measuring the cycles that elapse */ /* while sleeping for sleeptime seconds */ double mhz_full(int verbose, int sleeptime) { double rate; start_counter(); sleep(sleeptime); rate = get_counter() / (1e6*sleeptime); if (verbose) printf("Processor clock rate ~= %.1f MHz\n", rate); return rate; } /* $end mhz */ /* Version using a default sleeptime */ double mhz(int verbose) { return mhz_full(verbose, 2); } /** Special counters that compensate for timer interrupt overhead */ static double cyc_per_tick = 0.0; #define NEVENT 100 #define THRESHOLD 1000 #define RECORDTHRESH 3000 /* Attempt to see how much time is used by timer interrupt */ static void callibrate(int verbose) { double oldt; struct tms t; clock_t oldc; int e = 0; times(&t); oldc = t.tms_utime; start_counter(); oldt = get_counter(); while (e = THRESHOLD) { clock_t newc; times(&t); newc = t.tms_utime; if (newc > oldc) { double cpt = (newt-oldt)/(newc-oldc); if ((cyc_per_tick == 0.0 || cyc_per_tick > cpt) && cpt > RECORDTHRESH) cyc_per_tick = cpt; /* if (verbose) printf("Saw event lasting %.0f cycles and %d ticks. Ratio = %f\n", newt-oldt, (int) (newc-oldc), cpt); */ e++; oldc = newc; } oldt = newt; } } if (verbose) printf("Setting cyc_per_tick to %f\n", cyc_per_tick); } static clock_t start_tick = 0; void start_comp_counter() { struct tms t; if (cyc_per_tick == 0.0) callibrate(0); times(&t); start_tick = t.tms_utime; start_counter(); } double get_comp_counter() { double time = get_counter(); double ctime; struct tms t; clock_t ticks; times(&t); ticks = t.tms_utime - start_tick; ctime = time - ticks*cyc_per_tick; /* printf("Measured %.0f cycles. Ticks = %d. Corrected %.0f cycles\n", time, (int) ticks, ctime); */ return ctime; }