0,0 → 1,279 |
/* |
* 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 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 <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; |
} |
|