freq.c revision 1.1.1.1 1 /* CPU frequency determination.
2
3 Copyright 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
4
5 This file is part of the GNU MP Library.
6
7 The GNU MP Library is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11
12 The GNU MP Library is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
19
20
21 /* Currently we don't get a CPU frequency on the following systems,
22
23 alphaev5-cray-unicosmk2.0.6.X
24 times() has been seen at 13.33 ns (75 MHz), which is probably not the
25 cpu frequency. Measuring the cycle counter against that would be
26 possible though. But currently we don't use the cycle counter due to
27 unicos having int==8bytes where tune/alpha.asm assumes int==4bytes.
28
29 m68040-unknown-netbsd1.4.1
30 Not sure if the system even knows the cpu frequency. There's no
31 cycle counter to measure, though we could perhaps make a loop taking
32 a known number of cycles and measure that.
33
34 power-ibm-aix4.2.1.0
35 power2-ibm-aix4.3.1.0
36 powerpc604-ibm-aix4.3.1.0
37 powerpc604-ibm-aix4.3.3.0
38 powerpc630-ibm-aix4.3.3.0
39 powerpc-unknown-netbsd1.6
40 Don't know where any info hides on these. mftb is not related to the
41 cpu frequency so doesn't help.
42
43 sparc-unknown-linux-gnu [maybe]
44 Don't know where any info hides on this.
45
46 t90-cray-unicos10.0.X
47 The times() call seems to be for instance 2.22 nanoseconds, which
48 might be the cpu frequency (450 mhz), but need to confirm that.
49
50 */
51
52 #include "config.h"
53
54 #if HAVE_INVENT_H
55 #include <invent.h> /* for IRIX invent_cpuinfo_t */
56 #endif
57
58 #include <stdio.h>
59 #include <stdlib.h> /* for getenv, qsort */
60 #include <string.h> /* for memcmp */
61
62 #if HAVE_UNISTD_H
63 #include <unistd.h> /* for sysconf */
64 #endif
65
66 #include <sys/types.h>
67
68 #if HAVE_SYS_ATTRIBUTES_H
69 #include <sys/attributes.h> /* for IRIX attr_get(), needs sys/types.h */
70 #endif
71
72 #if HAVE_SYS_IOGRAPH_H
73 #include <sys/iograph.h> /* for IRIX INFO_LBL_DETAIL_INVENT */
74 #endif
75
76 #if HAVE_SYS_PARAM_H /* for constants needed by NetBSD <sys/sysctl.h> */
77 #include <sys/param.h> /* and needed by HPUX <sys/pstat.h> */
78 #endif
79
80 #if HAVE_SYS_PSTAT_H
81 #include <sys/pstat.h> /* for HPUX pstat_getprocessor() */
82 #endif
83
84 #if HAVE_SYS_SYSCTL_H
85 #include <sys/sysctl.h> /* for sysctlbyname() */
86 #endif
87
88 #if TIME_WITH_SYS_TIME
89 # include <sys/time.h> /* for struct timeval */
90 # include <time.h>
91 #else
92 # if HAVE_SYS_TIME_H
93 # include <sys/time.h>
94 # else
95 # include <time.h>
96 # endif
97 #endif
98
99 #if HAVE_SYS_RESOURCE_H
100 #include <sys/resource.h> /* for struct rusage */
101 #endif
102
103 #if HAVE_SYS_PROCESSOR_H
104 #include <sys/processor.h> /* for solaris processor_info_t */
105 #endif
106
107 /* On AIX 5.1 with gcc 2.9-aix51-020209 in -maix64 mode, <sys/sysinfo.h>
108 gets an error about "fill" in "struct cpuinfo" having a negative size,
109 apparently due to __64BIT_KERNEL not being defined because _KERNEL is not
110 defined. Avoid this file if we don't actually need it, which we don't on
111 AIX since there's no getsysinfo there. */
112 #if HAVE_SYS_SYSINFO_H && HAVE_GETSYSINFO
113 #include <sys/sysinfo.h> /* for OSF getsysinfo */
114 #endif
115
116 #if HAVE_MACHINE_HAL_SYSINFO_H
117 #include <machine/hal_sysinfo.h> /* for OSF GSI_CPU_INFO, struct cpu_info */
118 #endif
119
120 /* Remove definitions from NetBSD <sys/param.h>, to avoid conflicts with
121 gmp-impl.h. */
122 #ifdef MIN
123 #undef MIN
124 #endif
125 #ifdef MAX
126 #undef MAX
127 #endif
128
129 #include "gmp.h"
130 #include "gmp-impl.h"
131
132 #include "speed.h"
133
134
135 #define HELP(str) \
136 if (help) \
137 { \
138 printf (" - %s\n", str); \
139 return 0; \
140 }
141
142
143 /* GMP_CPU_FREQUENCY environment variable. Should be in Hertz and can be
144 floating point, for example "450e6". */
145 static int
146 freq_environment (int help)
147 {
148 char *e;
149
150 HELP ("environment variable GMP_CPU_FREQUENCY (in Hertz)");
151
152 e = getenv ("GMP_CPU_FREQUENCY");
153 if (e == NULL)
154 return 0;
155
156 speed_cycletime = 1.0 / atof (e);
157
158 if (speed_option_verbose)
159 printf ("Using GMP_CPU_FREQUENCY %.2f for cycle time %.3g\n",
160 atof (e), speed_cycletime);
161
162 return 1;
163 }
164
165
166 /* getsysinfo is available on OSF, or 4.0 and up at least.
167 The man page (on 4.0) suggests a 0 return indicates information not
168 available, but that seems to be the normal return for GSI_CPU_INFO. */
169 static int
170 freq_getsysinfo (int help)
171 {
172 #if HAVE_GETSYSINFO
173 struct cpu_info c;
174 int start;
175
176 HELP ("getsysinfo() GSI_CPU_INFO");
177
178 start = 0;
179 if (getsysinfo (GSI_CPU_INFO, (caddr_t) &c, sizeof (c),
180 &start, NULL, NULL) != -1)
181 {
182 speed_cycletime = 1e-6 / (double) c.mhz;
183 if (speed_option_verbose)
184 printf ("Using getsysinfo() GSI_CPU_INFO %u for cycle time %.3g\n",
185 c.mhz, speed_cycletime);
186 return 1;
187 }
188 #endif
189 return 0;
190 }
191
192
193 /* In HPUX 10 and up, pstat_getprocessor() psp_iticksperclktick is the
194 number of CPU cycles (ie. the CR16 register) per CLK_TCK. HPUX 9 doesn't
195 have that field in pst_processor though, and has no apparent
196 equivalent. */
197
198 static int
199 freq_pstat_getprocessor (int help)
200 {
201 #if HAVE_PSTAT_GETPROCESSOR && HAVE_PSP_ITICKSPERCLKTICK
202 struct pst_processor p;
203
204 HELP ("pstat_getprocessor() psp_iticksperclktick");
205
206 if (pstat_getprocessor (&p, sizeof(p), 1, 0) != -1)
207 {
208 long c = clk_tck();
209 speed_cycletime = 1.0 / (c * p.psp_iticksperclktick);
210 if (speed_option_verbose)
211 printf ("Using pstat_getprocessor() psp_iticksperclktick %lu and clk_tck %ld for cycle time %.3g\n",
212 (unsigned long) p.psp_iticksperclktick, c,
213 speed_cycletime);
214 return 1;
215 }
216 #endif
217 return 0;
218 }
219
220
221 /* i386 FreeBSD 2.2.8 sysctlbyname machdep.i586_freq is in Hertz.
222 There's no obvious defines available to get this from plain sysctl. */
223 static int
224 freq_sysctlbyname_i586_freq (int help)
225 {
226 #if HAVE_SYSCTLBYNAME
227 unsigned val;
228 size_t size;
229
230 HELP ("sysctlbyname() machdep.i586_freq");
231
232 size = sizeof(val);
233 if (sysctlbyname ("machdep.i586_freq", &val, &size, NULL, 0) == 0
234 && size == sizeof(val))
235 {
236 speed_cycletime = 1.0 / (double) val;
237 if (speed_option_verbose)
238 printf ("Using sysctlbyname() machdep.i586_freq %u for cycle time %.3g\n",
239 val, speed_cycletime);
240 return 1;
241 }
242 #endif
243 return 0;
244 }
245
246
247 /* i368 FreeBSD 3.3 sysctlbyname machdep.tsc_freq is in Hertz.
248 There's no obvious defines to get this from plain sysctl. */
249
250 static int
251 freq_sysctlbyname_tsc_freq (int help)
252 {
253 #if HAVE_SYSCTLBYNAME
254 unsigned val;
255 size_t size;
256
257 HELP ("sysctlbyname() machdep.tsc_freq");
258
259 size = sizeof(val);
260 if (sysctlbyname ("machdep.tsc_freq", &val, &size, NULL, 0) == 0
261 && size == sizeof(val))
262 {
263 speed_cycletime = 1.0 / (double) val;
264 if (speed_option_verbose)
265 printf ("Using sysctlbyname() machdep.tsc_freq %u for cycle time %.3g\n",
266 val, speed_cycletime);
267 return 1;
268 }
269 #endif
270 return 0;
271 }
272
273
274 /* Apple powerpc Darwin 1.3 sysctl hw.cpufrequency is in hertz. For some
275 reason only seems to be available from sysctl(), not sysctlbyname(). */
276
277 static int
278 freq_sysctl_hw_cpufrequency (int help)
279 {
280 #if HAVE_SYSCTL && defined (CTL_HW) && defined (HW_CPU_FREQ)
281 int mib[2];
282 unsigned val;
283 size_t size;
284
285 HELP ("sysctl() hw.cpufrequency");
286
287 mib[0] = CTL_HW;
288 mib[1] = HW_CPU_FREQ;
289 size = sizeof(val);
290 if (sysctl (mib, 2, &val, &size, NULL, 0) == 0)
291 {
292 speed_cycletime = 1.0 / (double) val;
293 if (speed_option_verbose)
294 printf ("Using sysctl() hw.cpufrequency %u for cycle time %.3g\n",
295 val, speed_cycletime);
296 return 1;
297 }
298 #endif
299 return 0;
300 }
301
302
303 /* The following ssyctl hw.model strings have been observed,
304
305 Alpha FreeBSD 4.1: Digital AlphaPC 164LX 599 MHz
306 NetBSD 1.4: Digital AlphaPC 164LX 599 MHz
307 NetBSD 1.6.1: CY7C601 @ 40 MHz, TMS390C602A FPU
308
309 NetBSD 1.4 doesn't seem to have sysctlbyname, so sysctl() is used. */
310
311 static int
312 freq_sysctl_hw_model (int help)
313 {
314 #if HAVE_SYSCTL && defined (CTL_HW) && defined (HW_MODEL)
315 int mib[2];
316 char str[128];
317 unsigned val;
318 size_t size;
319 char *p;
320 int end;
321
322 HELP ("sysctl() hw.model");
323
324 mib[0] = CTL_HW;
325 mib[1] = HW_MODEL;
326 size = sizeof(str);
327 if (sysctl (mib, 2, str, &size, NULL, 0) == 0)
328 {
329 for (p = str; *p != '\0'; p++)
330 {
331 end = 0;
332 if (sscanf (p, "%u MHz%n", &val, &end) == 1 && end != 0)
333 {
334 speed_cycletime = 1e-6 / (double) val;
335 if (speed_option_verbose)
336 printf ("Using sysctl() hw.model %u for cycle time %.3g\n",
337 val, speed_cycletime);
338 return 1;
339 }
340 }
341 }
342 #endif
343 return 0;
344 }
345
346
347 /* /proc/cpuinfo for linux kernel.
348
349 Linux doesn't seem to have any system call to get the CPU frequency, at
350 least not in 2.0.x or 2.2.x, so it's necessary to read /proc/cpuinfo.
351
352 i386 2.0.36 - "bogomips" is the CPU frequency.
353
354 i386 2.2.13 - has both "cpu MHz" and "bogomips", and it's "cpu MHz" which
355 is the frequency.
356
357 alpha 2.2.5 - "cycle frequency [Hz]" seems to be right, "BogoMIPS" is
358 very slightly different.
359
360 alpha 2.2.18pre21 - "cycle frequency [Hz]" is 0 on at least one system,
361 "BogoMIPS" seems near enough.
362
363 powerpc 2.2.19 - "clock" is the frequency, bogomips is something weird
364 */
365
366 static int
367 freq_proc_cpuinfo (int help)
368 {
369 FILE *fp;
370 char buf[128];
371 double val;
372 int ret = 0;
373 int end;
374
375 HELP ("linux kernel /proc/cpuinfo file, cpu MHz or bogomips");
376
377 if ((fp = fopen ("/proc/cpuinfo", "r")) != NULL)
378 {
379 while (fgets (buf, sizeof (buf), fp) != NULL)
380 {
381 if (sscanf (buf, "cycle frequency [Hz] : %lf", &val) == 1
382 && val != 0.0)
383 {
384 speed_cycletime = 1.0 / val;
385 if (speed_option_verbose)
386 printf ("Using /proc/cpuinfo \"cycle frequency\" %.2f for cycle time %.3g\n", val, speed_cycletime);
387 ret = 1;
388 break;
389 }
390 if (sscanf (buf, "cpu MHz : %lf\n", &val) == 1)
391 {
392 speed_cycletime = 1e-6 / val;
393 if (speed_option_verbose)
394 printf ("Using /proc/cpuinfo \"cpu MHz\" %.2f for cycle time %.3g\n", val, speed_cycletime);
395 ret = 1;
396 break;
397 }
398 end = 0;
399 if (sscanf (buf, "clock : %lfMHz\n%n", &val, &end) == 1 && end != 0)
400 {
401 speed_cycletime = 1e-6 / val;
402 if (speed_option_verbose)
403 printf ("Using /proc/cpuinfo \"clock\" %.2f for cycle time %.3g\n", val, speed_cycletime);
404 ret = 1;
405 break;
406 }
407 if (sscanf (buf, "bogomips : %lf\n", &val) == 1
408 || sscanf (buf, "BogoMIPS : %lf\n", &val) == 1)
409 {
410 speed_cycletime = 1e-6 / val;
411 if (speed_option_verbose)
412 printf ("Using /proc/cpuinfo \"bogomips\" %.2f for cycle time %.3g\n", val, speed_cycletime);
413 ret = 1;
414 break;
415 }
416 }
417 fclose (fp);
418 }
419 return ret;
420 }
421
422
423 /* /bin/sysinfo for SunOS 4.
424 Prints a line like: cpu0 is a "75 MHz TI,TMS390Z55" CPU */
425 static int
426 freq_sunos_sysinfo (int help)
427 {
428 int ret = 0;
429 #if HAVE_POPEN
430 FILE *fp;
431 char buf[128];
432 double val;
433 int end;
434
435 HELP ("SunOS /bin/sysinfo program output, cpu0");
436
437 /* Error messages are sent to /dev/null in case /bin/sysinfo doesn't
438 exist. The brackets are necessary for some shells. */
439 if ((fp = popen ("(/bin/sysinfo) 2>/dev/null", "r")) != NULL)
440 {
441 while (fgets (buf, sizeof (buf), fp) != NULL)
442 {
443 end = 0;
444 if (sscanf (buf, " cpu0 is a \"%lf MHz%n", &val, &end) == 1
445 && end != 0)
446 {
447 speed_cycletime = 1e-6 / val;
448 if (speed_option_verbose)
449 printf ("Using /bin/sysinfo \"cpu0 MHz\" %.2f for cycle time %.3g\n", val, speed_cycletime);
450 ret = 1;
451 break;
452 }
453 }
454 pclose (fp);
455 }
456 #endif
457 return ret;
458 }
459
460
461 /* "/etc/hw -r cpu" for SCO OpenUnix 8, printing a line like
462 The speed of the CPU is approximately 450Mhz
463 */
464 static int
465 freq_sco_etchw (int help)
466 {
467 int ret = 0;
468 #if HAVE_POPEN
469 FILE *fp;
470 char buf[128];
471 double val;
472 int end;
473
474 HELP ("SCO /etc/hw program output");
475
476 /* Error messages are sent to /dev/null in case /etc/hw doesn't exist.
477 The brackets are necessary for some shells. */
478 if ((fp = popen ("(/etc/hw -r cpu) 2>/dev/null", "r")) != NULL)
479 {
480 while (fgets (buf, sizeof (buf), fp) != NULL)
481 {
482 end = 0;
483 if (sscanf (buf, " The speed of the CPU is approximately %lfMhz%n",
484 &val, &end) == 1 && end != 0)
485 {
486 speed_cycletime = 1e-6 / val;
487 if (speed_option_verbose)
488 printf ("Using /etc/hw %.2f MHz, for cycle time %.3g\n",
489 val, speed_cycletime);
490 ret = 1;
491 break;
492 }
493 }
494 pclose (fp);
495 }
496 #endif
497 return ret;
498 }
499
500
501 /* attr_get("/hw/cpunum/0",INFO_LBL_DETAIL_INVENT) ic_cpu_info.cpufq for
502 IRIX 6.5. Past versions don't have INFO_LBL_DETAIL_INVENT,
503 invent_cpuinfo_t, or /hw/cpunum/0.
504
505 The same information is available from the "hinv -c processor" command,
506 but it seems better to make a system call where possible. */
507
508 static int
509 freq_attr_get_invent (int help)
510 {
511 int ret = 0;
512 #if HAVE_ATTR_GET && HAVE_INVENT_H && defined (INFO_LBL_DETAIL_INVENT)
513 invent_cpuinfo_t inv;
514 int len, val;
515
516 HELP ("attr_get(\"/hw/cpunum/0\") ic_cpu_info.cpufq");
517
518 len = sizeof (inv);
519 if (attr_get ("/hw/cpunum/0", INFO_LBL_DETAIL_INVENT,
520 (char *) &inv, &len, 0) == 0
521 && len == sizeof (inv)
522 && inv.ic_gen.ig_invclass == INV_PROCESSOR)
523 {
524 val = inv.ic_cpu_info.cpufq;
525 speed_cycletime = 1e-6 / val;
526 if (speed_option_verbose)
527 printf ("Using attr_get(\"/hw/cpunum/0\") ic_cpu_info.cpufq %d MHz for cycle time %.3g\n", val, speed_cycletime);
528 ret = 1;
529 }
530 #endif
531 return ret;
532 }
533
534
535 /* FreeBSD on i386 gives a line like the following at bootup, and which can
536 be read back from /var/run/dmesg.boot.
537
538 CPU: AMD Athlon(tm) Processor (755.29-MHz 686-class CPU)
539 CPU: Pentium 4 (1707.56-MHz 686-class CPU)
540 CPU: i486 DX4 (486-class CPU)
541
542 This is useful on FreeBSD 4.x, where there's no sysctl machdep.tsc_freq
543 or machdep.i586_freq.
544
545 It's better to use /var/run/dmesg.boot than to run /sbin/dmesg, since the
546 latter prints the current system message buffer, which is a limited size
547 and can wrap around if the system is up for a long time. */
548
549 static int
550 freq_bsd_dmesg (int help)
551 {
552 FILE *fp;
553 char buf[256], *p;
554 double val;
555 int ret = 0;
556 int end;
557
558 HELP ("BSD /var/run/dmesg.boot file");
559
560 if ((fp = fopen ("/var/run/dmesg.boot", "r")) != NULL)
561 {
562 while (fgets (buf, sizeof (buf), fp) != NULL)
563 {
564 if (memcmp (buf, "CPU:", 4) == 0)
565 {
566 for (p = buf; *p != '\0'; p++)
567 {
568 end = 0;
569 if (sscanf (p, "(%lf-MHz%n", &val, &end) == 1 && end != 0)
570 {
571 speed_cycletime = 1e-6 / val;
572 if (speed_option_verbose)
573 printf ("Using /var/run/dmesg.boot CPU: %.2f MHz for cycle time %.3g\n", val, speed_cycletime);
574 ret = 1;
575 break;
576 }
577 }
578 }
579 }
580 fclose (fp);
581 }
582 return ret;
583 }
584
585
586 /* "hinv -c processor" for IRIX. The following lines have been seen,
587
588 1 150 MHZ IP20 Processor
589 2 195 MHZ IP27 Processors
590 Processor 0: 500 MHZ IP35
591
592 This information is available from attr_get() on IRIX 6.5 (see above),
593 but on IRIX 6.2 it's not clear where to look, so fall back on
594 parsing. */
595
596 static int
597 freq_irix_hinv (int help)
598 {
599 int ret = 0;
600 #if HAVE_POPEN
601 FILE *fp;
602 char buf[128];
603 double val;
604 int nproc, end;
605
606 HELP ("IRIX \"hinv -c processor\" output");
607
608 /* Error messages are sent to /dev/null in case hinv doesn't exist. The
609 brackets are necessary for some shells. */
610 if ((fp = popen ("(hinv -c processor) 2>/dev/null", "r")) != NULL)
611 {
612 while (fgets (buf, sizeof (buf), fp) != NULL)
613 {
614 end = 0;
615 if (sscanf (buf, "Processor 0: %lf MHZ%n", &val, &end) == 1
616 && end != 0)
617 {
618 found:
619 speed_cycletime = 1e-6 / val;
620 if (speed_option_verbose)
621 printf ("Using hinv -c processor \"%.2f MHZ\" for cycle time %.3g\n", val, speed_cycletime);
622 ret = 1;
623 break;
624 }
625 end = 0;
626 if (sscanf (buf, "%d %lf MHZ%n", &nproc, &val, &end) == 2
627 && end != 0)
628 goto found;
629 }
630 pclose (fp);
631 }
632 #endif
633 return ret;
634 }
635
636
637 /* processor_info() for Solaris. "psrinfo" is the command-line interface to
638 this. "prtconf -vp" gives similar information.
639
640 Apple Darwin has a processor_info, but in an incompatible style. It
641 doesn't have <sys/processor.h>, so test for that. */
642
643 static int
644 freq_processor_info (int help)
645 {
646 #if HAVE_PROCESSOR_INFO && HAVE_SYS_PROCESSOR_H
647 processor_info_t p;
648 int i, n, mhz = 0;
649
650 HELP ("processor_info() pi_clock");
651
652 n = sysconf (_SC_NPROCESSORS_CONF);
653 for (i = 0; i < n; i++)
654 {
655 if (processor_info (i, &p) != 0)
656 continue;
657 if (p.pi_state != P_ONLINE)
658 continue;
659
660 if (mhz != 0 && p.pi_clock != mhz)
661 {
662 fprintf (stderr,
663 "freq_processor_info(): There's more than one CPU and they have different clock speeds\n");
664 return 0;
665 }
666
667 mhz = p.pi_clock;
668 }
669
670 speed_cycletime = 1.0e-6 / (double) mhz;
671
672 if (speed_option_verbose)
673 printf ("Using processor_info() %d mhz for cycle time %.3g\n",
674 mhz, speed_cycletime);
675 return 1;
676
677 #else
678 return 0;
679 #endif
680 }
681
682
683 #if HAVE_SPEED_CYCLECOUNTER && HAVE_GETTIMEOFDAY
684 static double
685 freq_measure_gettimeofday_one (void)
686 {
687 #define call_gettimeofday(t) gettimeofday (&(t), NULL)
688 #define timeval_tv_sec(t) ((t).tv_sec)
689 #define timeval_tv_usec(t) ((t).tv_usec)
690 FREQ_MEASURE_ONE ("gettimeofday", struct timeval,
691 call_gettimeofday, speed_cyclecounter,
692 timeval_tv_sec, timeval_tv_usec);
693 }
694 #endif
695
696 #if HAVE_SPEED_CYCLECOUNTER && HAVE_GETRUSAGE
697 static double
698 freq_measure_getrusage_one (void)
699 {
700 #define call_getrusage(t) getrusage (0, &(t))
701 #define rusage_tv_sec(t) ((t).ru_utime.tv_sec)
702 #define rusage_tv_usec(t) ((t).ru_utime.tv_usec)
703 FREQ_MEASURE_ONE ("getrusage", struct rusage,
704 call_getrusage, speed_cyclecounter,
705 rusage_tv_sec, rusage_tv_usec);
706 }
707 #endif
708
709
710 /* MEASURE_MATCH is how many readings within MEASURE_TOLERANCE of each other
711 are required. This must be at least 2. */
712 #define MEASURE_MAX_ATTEMPTS 20
713 #define MEASURE_TOLERANCE 1.005 /* 0.5% */
714 #define MEASURE_MATCH 3
715
716 double
717 freq_measure (const char *name, double (*one) (void))
718 {
719 double t[MEASURE_MAX_ATTEMPTS];
720 int i, j;
721
722 for (i = 0; i < numberof (t); i++)
723 {
724 t[i] = (*one) ();
725
726 qsort (t, i+1, sizeof(t[0]), (qsort_function_t) double_cmp_ptr);
727 if (speed_option_verbose >= 3)
728 for (j = 0; j <= i; j++)
729 printf (" t[%d] is %.6g\n", j, t[j]);
730
731 for (j = 0; j+MEASURE_MATCH-1 <= i; j++)
732 {
733 if (t[j+MEASURE_MATCH-1] <= t[j] * MEASURE_TOLERANCE)
734 {
735 /* use the average of the range found */
736 return (t[j+MEASURE_MATCH-1] + t[j]) / 2.0;
737 }
738 }
739 }
740 return -1.0;
741 }
742
743 static int
744 freq_measure_getrusage (int help)
745 {
746 #if HAVE_SPEED_CYCLECOUNTER && HAVE_GETRUSAGE
747 double cycletime;
748
749 if (! getrusage_microseconds_p ())
750 return 0;
751 if (! cycles_works_p ())
752 return 0;
753
754 HELP ("cycle counter measured with microsecond getrusage()");
755
756 cycletime = freq_measure ("getrusage", freq_measure_getrusage_one);
757 if (cycletime == -1.0)
758 return 0;
759
760 speed_cycletime = cycletime;
761 if (speed_option_verbose)
762 printf ("Using getrusage() measured cycle counter %.4g (%.2f MHz)\n",
763 speed_cycletime, 1e-6/speed_cycletime);
764 return 1;
765
766 #else
767 return 0;
768 #endif
769 }
770
771 static int
772 freq_measure_gettimeofday (int help)
773 {
774 #if HAVE_SPEED_CYCLECOUNTER && HAVE_GETTIMEOFDAY
775 double cycletime;
776
777 if (! gettimeofday_microseconds_p ())
778 return 0;
779 if (! cycles_works_p ())
780 return 0;
781
782 HELP ("cycle counter measured with microsecond gettimeofday()");
783
784 cycletime = freq_measure ("gettimeofday", freq_measure_gettimeofday_one);
785 if (cycletime == -1.0)
786 return 0;
787
788 speed_cycletime = cycletime;
789 if (speed_option_verbose)
790 printf ("Using gettimeofday() measured cycle counter %.4g (%.2f MHz)\n",
791 speed_cycletime, 1e-6/speed_cycletime);
792 return 1;
793 #else
794 return 0;
795 #endif
796 }
797
798
799 /* Each function returns 1 if it succeeds in setting speed_cycletime, or 0
800 if not.
801
802 In general system call tests are first since they're fast, then file
803 tests, then tests running programs. Necessary exceptions to this rule
804 are noted. The measuring is last since it's time consuming, and rather
805 wasteful of cpu. */
806
807 static int
808 freq_all (int help)
809 {
810 return
811 /* This should be first, so an environment variable can override
812 anything the system gives. */
813 freq_environment (help)
814
815 || freq_attr_get_invent (help)
816 || freq_getsysinfo (help)
817 || freq_pstat_getprocessor (help)
818 || freq_sysctl_hw_model (help)
819 || freq_sysctl_hw_cpufrequency (help)
820 || freq_sysctlbyname_i586_freq (help)
821 || freq_sysctlbyname_tsc_freq (help)
822
823 /* SCO openunix 8 puts a dummy pi_clock==16 in processor_info, so be
824 sure to check /etc/hw before that function. */
825 || freq_sco_etchw (help)
826
827 || freq_processor_info (help)
828 || freq_proc_cpuinfo (help)
829 || freq_bsd_dmesg (help)
830 || freq_irix_hinv (help)
831 || freq_sunos_sysinfo (help)
832 || freq_measure_getrusage (help)
833 || freq_measure_gettimeofday (help);
834 }
835
836
837 void
838 speed_cycletime_init (void)
839 {
840 static int attempted = 0;
841
842 if (attempted)
843 return;
844 attempted = 1;
845
846 if (freq_all (0))
847 return;
848
849 if (speed_option_verbose)
850 printf ("CPU frequency couldn't be determined\n");
851 }
852
853
854 void
855 speed_cycletime_fail (const char *str)
856 {
857 fprintf (stderr, "Measuring with: %s\n", speed_time_string);
858 fprintf (stderr, "%s,\n", str);
859 fprintf (stderr, "but none of the following are available,\n");
860 freq_all (1);
861 abort ();
862 }
863
864 /* speed_time_init leaves speed_cycletime set to either 0.0 or 1.0 when the
865 CPU frequency is unknown. 0.0 is when the time base is in seconds, so
866 that's no good if cycles are wanted. 1.0 is when the time base is in
867 cycles, which conversely is no good if seconds are wanted. */
868 void
869 speed_cycletime_need_cycles (void)
870 {
871 speed_time_init ();
872 if (speed_cycletime == 0.0)
873 speed_cycletime_fail
874 ("Need to know CPU frequency to give times in cycles");
875 }
876 void
877 speed_cycletime_need_seconds (void)
878 {
879 speed_time_init ();
880 if (speed_cycletime == 1.0)
881 speed_cycletime_fail
882 ("Need to know CPU frequency to convert cycles to seconds");
883 }
884