Blame | Last modification | View Log | Download | RSS feed
/** 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 <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/times.h>#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 volatile ("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 usemeasured 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 <NEVENT) {double newt = get_counter();if (newt-oldt >= 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;}