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