Blame tune/time.c

Packit 5c3484
/* Time routines for speed measurements.
Packit 5c3484
Packit 5c3484
Copyright 1999-2004, 2010-2012 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This file is part of the GNU MP Library.
Packit 5c3484
Packit 5c3484
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 5c3484
it under the terms of either:
Packit 5c3484
Packit 5c3484
  * the GNU Lesser General Public License as published by the Free
Packit 5c3484
    Software Foundation; either version 3 of the License, or (at your
Packit 5c3484
    option) any later version.
Packit 5c3484
Packit 5c3484
or
Packit 5c3484
Packit 5c3484
  * the GNU General Public License as published by the Free Software
Packit 5c3484
    Foundation; either version 2 of the License, or (at your option) any
Packit 5c3484
    later version.
Packit 5c3484
Packit 5c3484
or both in parallel, as here.
Packit 5c3484
Packit 5c3484
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 5c3484
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 5c3484
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 5c3484
for more details.
Packit 5c3484
Packit 5c3484
You should have received copies of the GNU General Public License and the
Packit 5c3484
GNU Lesser General Public License along with the GNU MP Library.  If not,
Packit 5c3484
see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Usage:
Packit 5c3484
Packit 5c3484
   The code in this file implements the lowest level of time measuring,
Packit 5c3484
   simple one-time measuring of time between two points.
Packit 5c3484
Packit 5c3484
   void speed_starttime (void)
Packit 5c3484
   double speed_endtime (void)
Packit 5c3484
       Call speed_starttime to start measuring, and then call speed_endtime
Packit 5c3484
       when done.
Packit 5c3484
Packit 5c3484
       speed_endtime returns the time taken, in seconds.  Or if the timebase
Packit 5c3484
       is in CPU cycles and the CPU frequency is unknown then speed_endtime
Packit 5c3484
       returns cycles.  Applications can identify the cycles return by
Packit 5c3484
       checking for speed_cycletime (described below) equal to 1.0.
Packit 5c3484
Packit 5c3484
       If some sort of temporary glitch occurs then speed_endtime returns
Packit 5c3484
       0.0.  Currently this is for various cases where a negative time has
Packit 5c3484
       occurred.  This unfortunately occurs with getrusage on some systems,
Packit 5c3484
       and with the hppa cycle counter on hpux.
Packit 5c3484
Packit 5c3484
   double speed_cycletime
Packit 5c3484
       The time in seconds for each CPU cycle.  For example on a 100 MHz CPU
Packit 5c3484
       this would be 1.0e-8.
Packit 5c3484
Packit 5c3484
       If the CPU frequency is unknown, then speed_cycletime is either 0.0
Packit 5c3484
       or 1.0.  It's 0.0 when speed_endtime is returning seconds, or it's
Packit 5c3484
       1.0 when speed_endtime is returning cycles.
Packit 5c3484
Packit 5c3484
       It may be noted that "speed_endtime() / speed_cycletime" gives a
Packit 5c3484
       measured time in cycles, irrespective of whether speed_endtime is
Packit 5c3484
       returning cycles or seconds.  (Assuming cycles can be had, ie. it's
Packit 5c3484
       either cycles already or the cpu frequency is known.  See also
Packit 5c3484
       speed_cycletime_need_cycles below.)
Packit 5c3484
Packit 5c3484
   double speed_unittime
Packit 5c3484
       The unit of time measurement accuracy for the timing method in use.
Packit 5c3484
       This is in seconds or cycles, as per speed_endtime.
Packit 5c3484
Packit 5c3484
   char speed_time_string[]
Packit 5c3484
       A null-terminated string describing the time method in use.
Packit 5c3484
Packit 5c3484
   void speed_time_init (void)
Packit 5c3484
       Initialize time measuring.  speed_starttime() does this
Packit 5c3484
       automatically, so it's only needed if an application wants to inspect
Packit 5c3484
       the above global variables before making a measurement.
Packit 5c3484
Packit 5c3484
   int speed_precision
Packit 5c3484
       The intended accuracy of time measurements.  speed_measure() in
Packit 5c3484
       common.c for instance runs target routines with enough repetitions so
Packit 5c3484
       it takes at least "speed_unittime * speed_precision" (this expression
Packit 5c3484
       works for both cycles or seconds from speed_endtime).
Packit 5c3484
Packit 5c3484
       A program can provide an option so the user to set speed_precision.
Packit 5c3484
       If speed_precision is zero when speed_time_init or speed_starttime
Packit 5c3484
       first run then it gets a default based on the measuring method
Packit 5c3484
       chosen.  (More precision for higher accuracy methods.)
Packit 5c3484
Packit 5c3484
   void speed_cycletime_need_seconds (void)
Packit 5c3484
       Call this to demand that speed_endtime will return seconds, and not
Packit 5c3484
       cycles.  If only cycles are available then an error is printed and
Packit 5c3484
       the program exits.
Packit 5c3484
Packit 5c3484
   void speed_cycletime_need_cycles (void)
Packit 5c3484
       Call this to demand that speed_cycletime is non-zero, so that
Packit 5c3484
       "speed_endtime() / speed_cycletime" will give times in cycles.
Packit 5c3484
Packit 5c3484
Packit 5c3484
Packit 5c3484
   Notes:
Packit 5c3484
Packit 5c3484
   Various combinations of cycle counter, read_real_time(), getrusage(),
Packit 5c3484
   gettimeofday() and times() can arise, according to which are available
Packit 5c3484
   and their precision.
Packit 5c3484
Packit 5c3484
Packit 5c3484
   Allowing speed_endtime() to return either seconds or cycles is only a
Packit 5c3484
   slight complication and makes it possible for the speed program to do
Packit 5c3484
   some sensible things without demanding the CPU frequency.  If seconds are
Packit 5c3484
   being measured then it can always print seconds, and if cycles are being
Packit 5c3484
   measured then it can always print them without needing to know how long
Packit 5c3484
   they are.  Also the tune program doesn't care at all what the units are.
Packit 5c3484
Packit 5c3484
   GMP_CPU_FREQUENCY can always be set when the automated methods in freq.c
Packit 5c3484
   fail.  This will be needed if times in seconds are wanted but a cycle
Packit 5c3484
   counter is being used, or if times in cycles are wanted but getrusage or
Packit 5c3484
   another seconds based timer is in use.
Packit 5c3484
Packit 5c3484
   If the measuring method uses a cycle counter but supplements it with
Packit 5c3484
   getrusage or the like, then knowing the CPU frequency is mandatory since
Packit 5c3484
   the code compares values from the two.
Packit 5c3484
Packit 5c3484
Packit 5c3484
   Not done:
Packit 5c3484
Packit 5c3484
   Solaris gethrtime() seems no more than a slow way to access the Sparc V9
Packit 5c3484
   cycle counter.  gethrvtime() seems to be relevant only to light weight
Packit 5c3484
   processes, it doesn't for instance give nanosecond virtual time.  So
Packit 5c3484
   neither of these are used.
Packit 5c3484
Packit 5c3484
Packit 5c3484
   Bugs:
Packit 5c3484
Packit 5c3484
   getrusage_microseconds_p is fundamentally flawed, getrusage and
Packit 5c3484
   gettimeofday can have resolutions other than clock ticks or microseconds,
Packit 5c3484
   for instance IRIX 5 has a tick of 10 ms but a getrusage of 1 ms.
Packit 5c3484
Packit 5c3484
Packit 5c3484
   Enhancements:
Packit 5c3484
Packit 5c3484
   The SGI hardware counter has 64 bits on some machines, which could be
Packit 5c3484
   used when available.  But perhaps 32 bits is enough range, and then rely
Packit 5c3484
   on the getrusage supplement.
Packit 5c3484
Packit 5c3484
   Maybe getrusage (or times) should be used as a supplement for any
Packit 5c3484
   wall-clock measuring method.  Currently a wall clock with a good range
Packit 5c3484
   (eg. a 64-bit cycle counter) is used without a supplement.
Packit 5c3484
Packit 5c3484
   On PowerPC the timebase registers could be used, but would have to do
Packit 5c3484
   something to find out the speed.  On 6xx chips it's normally 1/4 bus
Packit 5c3484
   speed, on 4xx chips it's either that or an external clock.  Measuring
Packit 5c3484
   against gettimeofday might be ok.  */
Packit 5c3484
Packit 5c3484
Packit 5c3484
#include "config.h"
Packit 5c3484
Packit 5c3484
#include <errno.h>
Packit 5c3484
#include <setjmp.h>
Packit 5c3484
#include <signal.h>
Packit 5c3484
#include <stddef.h>
Packit 5c3484
#include <stdio.h>
Packit 5c3484
#include <string.h>
Packit 5c3484
#include <stdlib.h> /* for getenv() */
Packit 5c3484
Packit 5c3484
#if HAVE_FCNTL_H
Packit 5c3484
#include <fcntl.h>  /* for open() */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_STDINT_H
Packit 5c3484
#include <stdint.h> /* for uint64_t */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_UNISTD_H
Packit 5c3484
#include <unistd.h> /* for sysconf() */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#include <sys/types.h>
Packit 5c3484
Packit 5c3484
#if TIME_WITH_SYS_TIME
Packit 5c3484
# include <sys/time.h>  /* for struct timeval */
Packit 5c3484
# include <time.h>
Packit 5c3484
#else
Packit 5c3484
# if HAVE_SYS_TIME_H
Packit 5c3484
#  include <sys/time.h>
Packit 5c3484
# else
Packit 5c3484
#  include <time.h>
Packit 5c3484
# endif
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_SYS_MMAN_H
Packit 5c3484
#include <sys/mman.h>      /* for mmap() */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_SYS_RESOURCE_H
Packit 5c3484
#include <sys/resource.h>  /* for struct rusage */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_SYS_SYSSGI_H
Packit 5c3484
#include <sys/syssgi.h>    /* for syssgi() */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_SYS_SYSTEMCFG_H
Packit 5c3484
#include <sys/systemcfg.h> /* for RTC_POWER on AIX */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_SYS_TIMES_H
Packit 5c3484
#include <sys/times.h>  /* for times() and struct tms */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
Packit 5c3484
#include "speed.h"
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* strerror is only used for some stuff on newish systems, no need to have a
Packit 5c3484
   proper replacement */
Packit 5c3484
#if ! HAVE_STRERROR
Packit 5c3484
#define strerror(n)  "<strerror not available>"
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
Packit 5c3484
char    speed_time_string[256];
Packit 5c3484
int     speed_precision = 0;
Packit 5c3484
double  speed_unittime;
Packit 5c3484
double  speed_cycletime = 0.0;
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* don't rely on "unsigned" to "double" conversion, it's broken in SunOS 4
Packit 5c3484
   native cc */
Packit 5c3484
#define M_2POWU   (((double) INT_MAX + 1.0) * 2.0)
Packit 5c3484
Packit 5c3484
#define M_2POW32  4294967296.0
Packit 5c3484
#define M_2POW64  (M_2POW32 * M_2POW32)
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Conditionals for the time functions available are done with normal C
Packit 5c3484
   code, which is a lot easier than wildly nested preprocessor directives.
Packit 5c3484
Packit 5c3484
   The choice of what to use is partly made at run-time, according to
Packit 5c3484
   whether the cycle counter works and the measured accuracy of getrusage
Packit 5c3484
   and gettimeofday.
Packit 5c3484
Packit 5c3484
   A routine that's not available won't be getting called, but is an abort()
Packit 5c3484
   to be sure it isn't called mistakenly.
Packit 5c3484
Packit 5c3484
   It can be assumed that if a function exists then its data type will, but
Packit 5c3484
   if the function doesn't then the data type might or might not exist, so
Packit 5c3484
   the type can't be used unconditionally.  The "struct_rusage" etc macros
Packit 5c3484
   provide dummies when the respective function doesn't exist. */
Packit 5c3484
Packit 5c3484
Packit 5c3484
#if HAVE_SPEED_CYCLECOUNTER
Packit 5c3484
static const int have_cycles = HAVE_SPEED_CYCLECOUNTER;
Packit 5c3484
#else
Packit 5c3484
static const int have_cycles = 0;
Packit 5c3484
#define speed_cyclecounter(p)  ASSERT_FAIL (speed_cyclecounter not available)
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
/* "stck" returns ticks since 1 Jan 1900 00:00 GMT, where each tick is 2^-12
Packit 5c3484
   microseconds.  Same #ifdefs here as in longlong.h.  */
Packit 5c3484
#if defined (__GNUC__) && ! defined (NO_ASM)                            \
Packit 5c3484
  && (defined (__i370__) || defined (__s390__) || defined (__mvs__))
Packit 5c3484
static const int  have_stck = 1;
Packit 5c3484
static const int  use_stck = 1;  /* always use when available */
Packit 5c3484
typedef uint64_t  stck_t; /* gcc for s390 is quite new, always has uint64_t */
Packit 5c3484
#define STCK(timestamp)                 \
Packit 5c3484
  do {                                  \
Packit 5c3484
    asm ("stck %0" : "=Q" (timestamp)); \
Packit 5c3484
  } while (0)
Packit 5c3484
#else
Packit 5c3484
static const int  have_stck = 0;
Packit 5c3484
static const int  use_stck = 0;
Packit 5c3484
typedef unsigned long  stck_t;   /* dummy */
Packit 5c3484
#define STCK(timestamp)  ASSERT_FAIL (stck instruction not available)
Packit 5c3484
#endif
Packit 5c3484
#define STCK_PERIOD      (1.0 / 4096e6)   /* 2^-12 microseconds */
Packit 5c3484
Packit 5c3484
/* mftb
Packit 5c3484
   Enhancement: On 64-bit chips mftb gives a 64-bit value, no need for mftbu
Packit 5c3484
   and a loop (see powerpc64.asm).  */
Packit 5c3484
#if HAVE_HOST_CPU_FAMILY_powerpc
Packit 5c3484
static const int  have_mftb = 1;
Packit 5c3484
#if defined (__GNUC__) && ! defined (NO_ASM)
Packit 5c3484
#define MFTB(a)                         \
Packit 5c3484
  do {                                  \
Packit 5c3484
    unsigned  __h1, __l, __h2;          \
Packit 5c3484
    do {                                \
Packit 5c3484
      asm volatile ("mftbu %0\n"        \
Packit 5c3484
		    "mftb  %1\n"        \
Packit 5c3484
		    "mftbu %2"          \
Packit 5c3484
		    : "=r" (__h1),      \
Packit 5c3484
		      "=r" (__l),       \
Packit 5c3484
		      "=r" (__h2));     \
Packit 5c3484
    } while (__h1 != __h2);             \
Packit 5c3484
    a[0] = __l;                         \
Packit 5c3484
    a[1] = __h1;                        \
Packit 5c3484
  } while (0)
Packit 5c3484
#else
Packit 5c3484
#define MFTB(a)   mftb_function (a)
Packit 5c3484
#endif
Packit 5c3484
#else /* ! powerpc */
Packit 5c3484
static const int  have_mftb = 0;
Packit 5c3484
#define MFTB(a)                         \
Packit 5c3484
  do {                                  \
Packit 5c3484
    a[0] = 0;                           \
Packit 5c3484
    a[1] = 0;                           \
Packit 5c3484
    ASSERT_FAIL (mftb not available);   \
Packit 5c3484
  } while (0)
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
/* Unicos 10.X has syssgi(), but not mmap(). */
Packit 5c3484
#if HAVE_SYSSGI && HAVE_MMAP
Packit 5c3484
static const int  have_sgi = 1;
Packit 5c3484
#else
Packit 5c3484
static const int  have_sgi = 0;
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_READ_REAL_TIME
Packit 5c3484
static const int have_rrt = 1;
Packit 5c3484
#else
Packit 5c3484
static const int have_rrt = 0;
Packit 5c3484
#define read_real_time(t,s)     ASSERT_FAIL (read_real_time not available)
Packit 5c3484
#define time_base_to_time(t,s)  ASSERT_FAIL (time_base_to_time not available)
Packit 5c3484
#define RTC_POWER     1
Packit 5c3484
#define RTC_POWER_PC  2
Packit 5c3484
#define timebasestruct_t   struct timebasestruct_dummy
Packit 5c3484
struct timebasestruct_dummy {
Packit 5c3484
  int             flag;
Packit 5c3484
  unsigned int    tb_high;
Packit 5c3484
  unsigned int    tb_low;
Packit 5c3484
};
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_CLOCK_GETTIME
Packit 5c3484
static const int have_cgt = 1;
Packit 5c3484
#define struct_timespec  struct timespec
Packit 5c3484
#else
Packit 5c3484
static const int have_cgt = 0;
Packit 5c3484
#define struct_timespec       struct timespec_dummy
Packit 5c3484
#define clock_gettime(id,ts)  (ASSERT_FAIL (clock_gettime not available), -1)
Packit 5c3484
#define clock_getres(id,ts)   (ASSERT_FAIL (clock_getres not available), -1)
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_GETRUSAGE
Packit 5c3484
static const int have_grus = 1;
Packit 5c3484
#define struct_rusage   struct rusage
Packit 5c3484
#else
Packit 5c3484
static const int have_grus = 0;
Packit 5c3484
#define getrusage(n,ru)  ASSERT_FAIL (getrusage not available)
Packit 5c3484
#define struct_rusage    struct rusage_dummy
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_GETTIMEOFDAY
Packit 5c3484
static const int have_gtod = 1;
Packit 5c3484
#define struct_timeval   struct timeval
Packit 5c3484
#else
Packit 5c3484
static const int have_gtod = 0;
Packit 5c3484
#define gettimeofday(tv,tz)  ASSERT_FAIL (gettimeofday not available)
Packit 5c3484
#define struct_timeval   struct timeval_dummy
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_TIMES
Packit 5c3484
static const int have_times = 1;
Packit 5c3484
#define struct_tms   struct tms
Packit 5c3484
#else
Packit 5c3484
static const int have_times = 0;
Packit 5c3484
#define times(tms)   ASSERT_FAIL (times not available)
Packit 5c3484
#define struct_tms   struct tms_dummy
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
struct tms_dummy {
Packit 5c3484
  long  tms_utime;
Packit 5c3484
};
Packit 5c3484
struct timeval_dummy {
Packit 5c3484
  long  tv_sec;
Packit 5c3484
  long  tv_usec;
Packit 5c3484
};
Packit 5c3484
struct rusage_dummy {
Packit 5c3484
  struct_timeval ru_utime;
Packit 5c3484
};
Packit 5c3484
struct timespec_dummy {
Packit 5c3484
  long  tv_sec;
Packit 5c3484
  long  tv_nsec;
Packit 5c3484
};
Packit 5c3484
Packit 5c3484
static int  use_cycles;
Packit 5c3484
static int  use_mftb;
Packit 5c3484
static int  use_sgi;
Packit 5c3484
static int  use_rrt;
Packit 5c3484
static int  use_cgt;
Packit 5c3484
static int  use_gtod;
Packit 5c3484
static int  use_grus;
Packit 5c3484
static int  use_times;
Packit 5c3484
static int  use_tick_boundary;
Packit 5c3484
Packit 5c3484
static unsigned         start_cycles[2];
Packit 5c3484
static stck_t           start_stck;
Packit 5c3484
static unsigned         start_mftb[2];
Packit 5c3484
static unsigned         start_sgi;
Packit 5c3484
static timebasestruct_t start_rrt;
Packit 5c3484
static struct_timespec  start_cgt;
Packit 5c3484
static struct_rusage    start_grus;
Packit 5c3484
static struct_timeval   start_gtod;
Packit 5c3484
static struct_tms       start_times;
Packit 5c3484
Packit 5c3484
static double  cycles_limit = 1e100;
Packit 5c3484
static double  mftb_unittime;
Packit 5c3484
static double  sgi_unittime;
Packit 5c3484
static double  cgt_unittime;
Packit 5c3484
static double  grus_unittime;
Packit 5c3484
static double  gtod_unittime;
Packit 5c3484
static double  times_unittime;
Packit 5c3484
Packit 5c3484
/* for RTC_POWER format, ie. seconds and nanoseconds */
Packit 5c3484
#define TIMEBASESTRUCT_SECS(t)  ((t)->tb_high + (t)->tb_low * 1e-9)
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Return a string representing a time in seconds, nicely formatted.
Packit 5c3484
   Eg. "10.25ms".  */
Packit 5c3484
char *
Packit 5c3484
unittime_string (double t)
Packit 5c3484
{
Packit 5c3484
  static char  buf[128];
Packit 5c3484
Packit 5c3484
  const char  *unit;
Packit 5c3484
  int         prec;
Packit 5c3484
Packit 5c3484
  /* choose units and scale */
Packit 5c3484
  if (t < 1e-6)
Packit 5c3484
    t *= 1e9, unit = "ns";
Packit 5c3484
  else if (t < 1e-3)
Packit 5c3484
    t *= 1e6, unit = "us";
Packit 5c3484
  else if (t < 1.0)
Packit 5c3484
    t *= 1e3, unit = "ms";
Packit 5c3484
  else
Packit 5c3484
    unit = "s";
Packit 5c3484
Packit 5c3484
  /* want 4 significant figures */
Packit 5c3484
  if (t < 1.0)
Packit 5c3484
    prec = 4;
Packit 5c3484
  else if (t < 10.0)
Packit 5c3484
    prec = 3;
Packit 5c3484
  else if (t < 100.0)
Packit 5c3484
    prec = 2;
Packit 5c3484
  else
Packit 5c3484
    prec = 1;
Packit 5c3484
Packit 5c3484
  sprintf (buf, "%.*f%s", prec, t, unit);
Packit 5c3484
  return buf;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
static jmp_buf  cycles_works_buf;
Packit 5c3484
Packit 5c3484
static RETSIGTYPE
Packit 5c3484
cycles_works_handler (int sig)
Packit 5c3484
{
Packit 5c3484
  longjmp (cycles_works_buf, 1);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
cycles_works_p (void)
Packit 5c3484
{
Packit 5c3484
  static int  result = -1;
Packit 5c3484
Packit 5c3484
  if (result != -1)
Packit 5c3484
    goto done;
Packit 5c3484
Packit 5c3484
  /* FIXME: On linux, the cycle counter is not saved and restored over
Packit 5c3484
   * context switches, making it almost useless for precise cputime
Packit 5c3484
   * measurements. When available, it's better to use clock_gettime,
Packit 5c3484
   * which seems to have reasonable accuracy (tested on x86_32,
Packit 5c3484
   * linux-2.6.26, glibc-2.7). However, there are also some linux
Packit 5c3484
   * systems where clock_gettime is broken in one way or the other,
Packit 5c3484
   * like CLOCK_PROCESS_CPUTIME_ID not implemented (easy case) or
Packit 5c3484
   * kind-of implemented but broken (needs code to detect that), and
Packit 5c3484
   * on those systems a wall-clock cycle counter is the least bad
Packit 5c3484
   * fallback.
Packit 5c3484
   *
Packit 5c3484
   * So we need some code to disable the cycle counter on some but not
Packit 5c3484
   * all linux systems. */
Packit 5c3484
#ifdef SIGILL
Packit 5c3484
  {
Packit 5c3484
    RETSIGTYPE (*old_handler) (int);
Packit 5c3484
    unsigned  cycles[2];
Packit 5c3484
Packit 5c3484
    old_handler = signal (SIGILL, cycles_works_handler);
Packit 5c3484
    if (old_handler == SIG_ERR)
Packit 5c3484
      {
Packit 5c3484
	if (speed_option_verbose)
Packit 5c3484
	  printf ("cycles_works_p(): SIGILL not supported, assuming speed_cyclecounter() works\n");
Packit 5c3484
	goto yes;
Packit 5c3484
      }
Packit 5c3484
    if (setjmp (cycles_works_buf))
Packit 5c3484
      {
Packit 5c3484
	if (speed_option_verbose)
Packit 5c3484
	  printf ("cycles_works_p(): SIGILL during speed_cyclecounter(), so doesn't work\n");
Packit 5c3484
	result = 0;
Packit 5c3484
	goto done;
Packit 5c3484
      }
Packit 5c3484
    speed_cyclecounter (cycles);
Packit 5c3484
    signal (SIGILL, old_handler);
Packit 5c3484
    if (speed_option_verbose)
Packit 5c3484
      printf ("cycles_works_p(): speed_cyclecounter() works\n");
Packit 5c3484
  }
Packit 5c3484
#else
Packit 5c3484
Packit 5c3484
  if (speed_option_verbose)
Packit 5c3484
    printf ("cycles_works_p(): SIGILL not defined, assuming speed_cyclecounter() works\n");
Packit 5c3484
  goto yes;
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
 yes:
Packit 5c3484
  result = 1;
Packit 5c3484
Packit 5c3484
 done:
Packit 5c3484
  return result;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* The number of clock ticks per second, but looking at sysconf rather than
Packit 5c3484
   just CLK_TCK, where possible.  */
Packit 5c3484
long
Packit 5c3484
clk_tck (void)
Packit 5c3484
{
Packit 5c3484
  static long  result = -1L;
Packit 5c3484
  if (result != -1L)
Packit 5c3484
    return result;
Packit 5c3484
Packit 5c3484
#if HAVE_SYSCONF
Packit 5c3484
  result = sysconf (_SC_CLK_TCK);
Packit 5c3484
  if (result != -1L)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("sysconf(_SC_CLK_TCK) is %ld per second\n", result);
Packit 5c3484
      return result;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  fprintf (stderr,
Packit 5c3484
	   "sysconf(_SC_CLK_TCK) not working, using CLK_TCK instead\n");
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#ifdef CLK_TCK
Packit 5c3484
  result = CLK_TCK;
Packit 5c3484
  if (speed_option_verbose)
Packit 5c3484
    printf ("CLK_TCK is %ld per second\n", result);
Packit 5c3484
  return result;
Packit 5c3484
#else
Packit 5c3484
  fprintf (stderr, "CLK_TCK not defined, cannot continue\n");
Packit 5c3484
  abort ();
Packit 5c3484
#endif
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* If two times can be observed less than half a clock tick apart, then
Packit 5c3484
   assume "get" is microsecond accurate.
Packit 5c3484
Packit 5c3484
   Two times only 1 microsecond apart are not believed, since some kernels
Packit 5c3484
   take it upon themselves to ensure gettimeofday doesn't return the same
Packit 5c3484
   value twice, for the benefit of applications using it for a timestamp.
Packit 5c3484
   This is obviously very stupid given the speed of CPUs these days.
Packit 5c3484
Packit 5c3484
   Making "reps" many calls to noop_1() is designed to waste some CPU, with
Packit 5c3484
   a view to getting measurements 2 microseconds (or more) apart.  "reps" is
Packit 5c3484
   increased progressively until such a period is seen.
Packit 5c3484
Packit 5c3484
   The outer loop "attempts" are just to allow for any random nonsense or
Packit 5c3484
   system load upsetting the measurements (ie. making two successive calls
Packit 5c3484
   to "get" come out as a longer interval than normal).
Packit 5c3484
Packit 5c3484
   Bugs:
Packit 5c3484
Packit 5c3484
   The assumption that any interval less than a half tick implies
Packit 5c3484
   microsecond resolution is obviously fairly rash, the true resolution
Packit 5c3484
   could be anything between a microsecond and that half tick.  Perhaps
Packit 5c3484
   something special would have to be done on a system where this is the
Packit 5c3484
   case, since there's no obvious reliable way to detect it
Packit 5c3484
   automatically.  */
Packit 5c3484
Packit 5c3484
#define MICROSECONDS_P(name, type, get, sec, usec)                      \
Packit 5c3484
  {                                                                     \
Packit 5c3484
    static int  result = -1;                                            \
Packit 5c3484
    type      st, et;                                                   \
Packit 5c3484
    long      dt, half_tick;                                            \
Packit 5c3484
    unsigned  attempt, reps, i, j;                                      \
Packit 5c3484
									\
Packit 5c3484
    if (result != -1)                                                   \
Packit 5c3484
      return result;                                                    \
Packit 5c3484
									\
Packit 5c3484
    result = 0;                                                         \
Packit 5c3484
    half_tick = (1000000L / clk_tck ()) / 2;                            \
Packit 5c3484
									\
Packit 5c3484
    for (attempt = 0; attempt < 5; attempt++)                           \
Packit 5c3484
      {                                                                 \
Packit 5c3484
	reps = 0;                                                       \
Packit 5c3484
	for (;;)                                                        \
Packit 5c3484
	  {                                                             \
Packit 5c3484
	    get (st);                                                   \
Packit 5c3484
	    for (i = 0; i < reps; i++)                                  \
Packit 5c3484
	      for (j = 0; j < 100; j++)                                 \
Packit 5c3484
		noop_1 (CNST_LIMB(0));                                  \
Packit 5c3484
	    get (et);                                                   \
Packit 5c3484
									\
Packit 5c3484
	    dt = (sec(et)-sec(st))*1000000L + usec(et)-usec(st);        \
Packit 5c3484
									\
Packit 5c3484
	    if (speed_option_verbose >= 2)                              \
Packit 5c3484
	      printf ("%s attempt=%u, reps=%u, dt=%ld\n",               \
Packit 5c3484
		      name, attempt, reps, dt);                         \
Packit 5c3484
									\
Packit 5c3484
	    if (dt >= 2)                                                \
Packit 5c3484
	      break;                                                    \
Packit 5c3484
									\
Packit 5c3484
	    reps = (reps == 0 ? 1 : 2*reps);                            \
Packit 5c3484
	    if (reps == 0)                                              \
Packit 5c3484
	      break;  /* uint overflow, not normal */                   \
Packit 5c3484
	  }                                                             \
Packit 5c3484
									\
Packit 5c3484
	if (dt < half_tick)                                             \
Packit 5c3484
	  {                                                             \
Packit 5c3484
	    result = 1;                                                 \
Packit 5c3484
	    break;                                                      \
Packit 5c3484
	  }                                                             \
Packit 5c3484
      }                                                                 \
Packit 5c3484
									\
Packit 5c3484
    if (speed_option_verbose)                                           \
Packit 5c3484
      {                                                                 \
Packit 5c3484
	if (result)                                                     \
Packit 5c3484
	  printf ("%s is microsecond accurate\n", name);                \
Packit 5c3484
	else                                                            \
Packit 5c3484
	  printf ("%s is only %s clock tick accurate\n",                \
Packit 5c3484
		  name, unittime_string (1.0/clk_tck()));               \
Packit 5c3484
      }                                                                 \
Packit 5c3484
    return result;                                                      \
Packit 5c3484
  }
Packit 5c3484
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
gettimeofday_microseconds_p (void)
Packit 5c3484
{
Packit 5c3484
#define call_gettimeofday(t)   gettimeofday (&(t), NULL)
Packit 5c3484
#define timeval_tv_sec(t)      ((t).tv_sec)
Packit 5c3484
#define timeval_tv_usec(t)     ((t).tv_usec)
Packit 5c3484
  MICROSECONDS_P ("gettimeofday", struct_timeval,
Packit 5c3484
		  call_gettimeofday, timeval_tv_sec, timeval_tv_usec);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
getrusage_microseconds_p (void)
Packit 5c3484
{
Packit 5c3484
#define call_getrusage(t)   getrusage (0, &(t))
Packit 5c3484
#define rusage_tv_sec(t)    ((t).ru_utime.tv_sec)
Packit 5c3484
#define rusage_tv_usec(t)   ((t).ru_utime.tv_usec)
Packit 5c3484
  MICROSECONDS_P ("getrusage", struct_rusage,
Packit 5c3484
		  call_getrusage, rusage_tv_sec, rusage_tv_usec);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Test whether getrusage goes backwards, return non-zero if it does
Packit 5c3484
   (suggesting it's flawed).
Packit 5c3484
Packit 5c3484
   On a macintosh m68040-unknown-netbsd1.4.1 getrusage looks like it's
Packit 5c3484
   microsecond accurate, but has been seen remaining unchanged after many
Packit 5c3484
   microseconds have elapsed.  It also regularly goes backwards by 1000 to
Packit 5c3484
   5000 usecs, this has been seen after between 500 and 4000 attempts taking
Packit 5c3484
   perhaps 0.03 seconds.  We consider this too broken for good measuring.
Packit 5c3484
   We used to have configure pretend getrusage didn't exist on this system,
Packit 5c3484
   but a runtime test should be more reliable, since we imagine the problem
Packit 5c3484
   is not confined to just this exact system tuple.  */
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
getrusage_backwards_p (void)
Packit 5c3484
{
Packit 5c3484
  static int result = -1;
Packit 5c3484
  struct rusage  start, prev, next;
Packit 5c3484
  long  d;
Packit 5c3484
  int   i;
Packit 5c3484
Packit 5c3484
  if (result != -1)
Packit 5c3484
    return result;
Packit 5c3484
Packit 5c3484
  getrusage (0, &start;;
Packit 5c3484
  memcpy (&next, &start, sizeof (next));
Packit 5c3484
Packit 5c3484
  result = 0;
Packit 5c3484
  i = 0;
Packit 5c3484
  for (;;)
Packit 5c3484
    {
Packit 5c3484
      memcpy (&prev, &next, sizeof (prev));
Packit 5c3484
      getrusage (0, &next;;
Packit 5c3484
Packit 5c3484
      if (next.ru_utime.tv_sec < prev.ru_utime.tv_sec
Packit 5c3484
	  || (next.ru_utime.tv_sec == prev.ru_utime.tv_sec
Packit 5c3484
	      && next.ru_utime.tv_usec < prev.ru_utime.tv_usec))
Packit 5c3484
	{
Packit 5c3484
	  if (speed_option_verbose)
Packit 5c3484
	    printf ("getrusage went backwards (attempt %d: %ld.%06ld -> %ld.%06ld)\n",
Packit 5c3484
		    i,
Packit 5c3484
		    (long) prev.ru_utime.tv_sec, (long) prev.ru_utime.tv_usec,
Packit 5c3484
		    (long) next.ru_utime.tv_sec, (long) next.ru_utime.tv_usec);
Packit 5c3484
	  result = 1;
Packit 5c3484
	  break;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* minimum 1000 attempts, then stop after either 0.1 seconds or 50000
Packit 5c3484
	 attempts, whichever comes first */
Packit 5c3484
      d = 1000000 * (next.ru_utime.tv_sec - start.ru_utime.tv_sec)
Packit 5c3484
	+ (next.ru_utime.tv_usec - start.ru_utime.tv_usec);
Packit 5c3484
      i++;
Packit 5c3484
      if (i > 50000 || (i > 1000 && d > 100000))
Packit 5c3484
	break;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  return result;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* CLOCK_PROCESS_CPUTIME_ID looks like it's going to be in a future version
Packit 5c3484
   of glibc (some time post 2.2).
Packit 5c3484
Packit 5c3484
   CLOCK_VIRTUAL is process time, available in BSD systems (though sometimes
Packit 5c3484
   defined, but returning -1 for an error).  */
Packit 5c3484
Packit 5c3484
#ifdef CLOCK_PROCESS_CPUTIME_ID
Packit 5c3484
# define CGT_ID        CLOCK_PROCESS_CPUTIME_ID
Packit 5c3484
#else
Packit 5c3484
# ifdef CLOCK_VIRTUAL
Packit 5c3484
#  define CGT_ID       CLOCK_VIRTUAL
Packit 5c3484
# endif
Packit 5c3484
#endif
Packit 5c3484
#ifdef CGT_ID
Packit 5c3484
const int  have_cgt_id = 1;
Packit 5c3484
#else
Packit 5c3484
const int  have_cgt_id = 0;
Packit 5c3484
# define CGT_ID       (ASSERT_FAIL (CGT_ID not determined), -1)
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#define CGT_DELAY_COUNT 1000
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
cgt_works_p (void)
Packit 5c3484
{
Packit 5c3484
  static int  result = -1;
Packit 5c3484
  struct_timespec  unit;
Packit 5c3484
Packit 5c3484
  if (! have_cgt)
Packit 5c3484
    return 0;
Packit 5c3484
Packit 5c3484
  if (! have_cgt_id)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("clock_gettime don't know what ID to use\n");
Packit 5c3484
      result = 0;
Packit 5c3484
      return result;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (result != -1)
Packit 5c3484
    return result;
Packit 5c3484
Packit 5c3484
  /* trial run to see if it works */
Packit 5c3484
  if (clock_gettime (CGT_ID, &unit) != 0)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("clock_gettime id=%d error: %s\n", CGT_ID, strerror (errno));
Packit 5c3484
      result = 0;
Packit 5c3484
      return result;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* get the resolution */
Packit 5c3484
  if (clock_getres (CGT_ID, &unit) != 0)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("clock_getres id=%d error: %s\n", CGT_ID, strerror (errno));
Packit 5c3484
      result = 0;
Packit 5c3484
      return result;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  cgt_unittime = unit.tv_sec + unit.tv_nsec * 1e-9;
Packit 5c3484
  if (speed_option_verbose)
Packit 5c3484
    printf ("clock_gettime is %s accurate\n", unittime_string (cgt_unittime));
Packit 5c3484
Packit 5c3484
  if (cgt_unittime < 10e-9)
Packit 5c3484
    {
Packit 5c3484
      /* Do we believe this? */
Packit 5c3484
      struct timespec start, end;
Packit 5c3484
      static volatile int counter;
Packit 5c3484
      double duration;
Packit 5c3484
      if (clock_gettime (CGT_ID, &start))
Packit 5c3484
	{
Packit 5c3484
	  if (speed_option_verbose)
Packit 5c3484
	    printf ("clock_gettime id=%d error: %s\n", CGT_ID, strerror (errno));
Packit 5c3484
	  result = 0;
Packit 5c3484
	  return result;
Packit 5c3484
	}
Packit 5c3484
      /* Loop of at least 1000 memory accesses, ought to take at
Packit 5c3484
	 least 100 ns*/
Packit 5c3484
      for (counter = 0; counter < CGT_DELAY_COUNT; counter++)
Packit 5c3484
	;
Packit 5c3484
      if (clock_gettime (CGT_ID, &end))
Packit 5c3484
	{
Packit 5c3484
	  if (speed_option_verbose)
Packit 5c3484
	    printf ("clock_gettime id=%d error: %s\n", CGT_ID, strerror (errno));
Packit 5c3484
	  result = 0;
Packit 5c3484
	  return result;
Packit 5c3484
	}
Packit 5c3484
      duration = (end.tv_sec + end.tv_nsec * 1e-9
Packit 5c3484
		  - start.tv_sec - start.tv_nsec * 1e-9);
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("delay loop of %d rounds took %s (according to clock_gettime)\n",
Packit 5c3484
		CGT_DELAY_COUNT, unittime_string (duration));
Packit 5c3484
      if (duration < 100e-9)
Packit 5c3484
	{
Packit 5c3484
	  if (speed_option_verbose)
Packit 5c3484
	    printf ("clock_gettime id=%d not believable\n", CGT_ID);
Packit 5c3484
	  result = 0;
Packit 5c3484
	  return result;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
  result = 1;
Packit 5c3484
  return result;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
static double
Packit 5c3484
freq_measure_mftb_one (void)
Packit 5c3484
{
Packit 5c3484
#define call_gettimeofday(t)   gettimeofday (&(t), NULL)
Packit 5c3484
#define timeval_tv_sec(t)      ((t).tv_sec)
Packit 5c3484
#define timeval_tv_usec(t)     ((t).tv_usec)
Packit 5c3484
  FREQ_MEASURE_ONE ("mftb", struct_timeval,
Packit 5c3484
		    call_gettimeofday, MFTB,
Packit 5c3484
		    timeval_tv_sec, timeval_tv_usec);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
static jmp_buf  mftb_works_buf;
Packit 5c3484
Packit 5c3484
static RETSIGTYPE
Packit 5c3484
mftb_works_handler (int sig)
Packit 5c3484
{
Packit 5c3484
  longjmp (mftb_works_buf, 1);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
mftb_works_p (void)
Packit 5c3484
{
Packit 5c3484
  unsigned   a[2];
Packit 5c3484
  RETSIGTYPE (*old_handler) (int);
Packit 5c3484
  double     cycletime;
Packit 5c3484
Packit 5c3484
  /* suppress a warning about a[] unused */
Packit 5c3484
  a[0] = 0;
Packit 5c3484
Packit 5c3484
  if (! have_mftb)
Packit 5c3484
    return 0;
Packit 5c3484
Packit 5c3484
#ifdef SIGILL
Packit 5c3484
  old_handler = signal (SIGILL, mftb_works_handler);
Packit 5c3484
  if (old_handler == SIG_ERR)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("mftb_works_p(): SIGILL not supported, assuming mftb works\n");
Packit 5c3484
      return 1;
Packit 5c3484
    }
Packit 5c3484
  if (setjmp (mftb_works_buf))
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("mftb_works_p(): SIGILL during mftb, so doesn't work\n");
Packit 5c3484
      return 0;
Packit 5c3484
    }
Packit 5c3484
  MFTB (a);
Packit 5c3484
  signal (SIGILL, old_handler);
Packit 5c3484
  if (speed_option_verbose)
Packit 5c3484
    printf ("mftb_works_p(): mftb works\n");
Packit 5c3484
#else
Packit 5c3484
Packit 5c3484
  if (speed_option_verbose)
Packit 5c3484
    printf ("mftb_works_p(): SIGILL not defined, assuming mftb works\n");
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if ! HAVE_GETTIMEOFDAY
Packit 5c3484
  if (speed_option_verbose)
Packit 5c3484
    printf ("mftb_works_p(): no gettimeofday available to measure mftb\n");
Packit 5c3484
  return 0;
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
  /* The time base is normally 1/4 of the bus speed on 6xx and 7xx chips, on
Packit 5c3484
     other chips it can be driven from an external clock. */
Packit 5c3484
  cycletime = freq_measure ("mftb", freq_measure_mftb_one);
Packit 5c3484
  if (cycletime == -1.0)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("mftb_works_p(): cannot measure mftb period\n");
Packit 5c3484
      return 0;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  mftb_unittime = cycletime;
Packit 5c3484
  return 1;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
volatile unsigned  *sgi_addr;
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
sgi_works_p (void)
Packit 5c3484
{
Packit 5c3484
#if HAVE_SYSSGI && HAVE_MMAP
Packit 5c3484
  static int  result = -1;
Packit 5c3484
Packit 5c3484
  size_t          pagesize, offset;
Packit 5c3484
  __psunsigned_t  phys, physpage;
Packit 5c3484
  void            *virtpage;
Packit 5c3484
  unsigned        period_picoseconds;
Packit 5c3484
  int             size, fd;
Packit 5c3484
Packit 5c3484
  if (result != -1)
Packit 5c3484
    return result;
Packit 5c3484
Packit 5c3484
  phys = syssgi (SGI_QUERY_CYCLECNTR, &period_picoseconds);
Packit 5c3484
  if (phys == (__psunsigned_t) -1)
Packit 5c3484
    {
Packit 5c3484
      /* ENODEV is the error when a counter is not available */
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("syssgi SGI_QUERY_CYCLECNTR error: %s\n", strerror (errno));
Packit 5c3484
      result = 0;
Packit 5c3484
      return result;
Packit 5c3484
    }
Packit 5c3484
  sgi_unittime = period_picoseconds * 1e-12;
Packit 5c3484
Packit 5c3484
  /* IRIX 5 doesn't have SGI_CYCLECNTR_SIZE, assume 32 bits in that case.
Packit 5c3484
     Challenge/ONYX hardware has a 64 bit byte counter, but there seems no
Packit 5c3484
     obvious way to identify that without SGI_CYCLECNTR_SIZE.  */
Packit 5c3484
#ifdef SGI_CYCLECNTR_SIZE
Packit 5c3484
  size = syssgi (SGI_CYCLECNTR_SIZE);
Packit 5c3484
  if (size == -1)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	{
Packit 5c3484
	  printf ("syssgi SGI_CYCLECNTR_SIZE error: %s\n", strerror (errno));
Packit 5c3484
	  printf ("    will assume size==4\n");
Packit 5c3484
	}
Packit 5c3484
      size = 32;
Packit 5c3484
    }
Packit 5c3484
#else
Packit 5c3484
  size = 32;
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
  if (size < 32)
Packit 5c3484
    {
Packit 5c3484
      printf ("syssgi SGI_CYCLECNTR_SIZE gives %d, expected 32 or 64\n", size);
Packit 5c3484
      result = 0;
Packit 5c3484
      return result;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  pagesize = getpagesize();
Packit 5c3484
  offset = (size_t) phys & (pagesize-1);
Packit 5c3484
  physpage = phys - offset;
Packit 5c3484
Packit 5c3484
  /* shouldn't cross over a page boundary */
Packit 5c3484
  ASSERT_ALWAYS (offset + size/8 <= pagesize);
Packit 5c3484
Packit 5c3484
  fd = open("/dev/mmem", O_RDONLY);
Packit 5c3484
  if (fd == -1)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("open /dev/mmem: %s\n", strerror (errno));
Packit 5c3484
      result = 0;
Packit 5c3484
      return result;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  virtpage = mmap (0, pagesize, PROT_READ, MAP_PRIVATE, fd, (off_t) physpage);
Packit 5c3484
  if (virtpage == (void *) -1)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose)
Packit 5c3484
	printf ("mmap /dev/mmem: %s\n", strerror (errno));
Packit 5c3484
      result = 0;
Packit 5c3484
      return result;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  /* address of least significant 4 bytes, knowing mips is big endian */
Packit 5c3484
  sgi_addr = (unsigned *) ((char *) virtpage + offset
Packit 5c3484
			   + size/8 - sizeof(unsigned));
Packit 5c3484
  result = 1;
Packit 5c3484
  return result;
Packit 5c3484
Packit 5c3484
#else /* ! (HAVE_SYSSGI && HAVE_MMAP) */
Packit 5c3484
  return 0;
Packit 5c3484
#endif
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
#define DEFAULT(var,n)  \
Packit 5c3484
  do {                  \
Packit 5c3484
    if (! (var))        \
Packit 5c3484
      (var) = (n);      \
Packit 5c3484
  } while (0)
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
speed_time_init (void)
Packit 5c3484
{
Packit 5c3484
  double supplement_unittime = 0.0;
Packit 5c3484
Packit 5c3484
  static int  speed_time_initialized = 0;
Packit 5c3484
  if (speed_time_initialized)
Packit 5c3484
    return;
Packit 5c3484
  speed_time_initialized = 1;
Packit 5c3484
Packit 5c3484
  speed_cycletime_init ();
Packit 5c3484
Packit 5c3484
  if (!speed_option_cycles_broken && have_cycles && cycles_works_p ())
Packit 5c3484
    {
Packit 5c3484
      use_cycles = 1;
Packit 5c3484
      DEFAULT (speed_cycletime, 1.0);
Packit 5c3484
      speed_unittime = speed_cycletime;
Packit 5c3484
      DEFAULT (speed_precision, 10000);
Packit 5c3484
      strcpy (speed_time_string, "CPU cycle counter");
Packit 5c3484
Packit 5c3484
      /* only used if a supplementary method is chosen below */
Packit 5c3484
      cycles_limit = (have_cycles == 1 ? M_2POW32 : M_2POW64) / 2.0
Packit 5c3484
	* speed_cycletime;
Packit 5c3484
Packit 5c3484
      if (have_grus && getrusage_microseconds_p() && ! getrusage_backwards_p())
Packit 5c3484
	{
Packit 5c3484
	  /* this is a good combination */
Packit 5c3484
	  use_grus = 1;
Packit 5c3484
	  supplement_unittime = grus_unittime = 1.0e-6;
Packit 5c3484
	  strcpy (speed_time_string, "CPU cycle counter, supplemented by microsecond getrusage()");
Packit 5c3484
	}
Packit 5c3484
      else if (have_cycles == 1)
Packit 5c3484
	{
Packit 5c3484
	  /* When speed_cyclecounter has a limited range, look for something
Packit 5c3484
	     to supplement it. */
Packit 5c3484
	  if (have_gtod && gettimeofday_microseconds_p())
Packit 5c3484
	    {
Packit 5c3484
	      use_gtod = 1;
Packit 5c3484
	      supplement_unittime = gtod_unittime = 1.0e-6;
Packit 5c3484
	      strcpy (speed_time_string, "CPU cycle counter, supplemented by microsecond gettimeofday()");
Packit 5c3484
	    }
Packit 5c3484
	  else if (have_grus)
Packit 5c3484
	    {
Packit 5c3484
	      use_grus = 1;
Packit 5c3484
	      supplement_unittime = grus_unittime = 1.0 / (double) clk_tck ();
Packit 5c3484
	      sprintf (speed_time_string, "CPU cycle counter, supplemented by %s clock tick getrusage()", unittime_string (supplement_unittime));
Packit 5c3484
	    }
Packit 5c3484
	  else if (have_times)
Packit 5c3484
	    {
Packit 5c3484
	      use_times = 1;
Packit 5c3484
	      supplement_unittime = times_unittime = 1.0 / (double) clk_tck ();
Packit 5c3484
	      sprintf (speed_time_string, "CPU cycle counter, supplemented by %s clock tick times()", unittime_string (supplement_unittime));
Packit 5c3484
	    }
Packit 5c3484
	  else if (have_gtod)
Packit 5c3484
	    {
Packit 5c3484
	      use_gtod = 1;
Packit 5c3484
	      supplement_unittime = gtod_unittime = 1.0 / (double) clk_tck ();
Packit 5c3484
	      sprintf (speed_time_string, "CPU cycle counter, supplemented by %s clock tick gettimeofday()", unittime_string (supplement_unittime));
Packit 5c3484
	    }
Packit 5c3484
	  else
Packit 5c3484
	    {
Packit 5c3484
	      fprintf (stderr, "WARNING: cycle counter is 32 bits and there's no other functions.\n");
Packit 5c3484
	      fprintf (stderr, "    Wraparounds may produce bad results on long measurements.\n");
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      if (use_grus || use_times || use_gtod)
Packit 5c3484
	{
Packit 5c3484
	  /* must know cycle period to compare cycles to other measuring
Packit 5c3484
	     (via cycles_limit) */
Packit 5c3484
	  speed_cycletime_need_seconds ();
Packit 5c3484
Packit 5c3484
	  if (speed_precision * supplement_unittime > cycles_limit)
Packit 5c3484
	    {
Packit 5c3484
	      fprintf (stderr, "WARNING: requested precision can't always be achieved due to limited range\n");
Packit 5c3484
	      fprintf (stderr, "    cycle counter and limited precision supplemental method\n");
Packit 5c3484
	      fprintf (stderr, "    (%s)\n", speed_time_string);
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
  else if (have_stck)
Packit 5c3484
    {
Packit 5c3484
      strcpy (speed_time_string, "STCK timestamp");
Packit 5c3484
      /* stck is in units of 2^-12 microseconds, which is very likely higher
Packit 5c3484
	 resolution than a cpu cycle */
Packit 5c3484
      if (speed_cycletime == 0.0)
Packit 5c3484
	speed_cycletime_fail
Packit 5c3484
	  ("Need to know CPU frequency for effective stck unit");
Packit 5c3484
      speed_unittime = MAX (speed_cycletime, STCK_PERIOD);
Packit 5c3484
      DEFAULT (speed_precision, 10000);
Packit 5c3484
    }
Packit 5c3484
  else if (have_mftb && mftb_works_p ())
Packit 5c3484
    {
Packit 5c3484
      use_mftb = 1;
Packit 5c3484
      DEFAULT (speed_precision, 10000);
Packit 5c3484
      speed_unittime = mftb_unittime;
Packit 5c3484
      sprintf (speed_time_string, "mftb counter (%s)",
Packit 5c3484
	       unittime_string (speed_unittime));
Packit 5c3484
    }
Packit 5c3484
  else if (have_sgi && sgi_works_p ())
Packit 5c3484
    {
Packit 5c3484
      use_sgi = 1;
Packit 5c3484
      DEFAULT (speed_precision, 10000);
Packit 5c3484
      speed_unittime = sgi_unittime;
Packit 5c3484
      sprintf (speed_time_string, "syssgi() mmap counter (%s), supplemented by millisecond getrusage()",
Packit 5c3484
	       unittime_string (speed_unittime));
Packit 5c3484
      /* supplemented with getrusage, which we assume to have 1ms resolution */
Packit 5c3484
      use_grus = 1;
Packit 5c3484
      supplement_unittime = 1e-3;
Packit 5c3484
    }
Packit 5c3484
  else if (have_rrt)
Packit 5c3484
    {
Packit 5c3484
      timebasestruct_t  t;
Packit 5c3484
      use_rrt = 1;
Packit 5c3484
      DEFAULT (speed_precision, 10000);
Packit 5c3484
      read_real_time (&t, sizeof(t));
Packit 5c3484
      switch (t.flag) {
Packit 5c3484
      case RTC_POWER:
Packit 5c3484
	/* FIXME: What's the actual RTC resolution? */
Packit 5c3484
	speed_unittime = 1e-7;
Packit 5c3484
	strcpy (speed_time_string, "read_real_time() power nanoseconds");
Packit 5c3484
	break;
Packit 5c3484
      case RTC_POWER_PC:
Packit 5c3484
	t.tb_high = 1;
Packit 5c3484
	t.tb_low = 0;
Packit 5c3484
	time_base_to_time (&t, sizeof(t));
Packit 5c3484
	speed_unittime = TIMEBASESTRUCT_SECS(&t) / M_2POW32;
Packit 5c3484
	sprintf (speed_time_string, "%s read_real_time() powerpc ticks",
Packit 5c3484
		 unittime_string (speed_unittime));
Packit 5c3484
	break;
Packit 5c3484
      default:
Packit 5c3484
	fprintf (stderr, "ERROR: Unrecognised timebasestruct_t flag=%d\n",
Packit 5c3484
		 t.flag);
Packit 5c3484
	abort ();
Packit 5c3484
      }
Packit 5c3484
    }
Packit 5c3484
  else if (have_cgt && cgt_works_p() && cgt_unittime < 1.5e-6)
Packit 5c3484
    {
Packit 5c3484
      /* use clock_gettime if microsecond or better resolution */
Packit 5c3484
    choose_cgt:
Packit 5c3484
      use_cgt = 1;
Packit 5c3484
      speed_unittime = cgt_unittime;
Packit 5c3484
      DEFAULT (speed_precision, (cgt_unittime <= 0.1e-6 ? 10000 : 1000));
Packit 5c3484
      strcpy (speed_time_string, "microsecond accurate clock_gettime()");
Packit 5c3484
    }
Packit 5c3484
  else if (have_times && clk_tck() > 1000000)
Packit 5c3484
    {
Packit 5c3484
      /* Cray vector systems have times() which is clock cycle resolution
Packit 5c3484
	 (eg. 450 MHz).  */
Packit 5c3484
      DEFAULT (speed_precision, 10000);
Packit 5c3484
      goto choose_times;
Packit 5c3484
    }
Packit 5c3484
  else if (have_grus && getrusage_microseconds_p() && ! getrusage_backwards_p())
Packit 5c3484
    {
Packit 5c3484
      use_grus = 1;
Packit 5c3484
      speed_unittime = grus_unittime = 1.0e-6;
Packit 5c3484
      DEFAULT (speed_precision, 1000);
Packit 5c3484
      strcpy (speed_time_string, "microsecond accurate getrusage()");
Packit 5c3484
    }
Packit 5c3484
  else if (have_gtod && gettimeofday_microseconds_p())
Packit 5c3484
    {
Packit 5c3484
      use_gtod = 1;
Packit 5c3484
      speed_unittime = gtod_unittime = 1.0e-6;
Packit 5c3484
      DEFAULT (speed_precision, 1000);
Packit 5c3484
      strcpy (speed_time_string, "microsecond accurate gettimeofday()");
Packit 5c3484
    }
Packit 5c3484
  else if (have_cgt && cgt_works_p() && cgt_unittime < 1.5/clk_tck())
Packit 5c3484
    {
Packit 5c3484
      /* use clock_gettime if 1 tick or better resolution */
Packit 5c3484
      goto choose_cgt;
Packit 5c3484
    }
Packit 5c3484
  else if (have_times)
Packit 5c3484
    {
Packit 5c3484
      use_tick_boundary = 1;
Packit 5c3484
      DEFAULT (speed_precision, 200);
Packit 5c3484
    choose_times:
Packit 5c3484
      use_times = 1;
Packit 5c3484
      speed_unittime = times_unittime = 1.0 / (double) clk_tck ();
Packit 5c3484
      sprintf (speed_time_string, "%s clock tick times()",
Packit 5c3484
	       unittime_string (speed_unittime));
Packit 5c3484
    }
Packit 5c3484
  else if (have_grus)
Packit 5c3484
    {
Packit 5c3484
      use_grus = 1;
Packit 5c3484
      use_tick_boundary = 1;
Packit 5c3484
      speed_unittime = grus_unittime = 1.0 / (double) clk_tck ();
Packit 5c3484
      DEFAULT (speed_precision, 200);
Packit 5c3484
      sprintf (speed_time_string, "%s clock tick getrusage()\n",
Packit 5c3484
	       unittime_string (speed_unittime));
Packit 5c3484
    }
Packit 5c3484
  else if (have_gtod)
Packit 5c3484
    {
Packit 5c3484
      use_gtod = 1;
Packit 5c3484
      use_tick_boundary = 1;
Packit 5c3484
      speed_unittime = gtod_unittime = 1.0 / (double) clk_tck ();
Packit 5c3484
      DEFAULT (speed_precision, 200);
Packit 5c3484
      sprintf (speed_time_string, "%s clock tick gettimeofday()",
Packit 5c3484
	       unittime_string (speed_unittime));
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      fprintf (stderr, "No time measuring method available\n");
Packit 5c3484
      fprintf (stderr, "None of: speed_cyclecounter(), STCK(), getrusage(), gettimeofday(), times()\n");
Packit 5c3484
      abort ();
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (speed_option_verbose)
Packit 5c3484
    {
Packit 5c3484
      printf ("speed_time_init: %s\n", speed_time_string);
Packit 5c3484
      printf ("    speed_precision     %d\n", speed_precision);
Packit 5c3484
      printf ("    speed_unittime      %.2g\n", speed_unittime);
Packit 5c3484
      if (supplement_unittime)
Packit 5c3484
	printf ("    supplement_unittime %.2g\n", supplement_unittime);
Packit 5c3484
      printf ("    use_tick_boundary   %d\n", use_tick_boundary);
Packit 5c3484
      if (have_cycles)
Packit 5c3484
	printf ("    cycles_limit        %.2g seconds\n", cycles_limit);
Packit 5c3484
    }
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Burn up CPU until a clock tick boundary, for greater accuracy.  Set the
Packit 5c3484
   corresponding "start_foo" appropriately too. */
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
grus_tick_boundary (void)
Packit 5c3484
{
Packit 5c3484
  struct_rusage  prev;
Packit 5c3484
  getrusage (0, &prev;;
Packit 5c3484
  do {
Packit 5c3484
    getrusage (0, &start_grus);
Packit 5c3484
  } while (start_grus.ru_utime.tv_usec == prev.ru_utime.tv_usec);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
gtod_tick_boundary (void)
Packit 5c3484
{
Packit 5c3484
  struct_timeval  prev;
Packit 5c3484
  gettimeofday (&prev, NULL);
Packit 5c3484
  do {
Packit 5c3484
    gettimeofday (&start_gtod, NULL);
Packit 5c3484
  } while (start_gtod.tv_usec == prev.tv_usec);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
times_tick_boundary (void)
Packit 5c3484
{
Packit 5c3484
  struct_tms  prev;
Packit 5c3484
  times (&prev;;
Packit 5c3484
  do
Packit 5c3484
    times (&start_times);
Packit 5c3484
  while (start_times.tms_utime == prev.tms_utime);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* "have_" values are tested to let unused code go dead.  */
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
speed_starttime (void)
Packit 5c3484
{
Packit 5c3484
  speed_time_init ();
Packit 5c3484
Packit 5c3484
  if (have_grus && use_grus)
Packit 5c3484
    {
Packit 5c3484
      if (use_tick_boundary)
Packit 5c3484
	grus_tick_boundary ();
Packit 5c3484
      else
Packit 5c3484
	getrusage (0, &start_grus);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (have_gtod && use_gtod)
Packit 5c3484
    {
Packit 5c3484
      if (use_tick_boundary)
Packit 5c3484
	gtod_tick_boundary ();
Packit 5c3484
      else
Packit 5c3484
	gettimeofday (&start_gtod, NULL);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (have_times && use_times)
Packit 5c3484
    {
Packit 5c3484
      if (use_tick_boundary)
Packit 5c3484
	times_tick_boundary ();
Packit 5c3484
      else
Packit 5c3484
	times (&start_times);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (have_cgt && use_cgt)
Packit 5c3484
    clock_gettime (CGT_ID, &start_cgt);
Packit 5c3484
Packit 5c3484
  if (have_rrt && use_rrt)
Packit 5c3484
    read_real_time (&start_rrt, sizeof(start_rrt));
Packit 5c3484
Packit 5c3484
  if (have_sgi && use_sgi)
Packit 5c3484
    start_sgi = *sgi_addr;
Packit 5c3484
Packit 5c3484
  if (have_mftb && use_mftb)
Packit 5c3484
    MFTB (start_mftb);
Packit 5c3484
Packit 5c3484
  if (have_stck && use_stck)
Packit 5c3484
    STCK (start_stck);
Packit 5c3484
Packit 5c3484
  /* Cycles sampled last for maximum accuracy. */
Packit 5c3484
  if (have_cycles && use_cycles)
Packit 5c3484
    speed_cyclecounter (start_cycles);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Calculate the difference between two cycle counter samples, as a "double"
Packit 5c3484
   counter of cycles.
Packit 5c3484
Packit 5c3484
   The start and end values are allowed to cancel in integers in case the
Packit 5c3484
   counter values are bigger than the 53 bits that normally fit in a double.
Packit 5c3484
Packit 5c3484
   This works even if speed_cyclecounter() puts a value bigger than 32-bits
Packit 5c3484
   in the low word (the high word always gets a 2**32 multiplier though). */
Packit 5c3484
Packit 5c3484
double
Packit 5c3484
speed_cyclecounter_diff (const unsigned end[2], const unsigned start[2])
Packit 5c3484
{
Packit 5c3484
  unsigned  d;
Packit 5c3484
  double    t;
Packit 5c3484
Packit 5c3484
  if (have_cycles == 1)
Packit 5c3484
    {
Packit 5c3484
      t = (end[0] - start[0]);
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      d = end[0] - start[0];
Packit 5c3484
      t = d - (d > end[0] ? M_2POWU : 0.0);
Packit 5c3484
      t += (end[1] - start[1]) * M_2POW32;
Packit 5c3484
    }
Packit 5c3484
  return t;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
double
Packit 5c3484
speed_mftb_diff (const unsigned end[2], const unsigned start[2])
Packit 5c3484
{
Packit 5c3484
  unsigned  d;
Packit 5c3484
  double    t;
Packit 5c3484
Packit 5c3484
  d = end[0] - start[0];
Packit 5c3484
  t = (double) d - (d > end[0] ? M_2POW32 : 0.0);
Packit 5c3484
  t += (end[1] - start[1]) * M_2POW32;
Packit 5c3484
  return t;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Calculate the difference between "start" and "end" using fields "sec" and
Packit 5c3484
   "psec", where each "psec" is a "punit" of a second.
Packit 5c3484
Packit 5c3484
   The seconds parts are allowed to cancel before being combined with the
Packit 5c3484
   psec parts, in case a simple "sec+psec*punit" exceeds the precision of a
Packit 5c3484
   double.
Packit 5c3484
Packit 5c3484
   Total time is only calculated in a "double" since an integer count of
Packit 5c3484
   psecs might overflow.  2^32 microseconds is only a bit over an hour, or
Packit 5c3484
   2^32 nanoseconds only about 4 seconds.
Packit 5c3484
Packit 5c3484
   The casts to "long" are for the benefit of timebasestruct_t, where the
Packit 5c3484
   fields are only "unsigned int", but we want a signed difference.  */
Packit 5c3484
Packit 5c3484
#define DIFF_SECS_ROUTINE(sec, psec, punit)                     \
Packit 5c3484
  {                                                             \
Packit 5c3484
    long  sec_diff, psec_diff;                                  \
Packit 5c3484
    sec_diff = (long) end->sec - (long) start->sec;             \
Packit 5c3484
    psec_diff = (long) end->psec - (long) start->psec;          \
Packit 5c3484
    return (double) sec_diff + punit * (double) psec_diff;      \
Packit 5c3484
  }
Packit 5c3484
Packit 5c3484
double
Packit 5c3484
timeval_diff_secs (const struct_timeval *end, const struct_timeval *start)
Packit 5c3484
{
Packit 5c3484
  DIFF_SECS_ROUTINE (tv_sec, tv_usec, 1e-6);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
double
Packit 5c3484
rusage_diff_secs (const struct_rusage *end, const struct_rusage *start)
Packit 5c3484
{
Packit 5c3484
  DIFF_SECS_ROUTINE (ru_utime.tv_sec, ru_utime.tv_usec, 1e-6);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
double
Packit 5c3484
timespec_diff_secs (const struct_timespec *end, const struct_timespec *start)
Packit 5c3484
{
Packit 5c3484
  DIFF_SECS_ROUTINE (tv_sec, tv_nsec, 1e-9);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* This is for use after time_base_to_time, ie. for seconds and nanoseconds. */
Packit 5c3484
double
Packit 5c3484
timebasestruct_diff_secs (const timebasestruct_t *end,
Packit 5c3484
			  const timebasestruct_t *start)
Packit 5c3484
{
Packit 5c3484
  DIFF_SECS_ROUTINE (tb_high, tb_low, 1e-9);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
double
Packit 5c3484
speed_endtime (void)
Packit 5c3484
{
Packit 5c3484
#define END_USE(name,value)                             \
Packit 5c3484
  do {                                                  \
Packit 5c3484
    if (speed_option_verbose >= 3)                      \
Packit 5c3484
      printf ("speed_endtime(): used %s\n", name);      \
Packit 5c3484
    result = value;                                     \
Packit 5c3484
    goto done;                                          \
Packit 5c3484
  } while (0)
Packit 5c3484
Packit 5c3484
#define END_ENOUGH(name,value)                                          \
Packit 5c3484
  do {                                                                  \
Packit 5c3484
    if (speed_option_verbose >= 3)                                      \
Packit 5c3484
      printf ("speed_endtime(): %s gives enough precision\n", name);    \
Packit 5c3484
    result = value;                                                     \
Packit 5c3484
    goto done;                                                          \
Packit 5c3484
  } while (0)
Packit 5c3484
Packit 5c3484
#define END_EXCEED(name,value)                                            \
Packit 5c3484
  do {                                                                    \
Packit 5c3484
    if (speed_option_verbose >= 3)                                        \
Packit 5c3484
      printf ("speed_endtime(): cycle counter limit exceeded, used %s\n", \
Packit 5c3484
	      name);                                                      \
Packit 5c3484
    result = value;                                                       \
Packit 5c3484
    goto done;                                                            \
Packit 5c3484
  } while (0)
Packit 5c3484
Packit 5c3484
  unsigned          end_cycles[2];
Packit 5c3484
  stck_t            end_stck;
Packit 5c3484
  unsigned          end_mftb[2];
Packit 5c3484
  unsigned          end_sgi;
Packit 5c3484
  timebasestruct_t  end_rrt;
Packit 5c3484
  struct_timespec   end_cgt;
Packit 5c3484
  struct_timeval    end_gtod;
Packit 5c3484
  struct_rusage     end_grus;
Packit 5c3484
  struct_tms        end_times;
Packit 5c3484
  double            t_gtod, t_grus, t_times, t_cgt;
Packit 5c3484
  double            t_rrt, t_sgi, t_mftb, t_stck, t_cycles;
Packit 5c3484
  double            result;
Packit 5c3484
Packit 5c3484
  /* Cycles sampled first for maximum accuracy.
Packit 5c3484
     "have_" values tested to let unused code go dead.  */
Packit 5c3484
Packit 5c3484
  if (have_cycles && use_cycles)  speed_cyclecounter (end_cycles);
Packit 5c3484
  if (have_stck   && use_stck)    STCK (end_stck);
Packit 5c3484
  if (have_mftb   && use_mftb)    MFTB (end_mftb);
Packit 5c3484
  if (have_sgi    && use_sgi)     end_sgi = *sgi_addr;
Packit 5c3484
  if (have_rrt    && use_rrt)     read_real_time (&end_rrt, sizeof(end_rrt));
Packit 5c3484
  if (have_cgt    && use_cgt)     clock_gettime (CGT_ID, &end_cgt);
Packit 5c3484
  if (have_gtod   && use_gtod)    gettimeofday (&end_gtod, NULL);
Packit 5c3484
  if (have_grus   && use_grus)    getrusage (0, &end_grus);
Packit 5c3484
  if (have_times  && use_times)   times (&end_times);
Packit 5c3484
Packit 5c3484
  result = -1.0;
Packit 5c3484
Packit 5c3484
  if (speed_option_verbose >= 4)
Packit 5c3484
    {
Packit 5c3484
      printf ("speed_endtime():\n");
Packit 5c3484
      if (use_cycles)
Packit 5c3484
	printf ("   cycles  0x%X,0x%X -> 0x%X,0x%X\n",
Packit 5c3484
		start_cycles[1], start_cycles[0],
Packit 5c3484
		end_cycles[1], end_cycles[0]);
Packit 5c3484
Packit 5c3484
      if (use_stck)
Packit 5c3484
	printf ("   stck  0x%lX -> 0x%lX\n", start_stck, end_stck);
Packit 5c3484
Packit 5c3484
      if (use_mftb)
Packit 5c3484
	printf ("   mftb  0x%X,%08X -> 0x%X,%08X\n",
Packit 5c3484
		start_mftb[1], start_mftb[0],
Packit 5c3484
		end_mftb[1], end_mftb[0]);
Packit 5c3484
Packit 5c3484
      if (use_sgi)
Packit 5c3484
	printf ("   sgi  0x%X -> 0x%X\n", start_sgi, end_sgi);
Packit 5c3484
Packit 5c3484
      if (use_rrt)
Packit 5c3484
	printf ("   read_real_time  (%d)%u,%u -> (%d)%u,%u\n",
Packit 5c3484
		start_rrt.flag, start_rrt.tb_high, start_rrt.tb_low,
Packit 5c3484
		end_rrt.flag, end_rrt.tb_high, end_rrt.tb_low);
Packit 5c3484
Packit 5c3484
      if (use_cgt)
Packit 5c3484
	printf ("   clock_gettime  %ld.%09ld -> %ld.%09ld\n",
Packit 5c3484
		start_cgt.tv_sec, start_cgt.tv_nsec,
Packit 5c3484
		end_cgt.tv_sec, end_cgt.tv_nsec);
Packit 5c3484
Packit 5c3484
      if (use_gtod)
Packit 5c3484
	printf ("   gettimeofday  %ld.%06ld -> %ld.%06ld\n",
Packit 5c3484
		start_gtod.tv_sec, start_gtod.tv_usec,
Packit 5c3484
		end_gtod.tv_sec, end_gtod.tv_usec);
Packit 5c3484
Packit 5c3484
      if (use_grus)
Packit 5c3484
	printf ("   getrusage  %ld.%06ld -> %ld.%06ld\n",
Packit 5c3484
		start_grus.ru_utime.tv_sec, start_grus.ru_utime.tv_usec,
Packit 5c3484
		end_grus.ru_utime.tv_sec, end_grus.ru_utime.tv_usec);
Packit 5c3484
Packit 5c3484
      if (use_times)
Packit 5c3484
	printf ("   times  %ld -> %ld\n",
Packit 5c3484
		start_times.tms_utime, end_times.tms_utime);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_rrt)
Packit 5c3484
    {
Packit 5c3484
      time_base_to_time (&start_rrt, sizeof(start_rrt));
Packit 5c3484
      time_base_to_time (&end_rrt, sizeof(end_rrt));
Packit 5c3484
      t_rrt = timebasestruct_diff_secs (&end_rrt, &start_rrt);
Packit 5c3484
      END_USE ("read_real_time()", t_rrt);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_cgt)
Packit 5c3484
    {
Packit 5c3484
      t_cgt = timespec_diff_secs (&end_cgt, &start_cgt);
Packit 5c3484
      END_USE ("clock_gettime()", t_cgt);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_grus)
Packit 5c3484
    {
Packit 5c3484
      t_grus = rusage_diff_secs (&end_grus, &start_grus);
Packit 5c3484
Packit 5c3484
      /* Use getrusage() if the cycle counter limit would be exceeded, or if
Packit 5c3484
	 it provides enough accuracy already. */
Packit 5c3484
      if (use_cycles)
Packit 5c3484
	{
Packit 5c3484
	  if (t_grus >= speed_precision*grus_unittime)
Packit 5c3484
	    END_ENOUGH ("getrusage()", t_grus);
Packit 5c3484
	  if (t_grus >= cycles_limit)
Packit 5c3484
	    END_EXCEED ("getrusage()", t_grus);
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_times)
Packit 5c3484
    {
Packit 5c3484
      t_times = (end_times.tms_utime - start_times.tms_utime) * times_unittime;
Packit 5c3484
Packit 5c3484
      /* Use times() if the cycle counter limit would be exceeded, or if
Packit 5c3484
	 it provides enough accuracy already. */
Packit 5c3484
      if (use_cycles)
Packit 5c3484
	{
Packit 5c3484
	  if (t_times >= speed_precision*times_unittime)
Packit 5c3484
	    END_ENOUGH ("times()", t_times);
Packit 5c3484
	  if (t_times >= cycles_limit)
Packit 5c3484
	    END_EXCEED ("times()", t_times);
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_gtod)
Packit 5c3484
    {
Packit 5c3484
      t_gtod = timeval_diff_secs (&end_gtod, &start_gtod);
Packit 5c3484
Packit 5c3484
      /* Use gettimeofday() if it measured a value bigger than the cycle
Packit 5c3484
	 counter can handle.  */
Packit 5c3484
      if (use_cycles)
Packit 5c3484
	{
Packit 5c3484
	  if (t_gtod >= cycles_limit)
Packit 5c3484
	    END_EXCEED ("gettimeofday()", t_gtod);
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_mftb)
Packit 5c3484
    {
Packit 5c3484
      t_mftb = speed_mftb_diff (end_mftb, start_mftb) * mftb_unittime;
Packit 5c3484
      END_USE ("mftb", t_mftb);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_stck)
Packit 5c3484
    {
Packit 5c3484
      t_stck = (end_stck - start_stck) * STCK_PERIOD;
Packit 5c3484
      END_USE ("stck", t_stck);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_sgi)
Packit 5c3484
    {
Packit 5c3484
      t_sgi = (end_sgi - start_sgi) * sgi_unittime;
Packit 5c3484
      END_USE ("SGI hardware counter", t_sgi);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_cycles)
Packit 5c3484
    {
Packit 5c3484
      t_cycles = speed_cyclecounter_diff (end_cycles, start_cycles)
Packit 5c3484
	* speed_cycletime;
Packit 5c3484
      END_USE ("cycle counter", t_cycles);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (use_grus && getrusage_microseconds_p())
Packit 5c3484
    END_USE ("getrusage()", t_grus);
Packit 5c3484
Packit 5c3484
  if (use_gtod && gettimeofday_microseconds_p())
Packit 5c3484
    END_USE ("gettimeofday()", t_gtod);
Packit 5c3484
Packit 5c3484
  if (use_times)  END_USE ("times()",        t_times);
Packit 5c3484
  if (use_grus)   END_USE ("getrusage()",    t_grus);
Packit 5c3484
  if (use_gtod)   END_USE ("gettimeofday()", t_gtod);
Packit 5c3484
Packit 5c3484
  fprintf (stderr, "speed_endtime(): oops, no time method available\n");
Packit 5c3484
  abort ();
Packit 5c3484
Packit 5c3484
 done:
Packit 5c3484
  if (result < 0.0)
Packit 5c3484
    {
Packit 5c3484
      if (speed_option_verbose >= 2)
Packit 5c3484
	fprintf (stderr, "speed_endtime(): warning, treating negative time as zero: %.9f\n", result);
Packit 5c3484
      result = 0.0;
Packit 5c3484
    }
Packit 5c3484
  return result;
Packit 5c3484
}