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