precision.c revision 54359
1#include <sys/types.h>
2#include <sys/time.h>
3#include <stdio.h>
4#include "ntp_unixtime.h"
5
6#define	DEFAULT_SYS_PRECISION	-99
7
8int default_get_resolution();
9int default_get_precision();
10
11int
12main(
13	int argc,
14	char *argv[]
15	)
16{
17	printf("log2(resolution) = %d, log2(precision) = %d\n",
18	       default_get_resolution(),
19	       default_get_precision());
20	return 0;
21}
22
23/* Find the resolution of the system clock by watching how the current time
24 * changes as we read it repeatedly.
25 *
26 * struct timeval is only good to 1us, which may cause problems as machines
27 * get faster, but until then the logic goes:
28 *
29 * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
30 * probably use the "unused" low order bits as a counter (to force time to be
31 * a strictly increaing variable), incrementing it each time any process
32 * requests the time [[ or maybe time will stand still ? ]].
33 *
34 * SO: the logic goes:
35 *
36 *      IF      the difference from the last time is "small" (< MINSTEP)
37 *      THEN    this machine is "counting" with the low order bits
38 *      ELIF    this is not the first time round the loop
39 *      THEN    this machine *WAS* counting, and has now stepped
40 *      ELSE    this machine has resolution < time to read clock
41 *
42 * SO: if it exits on the first loop, assume "full accuracy" (1us)
43 *     otherwise, take the log2(observered difference, rounded UP)
44 *
45 * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
46 * and the first loop, it doesn't stop too early.
47 * Making it even greater allows MINSTEP to be reduced, assuming that the
48 * chance of MINSTEP-1 other processes getting in and calling gettimeofday
49 * between this processes's calls.
50 * Reducing MINSTEP may be necessary as this sets an upper bound for the time
51 * to actually call gettimeofday.
52 */
53
54#define	DUSECS	1000000
55#define	HUSECS	(1024 * 1024)
56#define	MINSTEP	5	/* some systems increment uS on each call */
57/* Don't use "1" as some *other* process may read too*/
58/*We assume no system actually *ANSWERS* in this time*/
59#define MAXSTEP 20000   /* maximum clock increment (us) */
60#define MINLOOPS 5      /* minimum number of step samples */
61#define	MAXLOOPS HUSECS	/* Assume precision < .1s ! */
62
63int
64default_get_resolution(void)
65{
66	struct timeval tp;
67	struct timezone tzp;
68	long last;
69	int i;
70	long diff;
71	long val;
72	int minsteps = MINLOOPS;	/* need at least this many steps */
73
74	gettimeofday(&tp, &tzp);
75	last = tp.tv_usec;
76	for (i = - --minsteps; i< MAXLOOPS; i++) {
77		gettimeofday(&tp, &tzp);
78		diff = tp.tv_usec - last;
79		if (diff < 0) diff += DUSECS;
80		if (diff > MINSTEP) if (minsteps-- <= 0) break;
81		last = tp.tv_usec;
82	}
83
84	printf("resolution = %ld usec after %d loop%s\n",
85	       diff, i, (i==1) ? "" : "s");
86
87	diff = (diff *3)/2;
88	if (i >= MAXLOOPS) {
89		printf(
90			"     (Boy this machine is fast ! %d loops without a step)\n",
91			MAXLOOPS);
92		diff = 1; /* No STEP, so FAST machine */
93	}
94	if (i == 0) {
95		printf(
96			"     (The resolution is less than the time to read the clock -- Assume 1us)\n");
97		diff = 1; /* time to read clock >= resolution */
98	}
99	for (i=0, val=HUSECS; val>0; i--, val >>= 1) if (diff >= val) return i;
100	printf("     (Oh dear -- that wasn't expected ! I'll guess !)\n");
101	return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
102}
103
104/* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */
105
106/*
107 * This routine calculates the differences between successive calls to
108 * gettimeofday(). If a difference is less than zero, the us field
109 * has rolled over to the next second, so we add a second in us. If
110 * the difference is greater than zero and less than MINSTEP, the
111 * clock has been advanced by a small amount to avoid standing still.
112 * If the clock has advanced by a greater amount, then a timer interrupt
113 * has occurred and this amount represents the precision of the clock.
114 * In order to guard against spurious values, which could occur if we
115 * happen to hit a fat interrupt, we do this for MINLOOPS times and
116 * keep the minimum value obtained.
117 */
118int
119default_get_precision(void)
120{
121	struct timeval tp;
122	struct timezone tzp;
123#ifdef HAVE_GETCLOCK
124	struct timespec ts;
125#endif
126	long last;
127	int i;
128	long diff;
129	long val;
130	long usec;
131
132	usec = 0;
133	val = MAXSTEP;
134#ifdef HAVE_GETCLOCK
135	(void) getclock(TIMEOFDAY, &ts);
136	tp.tv_sec = ts.tv_sec;
137	tp.tv_usec = ts.tv_nsec / 1000;
138#else /*  not HAVE_GETCLOCK */
139	GETTIMEOFDAY(&tp, &tzp);
140#endif /* not HAVE_GETCLOCK */
141	last = tp.tv_usec;
142	for (i = 0; i < MINLOOPS && usec < HUSECS;) {
143#ifdef HAVE_GETCLOCK
144		(void) getclock(TIMEOFDAY, &ts);
145		tp.tv_sec = ts.tv_sec;
146		tp.tv_usec = ts.tv_nsec / 1000;
147#else /*  not HAVE_GETCLOCK */
148		GETTIMEOFDAY(&tp, &tzp);
149#endif /* not HAVE_GETCLOCK */
150		diff = tp.tv_usec - last;
151		last = tp.tv_usec;
152		if (diff < 0)
153		    diff += DUSECS;
154		usec += diff;
155		if (diff > MINSTEP) {
156			i++;
157			if (diff < val)
158			    val = diff;
159		}
160	}
161	printf("precision  = %ld usec after %d loop%s\n",
162	       val, i, (i == 1) ? "" : "s");
163	if (usec >= HUSECS) {
164		printf("     (Boy this machine is fast ! usec was %ld)\n",
165		       usec);
166		val = MINSTEP;	/* val <= MINSTEP; fast machine */
167	}
168	diff = HUSECS;
169	for (i = 0; diff > val; i--)
170	    diff >>= 1;
171	return (i);
172}
173