Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

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