home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1822 < prev    next >
Encoding:
Text File  |  1990-12-28  |  7.0 KB  |  202 lines

  1. Newsgroups: alt.sources
  2. From: hakanson@CSE.OGI.EDU (Marion Hakanson)
  3. Subject: [comp.protocols.time.ntp] Unix clock resolution revisited
  4. Message-ID: <1990Sep14.173427.14607@math.lsa.umich.edu>
  5. Date: Fri, 14 Sep 90 17:34:27 GMT
  6.  
  7. Archive-name: clockres/13-Sep-90
  8. Original-posting-by: hakanson@CSE.OGI.EDU (Marion Hakanson)
  9. Original-subject: Unix clock resolution revisited
  10. Reposted-by: emv@math.lsa.umich.edu (Edward Vielmetti)
  11.  
  12. [Reposted from comp.protocols.time.ntp.
  13. Comments on this service to emv@math.lsa.umich.edu (Edward Vielmetti).]
  14.  
  15. Well, I got tired of trying to convert microseconds into the power
  16. of two precision that ntpd and xntpd expect in their configuration
  17. files, so I added an option to my recently-posted clockres program
  18. to do the work for you.
  19.  
  20. This program turns out to be more useful than I thought.  On machines
  21. with clock resolutions better than clockres can measure (like the old
  22. SPARCstation-1), clockres ends up measuring the span of time between
  23. two gettimeofday(2) system calls.  For consumption of any single
  24. threaded user program (like ntpd and xntpd), this turns out to be the
  25. perceived resolution of the clock, even if the underlying hardware
  26. clock is better.  So the NTP precision value reported by clockres
  27. is probably a good value to tell ntpd or xntpd to advertise (and on
  28. most boxes, it agrees with the kernel hz value).
  29.  
  30. More trivia: A friend tells me that a Convex C2 shows up with a
  31. resolution of 25 microseconds (again, this is the time between two
  32. system calls -- the underlying hardware clock is better).  Who's next?
  33. Note that this is only three times better than a SPARC-1, but I hear
  34. you can do a lot more between system calls on a Convex.
  35.  
  36. The program is included below.  I'm still ambivalent about collecting
  37. results.  While I'm curious about various machines (especially
  38. "exotic" boxes or those with exceptional results), I'm not anxious to
  39. have everyone who has a Sun workstation or a [34]86 box sending me
  40. their results.  Certainly this program could be included with the
  41. other NTP programs in the FTP archives, as it may come in handy for
  42. those who have a lesser-known machine/OS, and it would save
  43. maintaining a database of reported results.  Have at it, Louie.
  44.  
  45. Hey, don't everybody run this at once -- you'll slow down the universe
  46. (especially if it runs SunOS :-).
  47.  
  48. -- 
  49. Marion Hakanson         Domain: hakanson@cse.ogi.edu
  50.                         UUCP  : {hp-pcd,tektronix}!ogicse!hakanson
  51.  
  52. ===============================================
  53. #ifndef lint
  54. char rcsid[] = "$Id: clockres.c,v 1.11 90/09/12 16:26:27 hakanson Exp $";
  55. #endif /* lint */
  56.  
  57. /*
  58.  * Determine the resolution of the system clock (gettimeofday(2)).
  59.  *   Marion Hakanson (hakanson@cse.ogi.edu)
  60.  *   Oregon Graduate Institute of Science and Technology
  61.  *
  62.  * The idea is to call gettimeofday(2), then repeatedly call it again
  63.  * until you get a different value.  The difference between the two
  64.  * values should be the resolution (precision) of the system clock.
  65.  * A little noise sometimes creeps in, due to adjtime(2)'s being done,
  66.  * but in practice this appears rarely and can be factored out by running
  67.  * this test program repeatedly (say 10 times).  There is a "-v" option
  68.  * to print out the two differing timestamps (and the number of calls
  69.  * it took before a difference was detected).  You may occasionally
  70.  * see two (but never more) of these printed out before a result is
  71.  * announced -- this is to avoid anomalies with comparing microseconds
  72.  * when a wrap to the next full second has occurred.  The "-n" option
  73.  * will convert the result to the NTP precision (power of two).
  74.  *
  75.  * The big flaw to this approach is that most 4.3bsd-based systems have
  76.  * in their kernels a microtime() routine which is supposed to return
  77.  * the current time to the microsecond.  A "good" implementation would
  78.  * (e.g.) read a hardware interval timer to determine how many usec's
  79.  * had passed since the last clock tick.  But much hardware seems to
  80.  * lack such a timer, and as a result, most implementations of microtime()
  81.  * fake it by adding a microsecond to the current time for each subsequent
  82.  * call to microtime() -- just to ensure that two invocations never return
  83.  * exactly the same value (the fake microseconds go away at the next clock
  84.  * tick) .  Since most machines (known to mortals) these days cannot make
  85.  * a system call in one microsecond, this program has a hack in it to
  86.  * detect these "fake" microtime() implementations by adding the number
  87.  * of gettimeofday(2) calls to the initial timestamp before comparing
  88.  * with the most recent timestamp.  A ">=" test is used to work with
  89.  * older bsd's (like 4.2), where two subsequent timestamps often are
  90.  * the same (within the value of a tick), and the "-f" option disables
  91.  * the "fake" microtime() workaround.  More irregular "fakes" will no
  92.  * doubt be reported as terrific (but erroneous) clock resolutions.
  93.  *
  94.  * A small flaw is that if a machine has a clock resolution finer than
  95.  * the time it takes to make a single pass through the loop, then the
  96.  * program reports that value as the resolution of the clock, rather
  97.  * than the actual resolution.  A warning about this is printed when
  98.  * a real difference is detected after only one pass (it says "... XX
  99.  * microseconds or better" instead of just "... XX microseconds").
  100.  * Again, repeated runs can show this condition if the value seems to
  101.  * fluctuate by a few microseconds and/or vary with system load (a
  102.  * "genuine" result seems to stay rock solid across invocations).
  103.  * In practice, few real machines seem to encounter this behavior,
  104.  * and you should count yourself lucky if you have one.
  105.  */
  106.  
  107. #include <stdio.h>
  108. #include <sys/time.h>
  109.  
  110. extern int getopt();
  111.  
  112. main(argc, argv)
  113.     int argc;  char *argv[];
  114. {
  115. register int fakemicro;
  116. int ntpprecision;
  117. int verbose;
  118. int again;
  119. struct timeval t1, t2;
  120. register int calls;
  121. unsigned long diff, quotient;
  122. int power;
  123. int c;
  124.  
  125.  
  126. verbose = 0;
  127. fakemicro = 1;
  128. ntpprecision = 0;
  129.  
  130. while ( (c = getopt(argc, argv, "fnv")) != EOF ) {
  131.   switch ( c ) {
  132.     case 'f':
  133.       fakemicro = 0;
  134.       break;
  135.     case 'n':
  136.       ntpprecision = 1;
  137.       break;
  138.     case 'v':
  139.       verbose = 1;
  140.       break;
  141.     case '?':
  142.       fprintf(stderr, "usage: %s [-fnv]\n", argv[0]);
  143.       exit(1);
  144.   }
  145. }
  146.    
  147.  
  148. again = 0;
  149.  
  150. top:
  151.  
  152. if (again > 1) {
  153.   fprintf (stderr, "%s: More than one second passed, giving up.\n", argv[0]);
  154.   exit(1);
  155. }
  156.  
  157. again++;
  158.  
  159. calls = 0;
  160.  
  161. (void) gettimeofday(&t1, NULL);
  162.  
  163. do {
  164.   calls++;
  165.   (void) gettimeofday(&t2, NULL);
  166. } while ( (t1.tv_sec == t2.tv_sec)
  167.      && ( fakemicro ? ((t1.tv_usec+calls) >= t2.tv_usec)
  168.              : (t1.tv_usec == t2.tv_usec) ) );
  169.  
  170. if (verbose) {
  171.   printf ("t1 %d.%06.6d t2 %d.%06.6d calls %d\n",
  172.     t1.tv_sec, t1.tv_usec, t2.tv_sec, t2.tv_usec, calls);
  173. }
  174.  
  175. /* Check for wrap to next second */
  176. if (t1.tv_sec != t2.tv_sec)
  177.   goto top;
  178.  
  179. diff = t2.tv_usec - t1.tv_usec;
  180.  
  181. printf ("Clock step resolution %d microseconds", diff);
  182.  
  183. if ( calls == 1 )
  184.   printf (" or better");
  185.  
  186. if ( ntpprecision ) {
  187.   quotient = 1000000;
  188.   power = 0;
  189.   
  190.   while ( quotient > diff ) {
  191.     quotient >>= 1;
  192.     --power;
  193.   }
  194.   printf (" (NTP precision %d)", power);
  195. }
  196.  
  197. printf("\n");
  198.  
  199. exit(0);
  200. }
  201. ===============================================
  202.