280 lines
6.9 KiB
C
280 lines
6.9 KiB
C
/*
|
|
* 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("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;
|
|
}
|
|
|