[alt.sources] [comp.protocols.time.ntp] Unix system clock resolution

hakanson@CSE.OGI.EDU (Marion Hakanson) (08/16/90)

Archive-name: clockres/15-Aug-90
Original-posting-by: hakanson@CSE.OGI.EDU (Marion Hakanson)
Original-subject: Unix system clock resolution (precision)
Reposted-by: emv@math.lsa.umich.edu (Edward Vielmetti)

[Reposted from comp.protocols.time.ntp.
Comments on this service to emv@math.lsa.umich.edu (Edward Vielmetti).]

Below find clockres.c, source to a program which will tell you (most of
the time) the resolution of your gettimeofday(2) system call, and thus
probably the precision of your system's underlying clock.  It has uses
other than satisfying curiosity:  you may be able to use it to tell
an ntp server what precision to use, or to find out how accurate various
performance measurements might be.  Do keep in mind that ntpd's estimate
using the kernel HZ value is probably best for most machines.

Bug reports would be welcome to me, though it's pretty simple.  I
don't really care to create an archive of the results on various and
sundry computer systems -- it takes less time to compile and run the
program than it does to ftp a list from somewhere to look up your
machine.  But perhaps such an archive would be of use to prospective
purchasers of hardware.  I certainly ran into some surprises (like
maybe 2^-6 isn't "bad" enough for an NTP precision).  Here's a list of
what I've encountered so far, in no particular order (the "or better"
is explained in the program header commentary).

=============
Hardware		O/S software		Resolution (microseconds)

HP-9000/320		4.3bsd/Univ.Utah	336 or better (prob. 4)
HP-9000/200		4.3bsd/Berkeley		830 or better (prob. 4)

DEC-DS2100		Ultrix-3.1 (UWS-2.2)	3906
DEC-uVAX/II		4.3bsd/MtXinu		10000

Sun-4/60		SunOS-4.0.3c		53 or better (maybe 1?)
Sun-4/60		SunOS-4.1(c)		62 or better (maybe 1?)
Sun-4/65		SunOS-4.1(c)		10000
Sun-4/110		SunOS-4.0.3		10000
Sun-4/280		SunOS-4.0.3		10000
Sun-3/50		SunOS-4.0.3		20000
Sun-3/60		SunOS-4.1		20000

Sequent-S81		DYNIX-3.0.12		10000
Sequent-B21		DYNIX-3.1.alpha1	10000

Tek-431[57]		UTek-3.1		50000 (missed the boat again)
=============


=============
#ifndef lint
char rcsid[] = "$Id: clockres.c,v 1.9 90/08/14 23:41:21 hakanson Exp $";
#endif /* lint */

/*
 * Determine the resolution of the system clock (gettimeofday(2)).
 *   Marion Hakanson (hakanson@cse.ogi.edu)
 *   Oregon Graduate Institute of Science and Technology
 *
 * The idea is to call gettimeofday(2), then repeatedly call it again
 * until you get a different value.  The difference between the two
 * values should be the resolution (precision) of the system clock.
 * A little noise sometimes creeps in, due to adjtime(2)'s being done,
 * but in practice this appears rarely and can be factored out by running
 * this test program repeatedly (say 10 times).  There is a "-v" option
 * to print out the two differing timestamps (and the number of calls
 * it took before a difference was detected).  You may occasionally
 * see two (but never more) of these printed out before a result is
 * announced -- this is to avoid anomalies with comparing microseconds
 * when a wrap to the next full second has occurred.
 *
 * The big flaw to this approach is that most 4.3bsd-based systems have
 * in their kernels a microtime() routine which is supposed to return
 * the current time to the microsecond.  A "good" implementation would
 * (e.g.) read a hardware interval timer to determine how many usec's
 * had passed since the last clock tick.  But much hardware seems to
 * lack such a timer, and as a result, most implementations of microtime()
 * fake it by adding a microsecond to the current time for each subsequent
 * call to microtime() -- just to ensure that two invocations never return
 * exactly the same value (the fake microseconds go away at the next clock
 * tick) .  Since most machines (known to mortals) these days cannot make
 * a system call in one microsecond, this program has a hack in it to
 * detect these "fake" microtime() implementations by adding the number
 * of gettimeofday(2) calls to the initial timestamp before comparing
 * with the most recent timestamp.  A ">=" test is used to work with
 * older bsd's (like 4.2), where two subsequent timestamps often are
 * the same (within the value of a tick), and the "-f" option disables
 * the "fake" microtime() workaround.  More irregular "fakes" will no
 * doubt be reported as terrific (but erroneous) clock resolutions.
 *
 * A small flaw is that if a machine has a clock resolution finer than
 * the time it takes to make a single pass through the loop, then the
 * program reports that value as the resolution of the clock, rather
 * than the actual resolution.  A warning about this is printed when
 * a real difference is detected after only one pass (it says "... XX
 * microseconds or better" instead of just "... XX microseconds").
 * Again, repeated runs can show this condition if the value seems to
 * fluctuate by a few microseconds and/or vary with system load (a
 * "genuine" result seems to stay rock solid across invocations).
 * In practice, few real machines seem to encounter this behavior,
 * and you should count yourself lucky if you have one.
 */

#include <stdio.h>
#include <sys/time.h>

extern int getopt();

main(argc, argv)
    int argc;  char *argv[];
{
int verbose;
register int fakemicro;
int again;
struct timeval t1, t2;
register int calls;
int c;


verbose = 0;
fakemicro = 1;

while ( (c = getopt(argc, argv, "fv")) != EOF ) {
  switch ( c ) {
    case 'f':
      fakemicro = 0;
      break;
    case 'v':
      verbose = 1;
      break;
    case '?':
      fprintf(stderr, "usage: %s [-fv]\n", argv[0]);
      exit(1);
  }
}
   

again = 0;

top:

if (again > 1) {
  fprintf (stderr, "%s: More than one second passed, giving up.\n", argv[0]);
  exit(1);
}

again++;

calls = 0;

(void) gettimeofday(&t1, NULL);

do {
  calls++;
  (void) gettimeofday(&t2, NULL);
} while ( (t1.tv_sec == t2.tv_sec)
	 && ( fakemicro ? ((t1.tv_usec+calls) >= t2.tv_usec)
	 		: (t1.tv_usec == t2.tv_usec) ) );

if (verbose) {
  printf ("t1 %d.%06.6d t2 %d.%06.6d calls %d\n",
	t1.tv_sec, t1.tv_usec, t2.tv_sec, t2.tv_usec, calls);
}

/* Check for wrap to next second */
if (t1.tv_sec != t2.tv_sec)
  goto top;

printf ("Clock step resolution %d microseconds", t2.tv_usec - t1.tv_usec);

if ( calls == 1 )
  printf (" or better");

printf("\n");

exit(0);
}
=============

-- 
Marion Hakanson         Domain: hakanson@cse.ogi.edu
                        UUCP  : {hp-pcd,tektronix}!ogicse!hakanson