Annotation of OpenXM_contrib/gmp/tune/time.c, Revision 1.1.1.1
1.1 maekawa 1: /* Time routines for speed measurments. */
2:
3: /*
4: Copyright (C) 1999, 2000 Free Software Foundation, Inc.
5:
6: This file is part of the GNU MP Library.
7:
8: The GNU MP Library is free software; you can redistribute it and/or modify
9: it under the terms of the GNU Lesser General Public License as published by
10: the Free Software Foundation; either version 2.1 of the License, or (at your
11: option) any later version.
12:
13: The GNU MP Library is distributed in the hope that it will be useful, but
14: WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15: or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16: License for more details.
17:
18: You should have received a copy of the GNU Lesser General Public License
19: along with the GNU MP Library; see the file COPYING.LIB. If not, write to
20: the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21: MA 02111-1307, USA.
22: */
23:
24: /* speed_time_init() - initialize timing things. speed_starttime() calls
25: this if it hasn't been done yet, so you only need to call this explicitly
26: if you want to use the global variables before the first measurement.
27:
28: speed_starttime() - start a time measurment.
29:
30: speed_endtime() - end a time measurment, return time taken, in seconds.
31:
32: speed_unittime - global variable with the unit of time measurement
33: accuracy, in seconds.
34:
35: speed_precision - global variable which is the intended accuracy of time
36: measurements. speed_measure() for instance runs target routines with
37: enough repetitions so it takes at least speed_unittime*speed_precision
38: seconds. A program can provide an option so the user can set this.
39:
40: speed_cycletime - the time in seconds for each CPU cycle, for example on
41: a 100 MHz CPU this would be 1.0e-8. If the CPU frequency is unknown,
42: speed_cycletime is 1.0. See speed_cycletime_init().
43:
44: speed_time_string - a string describing the time method in use.
45:
46:
47: Enhancements:
48:
49: Add support for accurate timing on more CPUs, machines and systems.
50:
51: Extend automatic CPU frequency determination to more kernels and systems.
52:
53: */
54:
55:
56: #include <stdio.h>
57: #include <stdlib.h> /* for getenv */
58: #if HAVE_UNISTD_H
59: #include <unistd.h>
60: #endif
61:
62: #include <sys/types.h>
63: #if HAVE_SYS_SYSCTL_H
64: #include <sys/sysctl.h>
65: #endif
66:
67: #include "gmp.h"
68: #include "gmp-impl.h"
69: #include "longlong.h"
70:
71: #include "speed.h"
72:
73:
74: #if HAVE_SPEED_CYCLECOUNTER
75: #define SPEED_USE_CYCLECOUNTER 1
76: #else
77: #define SPEED_USE_MICROSECOND_GETRUSAGE 0
78: #define SPEED_USE_MICROSECOND_GETTIMEOFDAY 1
79: #define SPEED_USE_TMS_UTIME 0
80: #endif
81:
82:
83: #define TIMEVAL_SECS(tp) \
84: ((double) (tp)->tv_sec + (double) (tp)->tv_usec * 1.0e-6)
85:
86:
87: /* Look for an environment variable for CPU clock frequency.
88: GMP_CPU_FREQUENCY should be in Hertz, in floating point form,
89: eg. "450e6". */
90: int
91: speed_cpu_frequency_environment (void)
92: {
93: char *e;
94:
95: e = getenv ("GMP_CPU_FREQUENCY");
96: if (e == NULL)
97: return 0;
98:
99: speed_cycletime = 1.0 / atof (e);
100: return 1;
101: }
102:
103:
104: /* On FreeBSD 3.3 the headers have #defines like CPU_WALLCLOCK under
105: CTL_MACHDEP but don't seem to have anything for machdep.tsc_freq or
106: machdep.i586_freq. Using the string forms with sysctlbyname() works
107: though, and lets libc worry about the defines and headers.
108:
109: FreeBSD 3.3 has tsc_freq, FreeBSD 2.2.8 has i586_freq instead.
110: The "sysctl -a" command prints everything available. */
111:
112: #if HAVE_SYSCTLBYNAME
113: int
114: speed_cpu_frequency_sysctlbyname (void)
115: {
116: unsigned val;
117: size_t valsize;
118:
119: valsize = sizeof(val);
120: if (sysctlbyname ("machdep.tsc_freq", &val, &valsize, NULL, 0) != 0
121: || valsize != sizeof(val))
122: {
123: valsize = sizeof(val);
124: if (sysctlbyname ("machdep.i586_freq", &val, &valsize, NULL, 0) != 0
125: || valsize != sizeof(val))
126: return 0;
127: }
128:
129: speed_cycletime = 1.0 / (double) val;
130: return 1;
131: }
132: #endif
133:
134:
135: /* Linux doesn't seem to have any system call to get the CPU frequency, at
136: least not in 2.0.x or 2.2.x, so it's necessary to read /proc/cpuinfo.
137:
138: i386 2.0.36 - "bogomips" is the CPU frequency.
139:
140: i386 2.2.13 - has both "cpu MHz" and "bogomips", and it's "cpu MHz" which
141: is the frequency.
142:
143: alpha 2.2.5 - "cycle frequency [Hz]" seems to be right, "BogoMIPS" is
144: very slightly different. */
145:
146: int
147: speed_cpu_frequency_proc_cpuinfo (void)
148: {
149: FILE *fp;
150: char buf[128];
151: double val;
152: int ret = 0;
153:
154: if ((fp = fopen ("/proc/cpuinfo", "r")) != NULL)
155: {
156: while (fgets (buf, sizeof (buf), fp) != NULL)
157: {
158: if (sscanf (buf, "cycle frequency [Hz] : %lf est.\n", &val) == 1)
159: {
160: speed_cycletime = 1.0 / val;
161: ret = 1;
162: break;
163: }
164: if (sscanf (buf, "cpu MHz : %lf\n", &val) == 1)
165: {
166: speed_cycletime = 1e-6 / val;
167: ret = 1;
168: break;
169: }
170: if (sscanf (buf, "bogomips : %lf\n", &val) == 1)
171: {
172: speed_cycletime = 1e-6 / val;
173: ret = 1;
174: break;
175: }
176: }
177: fclose (fp);
178: }
179: return ret;
180: }
181:
182:
183: /* SunOS /bin/sysinfo prints a line like:
184: cpu0 is a "75 MHz TI,TMS390Z55" CPU */
185:
186: #if HAVE_POPEN
187: int
188: speed_cpu_frequency_sunos_sysinfo (void)
189: {
190: FILE *fp;
191: char buf[128];
192: double val;
193: int ret = 0;
194:
195: /* Error messages are sent to /dev/null in case /bin/sysinfo doesn't
196: exist. The brackets are necessary for some shells (eg. ash). */
197: if ((fp = popen ("(/bin/sysinfo) 2>/dev/null", "r")) != NULL)
198: {
199: while (fgets (buf, sizeof (buf), fp) != NULL)
200: {
201: if (sscanf (buf, " cpu0 is a \"%lf MHz", &val) == 1)
202: {
203: speed_cycletime = 1e-6 / val;
204: ret = 1;
205: break;
206: }
207: }
208: pclose (fp);
209: }
210: return ret;
211: }
212: #endif
213:
214:
215: /* This is for Solaris. "psrinfo" is the command-line interface to
216: processor_info(). "prtconf -vp" gives similar information. */
217:
218: #if HAVE_PROCESSOR_INFO
219: #include <sys/unistd.h> /* for _SC_NPROCESSORS_CONF */
220: #include <sys/processor.h> /* for processor_info_t */
221: int
222: speed_cpu_frequency_processor_info (void)
223: {
224: processor_info_t p;
225: int i, n, mhz = 0;
226:
227: n = sysconf (_SC_NPROCESSORS_CONF);
228: for (i = 0; i < n; i++)
229: {
230: if (processor_info (i, &p) != 0)
231: continue;
232: if (p.pi_state != P_ONLINE)
233: continue;
234:
235: if (mhz != 0 && p.pi_clock != mhz)
236: {
237: fprintf (stderr,
238: "speed_cpu_frequency_processor_info(): There's more than one CPU and they have different clock speeds\n");
239: return 0;
240: }
241:
242: mhz = p.pi_clock;
243: }
244:
245: speed_cycletime = 1.0e-6 / (double) mhz;
246: return 1;
247: }
248: #endif
249:
250:
251: /* Each function returns 1 if it succeeds in setting speed_cycletime, or 0
252: if not. */
253:
254: static const struct {
255: int (*fun) _PROTO ((void));
256: const char *description;
257:
258: } speed_cpu_frequency_table[] = {
259:
260: /* This should be first, so an environment variable can override anything
261: the system gives. */
262: { speed_cpu_frequency_environment,
263: "environment variable GMP_CPU_FREQUENCY (in Hertz)" },
264:
265: #if HAVE_SYSCTLBYNAME
266: { speed_cpu_frequency_sysctlbyname,
267: "sysctlbyname() machdep.tsc_freq or machdep.i586_freq" },
268: #endif
269:
270: #if HAVE_PROCESSOR_INFO
271: { speed_cpu_frequency_processor_info,
272: "processor_info() pi_clock" },
273: #endif
274:
275: { speed_cpu_frequency_proc_cpuinfo,
276: "linux kernel /proc/cpuinfo file, cpu MHz or bogomips" },
277:
278: #if HAVE_POPEN
279: { speed_cpu_frequency_sunos_sysinfo,
280: "SunOS /bin/sysinfo program cpu0 output" },
281: #endif
282: };
283:
284:
285: int
286: speed_cycletime_init (void)
287: {
288: int i;
289:
290: for (i = 0; i < numberof (speed_cpu_frequency_table); i++)
291: if ((*speed_cpu_frequency_table[i].fun)())
292: return 1;
293:
294: fprintf (stderr,
295: "Cannot determine CPU frequency, need one of the following\n");
296: for (i = 0; i < numberof (speed_cpu_frequency_table); i++)
297: fprintf (stderr, "\t- %s\n", speed_cpu_frequency_table[i].description);
298:
299: return 0;
300: }
301:
302:
303: /* ---------------------------------------------------------------------- */
304: #if SPEED_USE_CYCLECOUNTER
305:
306: const char *speed_time_string
307: = "Time measurements using CPU cycle counter.\n";
308:
309: /* bigish value because we have a fast timer */
310: int speed_precision = 10000;
311:
312: double speed_unittime;
313: double speed_cycletime;
314:
315: static int speed_time_initialized = 0;
316: static unsigned speed_starttime_save[2];
317:
318: /* Knowing the CPU frequency is mandatory, so cycles can be converted to
319: seconds. */
320: void
321: speed_time_init (void)
322: {
323: if (speed_time_initialized)
324: return;
325: speed_time_initialized = 1;
326:
327: if (!speed_cycletime_init ())
328: exit (1);
329:
330: speed_unittime = speed_cycletime;
331: }
332:
333: void
334: speed_starttime (void)
335: {
336: if (!speed_time_initialized)
337: speed_time_init ();
338: speed_cyclecounter (speed_starttime_save);
339: }
340:
341: #define M_2POWU ((double) (1L << (BITS_PER_INT-2)) * 4.0)
342: #define M_2POW32 4294967296.0
343:
344: double
345: speed_endtime (void)
346: {
347: unsigned endtime[2], e0;
348: double t;
349:
350: speed_cyclecounter (endtime);
351:
352: /* This still works even if speed_cyclecounter() puts a value bigger than
353: 32-bits in the low word. The start and end values are allowed to
354: cancel in uints in case a uint is more than the 53 bits that will
355: normally fit in a double. */
356: e0 = endtime[0] - speed_starttime_save[0];
357: t = e0 - (e0 > endtime[0] ? M_2POWU : 0);
358: t += (endtime[1] - speed_starttime_save[1]) * M_2POW32;
359:
360: return t * speed_unittime;
361: }
362:
363: #endif
364:
365:
366: /* ---------------------------------------------------------------------- */
367: #if SPEED_USE_MICROSECOND_GETRUSAGE
368: #include <sys/types.h>
369: #include <sys/time.h>
370: #include <sys/resource.h>
371:
372: const char *speed_time_string
373: = "Time measurements using microsecond accurate getrusage.\n";
374:
375: int speed_precision = 1000;
376:
377: double speed_unittime = 1.0e-6;
378: double speed_cycletime = 1.0;
379:
380: static struct rusage speed_starttime_save;
381: static int speed_time_initialized = 0;
382:
383: void
384: speed_time_init (void)
385: {
386: if (speed_time_initialized)
387: return;
388: speed_time_initialized = 1;
389:
390: speed_cycletime_init ();
391: }
392:
393: void
394: speed_starttime (void)
395: {
396: if (!speed_time_initialized)
397: speed_time_init ();
398:
399: getrusage (0, &speed_starttime_save);
400: }
401:
402: double
403: speed_endtime (void)
404: {
405: struct rusage r;
406:
407: getrusage (0, &r);
408: return TIMEVAL_SECS (&r.ru_utime)
409: - TIMEVAL_SECS (&speed_starttime_save.ru_utime);
410: }
411: #endif
412:
413:
414: /* ---------------------------------------------------------------------- */
415: #if SPEED_USE_MICROSECOND_GETTIMEOFDAY
416: /* This method is for systems with a microsecond accurate gettimeofday().
417:
418: A dummy timezone parameter is always given to gettimeofday(), in case it
419: doesn't allow NULL. */
420:
421: #include <sys/time.h>
422:
423: const char *speed_time_string
424: = "Time measurements using microsecond accurate gettimeofday.\n";
425:
426: /* highish value because we have an accurate timer */
427: int speed_precision = 1000;
428:
429: double speed_unittime = 1.0e-6;
430: double speed_cycletime = 1.0;
431:
432: static struct timeval speed_starttime_save;
433: static int speed_time_initialized = 0;
434:
435: void
436: speed_time_init (void)
437: {
438: if (speed_time_initialized)
439: return;
440: speed_time_initialized = 1;
441:
442: speed_cycletime_init ();
443: }
444:
445: void
446: speed_starttime (void)
447: {
448: struct timezone tz;
449: if (!speed_time_initialized)
450: speed_time_init ();
451:
452: gettimeofday (&speed_starttime_save, &tz);
453: }
454:
455: double
456: speed_endtime (void)
457: {
458: struct timeval t;
459: struct timezone tz;
460:
461: gettimeofday (&t, &tz);
462: return TIMEVAL_SECS (&t) - TIMEVAL_SECS (&speed_starttime_save);
463: }
464:
465: #endif
466:
467:
468: /* ---------------------------------------------------------------------- */
469: #if SPEED_USE_TMS_UTIME
470: /* You're in trouble if you have to use this method. Speed measurments and
471: threshold tuning are going to take a long time. */
472:
473: #if STDC_HEADERS
474: #include <errno.h> /* for errno */
475: #include <string.h> /* for strerror */
476: #endif
477: #if HAVE_UNISTD_H
478: #include <unistd.h> /* for sysconf */
479: #endif
480: #include <sys/times.h> /* for times */
481:
482: const char *speed_time_string
483: = "Time measurements using tms_utime.\n";
484:
485:
486: /* lowish default value so we don't take days and days to do tuning */
487: int speed_precision = 200;
488:
489: double speed_unittime;
490: double speed_cycletime = 1.0;
491:
492: static struct tms speed_starttime_save;
493: static int speed_time_initialized = 0;
494:
495: void
496: speed_time_init (void)
497: {
498: long clk_tck;
499:
500: if (speed_time_initialized)
501: return;
502: speed_time_initialized = 1;
503:
504: speed_cycletime_init ();
505:
506: #if HAVE_SYSCONF
507: clk_tck = sysconf (_SC_CLK_TCK);
508: if (clk_tck == -1L)
509: {
510: fprintf (stderr, "sysconf(_SC_CLK_TCK) not available: %s\n",
511: strerror(errno));
512: fprintf (stderr, "\tusing CLK_TCK instead\n");
513: clk_tck = CLK_TCK;
514: }
515: #else
516: clk_tck = CLK_TCK;
517: #endif
518:
519: speed_unittime = 1.0 / (double) clk_tck;
520: }
521:
522: /* Burn up CPU until a times() tms_utime tick boundary.
523: Doing so lets you know a measurement has started on a tick boundary,
524: effectively halving the uncertainty in the measurement.
525: *t1 gets the start times() values the caller should use. */
526: void
527: times_utime_boundary (struct tms *t1)
528: {
529: struct tms t2;
530: times (&t2);
531: do
532: times (t1);
533: while (t1->tms_utime == t2.tms_utime);
534: }
535:
536: void
537: speed_starttime (void)
538: {
539: if (!speed_time_initialized)
540: speed_time_init ();
541: times_utime_boundary (&speed_starttime_save);
542: }
543:
544: double
545: speed_endtime (void)
546: {
547: struct tms t;
548: times (&t);
549: return (t.tms_utime - speed_starttime_save.tms_utime) * speed_unittime;
550: }
551:
552: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>