summaryrefslogtreecommitdiff
path: root/util/precision.c
blob: b1516b34cac28eeaa796dc6cdb975d6fae16edb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include "ntp_unixtime.h"

#include <stdio.h>

#define	DEFAULT_SYS_PRECISION	-99

int default_get_resolution();
int default_get_precision();

int
main(
	int argc,
	char *argv[]
	)
{
	printf("log2(resolution) = %d, log2(precision) = %d\n",
	       default_get_resolution(),
	       default_get_precision());
	return 0;
}

/* Find the resolution of the system clock by watching how the current time
 * changes as we read it repeatedly.
 *
 * struct timeval is only good to 1us, which may cause problems as machines
 * get faster, but until then the logic goes:
 *
 * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
 * probably use the "unused" low order bits as a counter (to force time to be
 * a strictly increaing variable), incrementing it each time any process
 * requests the time [[ or maybe time will stand still ? ]].
 *
 * SO: the logic goes:
 *
 *      IF      the difference from the last time is "small" (< MINSTEP)
 *      THEN    this machine is "counting" with the low order bits
 *      ELIF    this is not the first time round the loop
 *      THEN    this machine *WAS* counting, and has now stepped
 *      ELSE    this machine has resolution < time to read clock
 *
 * SO: if it exits on the first loop, assume "full accuracy" (1us)
 *     otherwise, take the log2(observered difference, rounded UP)
 *
 * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
 * and the first loop, it doesn't stop too early.
 * Making it even greater allows MINSTEP to be reduced, assuming that the
 * chance of MINSTEP-1 other processes getting in and calling gettimeofday
 * between this processes's calls.
 * Reducing MINSTEP may be necessary as this sets an upper bound for the time
 * to actually call gettimeofday.
 */

#define	DUSECS	1000000
#define	HUSECS	(1024 * 1024)
#define	MINSTEP	5	/* some systems increment uS on each call */
/* Don't use "1" as some *other* process may read too*/
/*We assume no system actually *ANSWERS* in this time*/
#define MAXSTEP 20000   /* maximum clock increment (us) */
#define MINLOOPS 5      /* minimum number of step samples */
#define	MAXLOOPS HUSECS	/* Assume precision < .1s ! */

int
default_get_resolution(void)
{
	struct timeval tp;
	struct timezone tzp;
	long last;
	int i;
	long diff;
	long val;
	int minsteps = MINLOOPS;	/* need at least this many steps */

	gettimeofday(&tp, &tzp);
	last = tp.tv_usec;
	for (i = - --minsteps; i< MAXLOOPS; i++) {
		gettimeofday(&tp, &tzp);
		diff = tp.tv_usec - last;
		if (diff < 0) diff += DUSECS;
		if (diff > MINSTEP) if (minsteps-- <= 0) break;
		last = tp.tv_usec;
	}

	printf("resolution = %ld usec after %d loop%s\n",
	       diff, i, (i==1) ? "" : "s");

	diff = (diff *3)/2;
	if (i >= MAXLOOPS) {
		printf(
			"     (Boy this machine is fast ! %d loops without a step)\n",
			MAXLOOPS);
		diff = 1; /* No STEP, so FAST machine */
	}
	if (i == 0) {
		printf(
			"     (The resolution is less than the time to read the clock -- Assume 1us)\n");
		diff = 1; /* time to read clock >= resolution */
	}
	for (i=0, val=HUSECS; val>0; i--, val >>= 1) if (diff >= val) return i;
	printf("     (Oh dear -- that wasn't expected ! I'll guess !)\n");
	return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
}

/* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */

/*
 * This routine calculates the differences between successive calls to
 * gettimeofday(). If a difference is less than zero, the us field
 * has rolled over to the next second, so we add a second in us. If
 * the difference is greater than zero and less than MINSTEP, the
 * clock has been advanced by a small amount to avoid standing still.
 * If the clock has advanced by a greater amount, then a timer interrupt
 * has occurred and this amount represents the precision of the clock.
 * In order to guard against spurious values, which could occur if we
 * happen to hit a fat interrupt, we do this for MINLOOPS times and
 * keep the minimum value obtained.
 */  
int
default_get_precision(void)
{
	struct timeval tp;
	struct timezone tzp;
#ifdef HAVE_GETCLOCK
	struct timespec ts;
#endif
	long last;
	int i;
	long diff;
	long val;
	long usec;

	usec = 0;
	val = MAXSTEP;
#ifdef HAVE_GETCLOCK
	(void) getclock(TIMEOFDAY, &ts);
	tp.tv_sec = ts.tv_sec;
	tp.tv_usec = ts.tv_nsec / 1000;
#else /*  not HAVE_GETCLOCK */
	GETTIMEOFDAY(&tp, &tzp);
#endif /* not HAVE_GETCLOCK */
	last = tp.tv_usec;
	for (i = 0; i < MINLOOPS && usec < HUSECS;) {
#ifdef HAVE_GETCLOCK
		(void) getclock(TIMEOFDAY, &ts);
		tp.tv_sec = ts.tv_sec;
		tp.tv_usec = ts.tv_nsec / 1000;
#else /*  not HAVE_GETCLOCK */
		GETTIMEOFDAY(&tp, &tzp);
#endif /* not HAVE_GETCLOCK */
		diff = tp.tv_usec - last;
		last = tp.tv_usec;
		if (diff < 0)
		    diff += DUSECS;
		usec += diff;
		if (diff > MINSTEP) {
			i++;
			if (diff < val)
			    val = diff;
		}
	}
	printf("precision  = %ld usec after %d loop%s\n",
	       val, i, (i == 1) ? "" : "s");
	if (usec >= HUSECS) {
		printf("     (Boy this machine is fast ! usec was %ld)\n",
		       usec);
		val = MINSTEP;	/* val <= MINSTEP; fast machine */
	}
	diff = HUSECS;
	for (i = 0; diff > val; i--)
	    diff >>= 1;
	return (i);
}