nanosecond: add test program t/time-test for experimenting with cpu clock ticks to...
[fio.git] / t / time-test.c
1 /*
2  * Carry out arithmetic to explore conversion of CPU clock ticks to nsec
3  *
4  * When we use the CPU clock for timing, we do the following:
5  *
6  * 1) Calibrate the CPU clock to relate the frequency of CPU clock ticks
7  *    to actual time.
8  *
9  *    Using gettimeofday() or clock_gettime(), count how many CPU clock
10  *    ticks occur per usec
11  *
12  * 2) Calculate conversion factors so that we can ultimately convert
13  *    from clocks ticks to nsec with
14  *      nsec = (ticks * clock_mult) >> clock_shift
15  *
16  *    This is equivalent to
17  *      nsec = ticks * (MULTIPLIER / cycles_per_nsec) / MULTIPLIER
18  *    where
19  *      clock_mult = MULTIPLIER / cycles_per_nsec
20  *      MULTIPLIER = 2^clock_shift
21  *
22  *    It would be simpler to just calculate nsec = ticks / cycles_per_nsec,
23  *    but all of this is necessary because of rounding when calculating
24  *    cycles_per_nsec. With a 3.0GHz CPU, cycles_per_nsec would simply
25  *    be 3. But with a 3.33GHz CPU or a 4.5GHz CPU, the fractional
26  *    portion is lost with integer arithmetic.
27  *
28  *    This multiply and shift calculation also has a performance benefit
29  *    as multiplication and bit shift operations are faster than integer
30  *    division.
31  *
32  * 3) Dynamically determine clock_shift and clock_mult at run time based
33  *    on MAX_CLOCK_SEC and cycles_per_usec. MAX_CLOCK_SEC is the maximum
34  *    duration for which the conversion will be valid.
35  *
36  *    The primary constraint is that (ticks * clock_mult) must not overflow
37  *    when ticks is at its maximum value.
38  *
39  *    So we have
40  *      max_ticks * clock_mult <= ULLONG_MAX
41  *      max_ticks * MULTIPLIER / cycles_per_nsec <= ULLONG_MAX
42  *      MULTIPLIER <= ULLONG_MAX / max_ticks * cycles_per_nsec
43  *
44  *    Then choose the largest clock_shift that satisfies
45  *      2^clock_shift <= ULLONG_MAX / max_ticks * cycles_per_nsec
46  *
47  *    Finally calculate the appropriate clock_mult associated with clock_shift
48  *      clock_mult = 2^clock_shift / cycles_per_nsec
49  *
50  * 4) In the code below we have cycles_per_usec and use
51  *      cycles_per_nsec = cycles_per_usec / 1000
52  *
53  */
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <limits.h>
58 #include <assert.h>
59 #include <stdlib.h>
60
61 #define DEBUG 0
62 #define MAX_CLOCK_SEC 365*24*60*60ULL
63 #define MAX_CLOCK_SEC64 60*60ULL
64 #define dprintf(...) if (DEBUG) { printf(__VA_ARGS__); }
65
66 enum {
67         __CLOCK_64_BIT          = 1 << 0,
68         __CLOCK_128_BIT         = 1 << 1,
69         __CLOCK_MULT_SHIFT      = 1 << 2,
70         __CLOCK_EMULATE_128     = 1 << 3,
71         __CLOCK_REDUCE          = 1 << 4,
72
73         CLOCK_64_MULT_SHIFT     = __CLOCK_64_BIT | __CLOCK_MULT_SHIFT,
74         CLOCK_64_EMULATE_128    = __CLOCK_64_BIT | __CLOCK_EMULATE_128,
75         CLOCK_64_2STAGE         = __CLOCK_64_BIT | __CLOCK_REDUCE,
76         CLOCK_128_MULT_SHIFT    = __CLOCK_128_BIT | __CLOCK_MULT_SHIFT,
77 };
78
79 unsigned int clock_shift;
80 unsigned int max_cycles_shift;
81 unsigned long long max_cycles_mask;
82 unsigned long long *nsecs;
83 unsigned long long clock_mult;
84 unsigned long long nsecs_for_max_cycles;
85 unsigned long long clock_mult64_128[2];
86 __uint128_t clock_mult128;
87
88
89 /*
90  * Functions for carrying out 128-bit
91  * arithmetic using 64-bit integers
92  *
93  * 128-bit integers are stored as
94  * arrays of two 64-bit integers
95  *
96  * Ordering is little endian
97  *
98  * a[0] has the less significant bits
99  * a[1] has the more significant bits
100  */
101 void do_mult(unsigned long long a[2], unsigned long long b, unsigned long long product[2])
102 {
103         product[0] = product[1] = 0;
104         return;
105 }
106
107 void do_div(unsigned long long a[2], unsigned long long b, unsigned long long c[2])
108 {
109         return;
110 }
111
112 void do_shift64(unsigned long long a[2], unsigned int count)
113 {
114         a[0] = a[1] >> (count-64);
115         a[1] = 0;
116 }
117
118 void do_shift(unsigned long long a[2], unsigned int count)
119 {
120         if (count > 64)
121                 do_shift64(a, count);
122         else
123                 while (count--) {
124                         a[0] >>= 1;
125                         a[0] |= a[1] << 63;
126                         a[1] >>= 1;
127                 }
128 }
129
130 unsigned long long get_nsec(int mode, unsigned long long t)
131 {
132         switch(mode) {
133                 case CLOCK_64_MULT_SHIFT: {
134                         return (t * clock_mult) >> clock_shift;
135                 }
136                 case CLOCK_64_EMULATE_128: {
137                         unsigned long long product[2];
138                         do_mult(clock_mult64_128, t, product);
139                         do_shift(product, clock_shift);
140                         return product[0];
141                 }
142                 case CLOCK_64_2STAGE: {
143                         unsigned long long multiples, nsec;
144                         multiples = t >> max_cycles_shift;
145                         dprintf("multiples=%llu\n", multiples);
146                         nsec = multiples * nsecs_for_max_cycles;
147                         nsec += ((t & max_cycles_mask) * clock_mult) >> clock_shift;
148                         return nsec;
149                 }
150                 case CLOCK_128_MULT_SHIFT: {
151                         return (unsigned long long)((t * clock_mult128) >> clock_shift);
152                 }
153                 default: {
154                         assert(0);
155                 }
156         }
157 }
158
159 void calc_mult_shift(int mode, void *mult, unsigned int *shift, unsigned long long max_sec, unsigned long long cycles_per_usec)
160 {
161         unsigned long long max_ticks;
162         max_ticks = max_sec * cycles_per_usec * 1000000ULL;
163
164         switch (mode) {
165                 case CLOCK_64_MULT_SHIFT: {
166                         unsigned long long max_mult, tmp;
167                         unsigned int sft = 0;
168
169                         /*
170                          * Calculate the largest multiplier that will not
171                          * produce a 64-bit overflow in the multiplication
172                          * step of the clock ticks to nsec conversion
173                          */
174                         max_mult = ULLONG_MAX / max_ticks;
175                         dprintf("max_ticks=%llu, __builtin_clzll=%d, max_mult=%llu\n", max_ticks, __builtin_clzll(max_ticks), max_mult);
176
177                         /*
178                          * Find the largest shift count that will produce
179                          * a multiplier less than max_mult
180                          */
181                         tmp = max_mult * cycles_per_usec / 1000;
182                         while (tmp > 1) {
183                                 tmp >>= 1;
184                                 sft++;
185                                 dprintf("tmp=%llu, sft=%u\n", tmp, sft);
186                         }
187
188                         *shift = sft;
189                         *((unsigned long long *)mult) = (unsigned long long) ((1ULL << sft) * 1000 / cycles_per_usec);
190                         break;
191                 }
192                 case CLOCK_64_EMULATE_128: {
193                         unsigned long long max_mult[2], tmp[2];
194                         unsigned int sft = 0;
195
196                         /*
197                          * Calculate the largest multiplier that will not
198                          * produce a 128-bit overflow in the multiplication
199                          * step of the clock ticks to nsec conversion,
200                          * but use only 64-bit integers in the process
201                          */
202                         max_mult[0] = max_mult[1] = ULLONG_MAX;
203                         do_div(max_mult, max_ticks, max_mult);
204                         dprintf("max_ticks=%llu, __builtin_clzll=%d, max_mult=0x%016llx%016llx\n",
205                                 max_ticks, __builtin_clzll(max_ticks), max_mult[1], max_mult[0]);
206
207                         /*
208                          * Find the largest shift count that will produce
209                          * a multiplier less than max_mult
210                          */
211                         do_div(max_mult, cycles_per_usec, tmp);
212                         do_div(tmp, 1000ULL, tmp);
213                         while (tmp[0] > 1 || tmp[1] > 1) {
214                                 do_shift(tmp, 1);
215                                 sft++;
216                                 dprintf("tmp=0x%016llx%016llx, sft=%u\n", tmp[1], tmp[0], sft);
217                         }
218
219                         *shift = sft;
220 //                      *((unsigned long long *)mult) = (__uint128_t) (((__uint128_t)1 << sft) * 1000 / cycles_per_usec);
221                         break;
222                 }
223                 case CLOCK_64_2STAGE: {
224                         unsigned long long tmp;
225 /*
226  * This clock tick to nsec conversion requires two stages.
227  *
228  * Stage 1: Determine how many ~MAX_CLOCK_SEC64 periods worth of clock ticks
229  *      have elapsed and set nsecs to the appropriate value for those
230  *      ~MAX_CLOCK_SEC64 periods.
231  * Stage 2: Subtract the ticks for the elapsed ~MAX_CLOCK_SEC64 periods from
232  *      Stage 1. Convert remaining clock ticks to nsecs and add to previously
233  *      set nsec value.
234  *
235  * To optimize the arithmetic operations, use the greatest power of 2 ticks
236  * less than the number of ticks in MAX_CLOCK_SEC64 seconds.
237  *
238  */
239                         // Use a period shorter than MAX_CLOCK_SEC here for better accuracy
240                         calc_mult_shift(CLOCK_64_MULT_SHIFT, mult, shift, MAX_CLOCK_SEC64, cycles_per_usec);
241
242                         // Find the greatest power of 2 clock ticks that is less than the ticks in MAX_CLOCK_SEC64
243                         max_cycles_shift = max_cycles_mask = 0;
244                         tmp = MAX_CLOCK_SEC64 * 1000000ULL * cycles_per_usec;
245                         dprintf("tmp=%llu, max_cycles_shift=%u\n", tmp, max_cycles_shift);
246                         while (tmp > 1) {
247                                 tmp >>= 1;
248                                 max_cycles_shift++;
249                                 dprintf("tmp=%llu, max_cycles_shift=%u\n", tmp, max_cycles_shift);
250                         }
251                         // if use use (1ULL << max_cycles_shift) * 1000 / cycles_per_usec here we will
252                         // have a discontinuity every (1ULL << max_cycles_shift) cycles
253                         nsecs_for_max_cycles = (1ULL << max_cycles_shift) * *((unsigned long long *)mult) >> *shift;
254
255                         // Use a bitmask to calculate ticks % (1ULL << max_cycles_shift)
256                         for (tmp = 0; tmp < max_cycles_shift; tmp++)
257                                 max_cycles_mask |= 1ULL << tmp;
258
259                         dprintf("max_cycles_shift=%u, 2^max_cycles_shift=%llu, nsecs_for_max_cycles=%llu, max_cycles_mask=%016llx\n",
260                                 max_cycles_shift, (1ULL << max_cycles_shift),
261                                 nsecs_for_max_cycles, max_cycles_mask);
262
263
264                         break;
265                 }
266                 case CLOCK_128_MULT_SHIFT: {
267                         __uint128_t max_mult, tmp;
268                         unsigned int sft = 0;
269
270                         /*
271                          * Calculate the largest multiplier that will not
272                          * produce a 128-bit overflow in the multiplication
273                          * step of the clock ticks to nsec conversion
274                          */
275                         max_mult = ((__uint128_t) ULLONG_MAX) << 64 | ULLONG_MAX;
276                         max_mult /= max_ticks;
277                         dprintf("max_ticks=%llu, __builtin_clzll=%d, max_mult=0x%016llx%016llx\n",
278                                 max_ticks, __builtin_clzll(max_ticks),
279                                 (unsigned long long) (max_mult >> 64),
280                                 (unsigned long long) max_mult);
281
282                         /*
283                          * Find the largest shift count that will produce
284                          * a multiplier less than max_mult
285                          */
286                         tmp = max_mult * cycles_per_usec / 1000;
287                         while (tmp > 1) {
288                                 tmp >>= 1;
289                                 sft++;
290                                 dprintf("tmp=0x%016llx%016llx, sft=%u\n",
291                                         (unsigned long long) (tmp >> 64),
292                                         (unsigned long long) tmp, sft);
293                         }
294
295                         *shift = sft;
296                         *((__uint128_t *)mult) = (__uint128_t) (((__uint128_t)1 << sft) * 1000 / cycles_per_usec);
297                         break;
298                 }
299         }
300 }
301
302 int discontinuity(int mode, int delta_ticks, int delta_nsec, unsigned long long start, unsigned long len)
303 {
304         int i;
305         unsigned long mismatches = 0, bad_mismatches = 0;
306         unsigned long long delta, max_mismatch = 0;
307         unsigned long long *ns = nsecs;
308
309         for (i = 0; i < len; ns++, i++) {
310                 *ns = get_nsec(mode, start + i);
311                 if (i - delta_ticks >= 0) {
312                         if (*ns > *(ns - delta_ticks))
313                                 delta = *ns - *(ns - delta_ticks);
314                         else
315                                 delta = *(ns - delta_ticks) - *ns;
316                         if (delta > delta_nsec)
317                                 delta -= delta_nsec;
318                         else
319                                 delta = delta_nsec - delta;
320                         if (delta) {
321                                 mismatches++;
322                                 if (delta > 1)
323                                         bad_mismatches++;
324                                 if (delta > max_mismatch)
325                                         max_mismatch = delta;
326                         }
327                 }
328                 if (!bad_mismatches)
329                         assert(max_mismatch == 0 || max_mismatch == 1);
330                 if (!mismatches)
331                         assert(max_mismatch == 0);
332         }
333
334         printf("%lu discontinuities (%lu%%) (%lu errors > 1ns, max delta = %lluns) for ticks = %llu...%llu\n",
335                 mismatches, (mismatches * 100) / len, bad_mismatches, max_mismatch, start,
336                 start + len - 1);
337         return mismatches;
338 }
339
340 #define MIN_TICKS 1ULL
341 #define LEN 1000000000ULL
342 #define NSEC_ONE_SEC 1000000000ULL
343 #define TESTLEN 9
344 long long test_clock(int mode, int cycles_per_usec, int fast_test, int quiet, int delta_ticks, int delta_nsec)
345 {
346         int i;
347         long long delta;
348         unsigned long long max_ticks;
349         unsigned long long nsecs;
350         void *mult;
351         unsigned long long test_ns[TESTLEN] =
352                         {NSEC_ONE_SEC, NSEC_ONE_SEC,
353                          NSEC_ONE_SEC, NSEC_ONE_SEC*60, NSEC_ONE_SEC*60*60,
354                          NSEC_ONE_SEC*60*60*2, NSEC_ONE_SEC*60*60*4,
355                          NSEC_ONE_SEC*60*60*8, NSEC_ONE_SEC*60*60*24};
356         unsigned long long test_ticks[TESTLEN];
357
358         max_ticks = MAX_CLOCK_SEC * (unsigned long long) cycles_per_usec * 1000000ULL;
359
360         switch(mode) {
361                 case CLOCK_64_MULT_SHIFT: {
362                         mult = &clock_mult;
363                         break;
364                 }
365                 case CLOCK_64_EMULATE_128: {
366                         mult = clock_mult64_128;
367                         break;
368                 }
369                 case CLOCK_64_2STAGE: {
370                         mult = &clock_mult;
371                         break;
372                 }
373                 case CLOCK_128_MULT_SHIFT: {
374                         mult = &clock_mult128;
375                         break;
376                 }
377         }
378         calc_mult_shift(mode, mult, &clock_shift, MAX_CLOCK_SEC, cycles_per_usec);
379         nsecs = get_nsec(mode, max_ticks);
380         delta = nsecs/1000000 - MAX_CLOCK_SEC*1000;
381
382         if (mode == CLOCK_64_2STAGE) {
383                 test_ns[0] = nsecs_for_max_cycles - 1;
384                 test_ns[1] = nsecs_for_max_cycles;
385                 test_ticks[0] = (1ULL << max_cycles_shift) - 1;
386                 test_ticks[1] = (1ULL << max_cycles_shift);
387
388                 for (i = 2; i < TESTLEN; i++)
389                         test_ticks[i] = test_ns[i] / 1000 * cycles_per_usec;
390         }
391         else {
392                 for (i = 0; i < TESTLEN; i++)
393                         test_ticks[i] = test_ns[i] / 1000 * cycles_per_usec;
394         }
395
396         if (!quiet) {
397                 printf("cycles_per_usec=%d, delta_ticks=%d, delta_nsec=%d, max_ticks=%llu, shift=%u, 2^shift=%llu\n",
398                         cycles_per_usec, delta_ticks, delta_nsec, max_ticks, clock_shift, (1ULL << clock_shift));
399                 switch(mode) {
400                         case CLOCK_64_2STAGE:
401                         case CLOCK_64_MULT_SHIFT: {
402                                 printf("clock_mult=%llu, clock_mult / 2^clock_shift=%f\n",
403                                         clock_mult, (double) clock_mult / (1ULL << clock_shift));
404                                 break;
405                         }
406                         case CLOCK_64_EMULATE_128: {
407                                 printf("clock_mult=0x%016llx%016llx\n",
408                                         clock_mult64_128[1], clock_mult64_128[0]);
409                                 break;
410                         }
411                         case CLOCK_128_MULT_SHIFT: {
412                                 printf("clock_mult=0x%016llx%016llx\n",
413                                         (unsigned long long) (clock_mult128 >> 64),
414                                         (unsigned long long) clock_mult128);
415                                 break;
416                         }
417                 }
418                 printf("get_nsec(max_ticks) = %lluns, should be %lluns, error<=abs(%lld)ms\n",
419                         nsecs, MAX_CLOCK_SEC*1000000000ULL, delta);
420         }
421
422         for (i = 0; i < TESTLEN; i++)
423         {
424                 nsecs = get_nsec(mode, test_ticks[i]);
425                 delta = nsecs > test_ns[i] ? nsecs - test_ns[i] : test_ns[i] - nsecs;
426                 if (!quiet || delta > 0)
427                         printf("get_nsec(%llu)=%llu, expected %llu, delta=%llu\n",
428                                 test_ticks[i], nsecs, test_ns[i], delta);
429         }
430
431         if (!fast_test) {
432                 discontinuity(mode, delta_ticks, delta_nsec, max_ticks - LEN + 1, LEN);
433                 discontinuity(mode, delta_ticks, delta_nsec, MIN_TICKS, LEN);
434         }
435
436         if (!quiet)
437                 printf("\n\n");
438
439         return delta;
440 }
441
442 int main(int argc, char *argv[])
443 {
444         int i, days;
445         long long error;
446         long long errors[10001];
447         double mean;
448
449         nsecs = malloc(LEN * sizeof(unsigned long long));
450         assert(nsecs != NULL);
451         days = MAX_CLOCK_SEC / 60 / 60 / 24;
452
453         test_clock(CLOCK_64_2STAGE, 3333, 1, 0, 0, 0);
454 //      test_clock(CLOCK_64_MULT_SHIFT, 3333, 1, 0, 0, 0);
455 //      test_clock(CLOCK_128_MULT_SHIFT, 3333, 1, 0, 0, 0);
456
457 // Test 3 different clock types from 1000 to 10000 MHz
458 // and calculate average error
459 /*
460         for (i = 1000, mean = 0.0; i <= 10000; i++) {
461                 error = test_clock(CLOCK_64_MULT_SHIFT, i, 1, 1, 0, 0);
462                 errors[i] = error > 0 ? error : -1LL * error;
463                 mean += (double) errors[i] / 9000;
464         }
465         printf("  64-bit average error per %d days: %fms\n", days, mean);
466
467         for (i = 1000, mean = 0.0; i <= 10000; i++) {
468                 error = test_clock(CLOCK_64_2STAGE, i, 1, 1, 0, 0);
469                 errors[i] = error > 0 ? error : -1LL * error;
470                 mean += (double) errors[i] / 9000;
471         }
472         printf("  64-bit two-stage average error per %d days: %fms\n", days, mean);
473
474         for (i = 1000, mean = 0.0; i <= 10000; i++) {
475                 error = test_clock(CLOCK_128_MULT_SHIFT, i, 1, 1, 0, 0);
476                 errors[i] = error > 0 ? error : -1LL * error;
477                 mean += (double) errors[i] / 9000;
478         }
479         printf(" 128-bit average error per %d days: %fms\n", days, mean);
480 */
481         test_clock(CLOCK_64_2STAGE, 1000, 1, 0, 1, 1);
482         test_clock(CLOCK_64_2STAGE, 1100, 1, 0, 11, 10);
483         test_clock(CLOCK_64_2STAGE, 3000, 1, 0, 3, 1);
484         test_clock(CLOCK_64_2STAGE, 3333, 1, 0, 3333, 1000);
485         test_clock(CLOCK_64_2STAGE, 3392, 1, 0, 424, 125);
486         test_clock(CLOCK_64_2STAGE, 4500, 1, 0, 9, 2);
487         test_clock(CLOCK_64_2STAGE, 5000, 1, 0, 5, 1);
488
489         free(nsecs);
490         return 0;
491 }