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