Annotation of OpenXM_contrib/gmp/tune/time.c, Revision 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>