c11 safe string handling support
[vpp.git] / src / vppinfra / test_tw_timer.c
1 #include <vppinfra/time.h>
2 #include <vppinfra/cache.h>
3 #include <vppinfra/error.h>
4 #include <vppinfra/tw_timer_2t_1w_2048sl.h>
5 #include <vppinfra/tw_timer_16t_2w_512sl.h>
6 #include <vppinfra/tw_timer_4t_3w_256sl.h>
7 #include <vppinfra/tw_timer_1t_3w_1024sl_ov.h>
8
9 typedef struct
10 {
11   /** Handle returned from tw_start_timer */
12   u32 stop_timer_handle;
13
14   /** Test item should expire at this clock tick */
15   u64 expected_to_expire;
16 } tw_timer_test_elt_t;
17
18 typedef struct
19 {
20   /** Pool of test objects */
21   tw_timer_test_elt_t *test_elts;
22
23   /** The single-wheel */
24   tw_timer_wheel_2t_1w_2048sl_t single_wheel;
25
26   /** The double-wheel */
27   tw_timer_wheel_16t_2w_512sl_t double_wheel;
28
29   /* The triple wheel */
30   tw_timer_wheel_4t_3w_256sl_t triple_wheel;
31
32   /* The triple wheel with overflow vector */
33   tw_timer_wheel_1t_3w_1024sl_ov_t triple_ov_wheel;
34
35   /** random number seed */
36   u64 seed;
37
38   /** number of timers */
39   u32 ntimers;
40
41   /** number of "churn" iterations */
42   u32 niter;
43
44   /** number of clock ticks per churn iteration */
45   u32 ticks_per_iter;
46
47   /** cpu timer */
48   clib_time_t clib_time;
49 } tw_timer_test_main_t;
50
51 tw_timer_test_main_t tw_timer_test_main;
52
53 static void
54 run_single_wheel (tw_timer_wheel_2t_1w_2048sl_t * tw, u32 n_ticks)
55 {
56   u32 i;
57   f64 now = tw->last_run_time + 1.01;
58
59   for (i = 0; i < n_ticks; i++)
60     {
61       tw_timer_expire_timers_2t_1w_2048sl (tw, now);
62       now += 1.01;
63     }
64 }
65
66 static void
67 run_double_wheel (tw_timer_wheel_16t_2w_512sl_t * tw, u32 n_ticks)
68 {
69   u32 i;
70   f64 now = tw->last_run_time + 1.01;
71
72   for (i = 0; i < n_ticks; i++)
73     {
74       tw_timer_expire_timers_16t_2w_512sl (tw, now);
75       now += 1.01;
76     }
77 }
78
79 static void
80 run_triple_wheel (tw_timer_wheel_4t_3w_256sl_t * tw, u32 n_ticks)
81 {
82   u32 i;
83   f64 now = tw->last_run_time + 1.01;
84
85   for (i = 0; i < n_ticks; i++)
86     {
87       tw_timer_expire_timers_4t_3w_256sl (tw, now);
88       now += 1.01;
89     }
90 }
91
92 static void
93 run_triple_ov_wheel (tw_timer_wheel_1t_3w_1024sl_ov_t * tw, u32 n_ticks)
94 {
95   u32 i;
96   f64 now = tw->last_run_time + 1.01;
97
98   for (i = 0; i < n_ticks; i++)
99     {
100       tw_timer_expire_timers_1t_3w_1024sl_ov (tw, now);
101       now += 1.01;
102     }
103 }
104
105 static void
106 expired_timer_single_callback (u32 * expired_timers)
107 {
108   int i;
109   u32 pool_index, timer_id;
110   tw_timer_test_elt_t *e;
111   tw_timer_test_main_t *tm = &tw_timer_test_main;
112
113   for (i = 0; i < vec_len (expired_timers); i++)
114     {
115       pool_index = expired_timers[i] & 0x7FFFFFFF;
116       timer_id = expired_timers[i] >> 31;
117
118       ASSERT (timer_id == 1);
119
120       e = pool_elt_at_index (tm->test_elts, pool_index);
121
122       if (e->expected_to_expire != tm->single_wheel.current_tick)
123         {
124           fformat (stdout, "[%d] expired at %lld not %lld\n",
125                    e - tm->test_elts, tm->single_wheel.current_tick,
126                    e->expected_to_expire);
127         }
128       pool_put (tm->test_elts, e);
129     }
130 }
131
132 static void
133 expired_timer_double_callback (u32 * expired_timers)
134 {
135   int i;
136   u32 pool_index, timer_id;
137   tw_timer_test_elt_t *e;
138   tw_timer_test_main_t *tm = &tw_timer_test_main;
139
140   for (i = 0; i < vec_len (expired_timers); i++)
141     {
142       pool_index = expired_timers[i] & 0x0FFFFFFF;
143       timer_id = expired_timers[i] >> 28;
144
145       ASSERT (timer_id == 14);
146
147       e = pool_elt_at_index (tm->test_elts, pool_index);
148
149       if (e->expected_to_expire != tm->double_wheel.current_tick)
150         {
151           fformat (stdout, "[%d] expired at %lld not %lld\n",
152                    e - tm->test_elts, tm->double_wheel.current_tick,
153                    e->expected_to_expire);
154         }
155       pool_put (tm->test_elts, e);
156     }
157 }
158
159 static void
160 expired_timer_triple_callback (u32 * expired_timers)
161 {
162   int i;
163   u32 pool_index, timer_id;
164   tw_timer_test_elt_t *e;
165   tw_timer_test_main_t *tm = &tw_timer_test_main;
166
167   for (i = 0; i < vec_len (expired_timers); i++)
168     {
169       pool_index = expired_timers[i] & 0x3FFFFFFF;
170       timer_id = expired_timers[i] >> 30;
171
172       ASSERT (timer_id == 3);
173
174       e = pool_elt_at_index (tm->test_elts, pool_index);
175
176       if (e->expected_to_expire != tm->triple_wheel.current_tick)
177         {
178           fformat (stdout, "[%d] expired at %lld not %lld\n",
179                    e - tm->test_elts, tm->triple_wheel.current_tick,
180                    e->expected_to_expire);
181         }
182       pool_put (tm->test_elts, e);
183     }
184 }
185
186 static void
187 expired_timer_triple_ov_callback (u32 * expired_timers)
188 {
189   int i;
190   u32 pool_index;
191   tw_timer_test_elt_t *e;
192   tw_timer_test_main_t *tm = &tw_timer_test_main;
193
194   for (i = 0; i < vec_len (expired_timers); i++)
195     {
196       pool_index = expired_timers[i];
197
198       e = pool_elt_at_index (tm->test_elts, pool_index);
199
200       if (e->expected_to_expire != tm->triple_ov_wheel.current_tick)
201         {
202           fformat (stdout, "[%d] expired at %lld not %lld\n",
203                    e - tm->test_elts, tm->triple_ov_wheel.current_tick,
204                    e->expected_to_expire);
205         }
206       pool_put (tm->test_elts, e);
207     }
208 }
209
210 static clib_error_t *
211 test2_single (tw_timer_test_main_t * tm)
212 {
213   u32 i, j;
214   tw_timer_test_elt_t *e;
215   u32 initial_wheel_offset;
216   u64 expiration_time;
217   u32 max_expiration_time = 0;
218   u32 *deleted_indices = 0;
219   u32 adds = 0, deletes = 0;
220   f64 before, after;
221
222   clib_time_init (&tm->clib_time);
223
224   tw_timer_wheel_init_2t_1w_2048sl (&tm->single_wheel,
225                                     expired_timer_single_callback,
226                                     1.0 /* timer interval */ , ~0);
227
228   /* Prime offset */
229   initial_wheel_offset = 757;
230
231   run_single_wheel (&tm->single_wheel, initial_wheel_offset);
232
233   fformat (stdout, "initial wheel time %d, fast index %d\n",
234            tm->single_wheel.current_tick,
235            tm->single_wheel.current_index[TW_TIMER_RING_FAST]);
236
237   initial_wheel_offset = tm->single_wheel.current_tick;
238
239   fformat (stdout,
240            "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
241            tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
242
243   before = clib_time_now (&tm->clib_time);
244
245   /* Prime the pump */
246   for (i = 0; i < tm->ntimers; i++)
247     {
248       pool_get (tm->test_elts, e);
249       clib_memset (e, 0, sizeof (*e));
250
251       do
252         {
253           expiration_time = random_u64 (&tm->seed) & (2047);
254         }
255       while (expiration_time == 0);
256
257       if (expiration_time > max_expiration_time)
258         max_expiration_time = expiration_time;
259
260       e->expected_to_expire = expiration_time + initial_wheel_offset;
261       e->stop_timer_handle =
262         tw_timer_start_2t_1w_2048sl (&tm->single_wheel, e - tm->test_elts,
263                                      1 /* timer id */ ,
264                                      expiration_time);
265     }
266
267   adds += i;
268
269   for (i = 0; i < tm->niter; i++)
270     {
271       run_single_wheel (&tm->single_wheel, tm->ticks_per_iter);
272
273       j = 0;
274       vec_reset_length (deleted_indices);
275       /* *INDENT-OFF* */
276       pool_foreach (e, tm->test_elts,
277       ({
278         tw_timer_stop_2t_1w_2048sl (&tm->single_wheel, e->stop_timer_handle);
279         vec_add1 (deleted_indices, e - tm->test_elts);
280         if (++j >= tm->ntimers / 4)
281           goto del_and_re_add;
282       }));
283       /* *INDENT-ON* */
284
285     del_and_re_add:
286       for (j = 0; j < vec_len (deleted_indices); j++)
287         {
288           pool_put_index (tm->test_elts, deleted_indices[j]);
289         }
290
291       deletes += j;
292
293       for (j = 0; j < tm->ntimers / 4; j++)
294         {
295           pool_get (tm->test_elts, e);
296           clib_memset (e, 0, sizeof (*e));
297
298           do
299             {
300               expiration_time = random_u64 (&tm->seed) & (2047);
301             }
302           while (expiration_time == 0);
303
304           if (expiration_time > max_expiration_time)
305             max_expiration_time = expiration_time;
306
307           e->expected_to_expire =
308             expiration_time + tm->single_wheel.current_tick;
309           e->stop_timer_handle = tw_timer_start_2t_1w_2048sl
310             (&tm->single_wheel, e - tm->test_elts, 1 /* timer id */ ,
311              expiration_time);
312         }
313       adds += j;
314     }
315
316   vec_free (deleted_indices);
317
318   run_single_wheel (&tm->single_wheel, max_expiration_time + 1);
319
320   after = clib_time_now (&tm->clib_time);
321
322   fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
323            tm->single_wheel.current_tick);
324   fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
325            (after - before),
326            ((f64) adds + (f64) deletes +
327             (f64) tm->single_wheel.current_tick) / (after - before));
328
329   if (pool_elts (tm->test_elts))
330     fformat (stdout, "Note: %d elements remain in pool\n",
331              pool_elts (tm->test_elts));
332
333   /* *INDENT-OFF* */
334   pool_foreach (e, tm->test_elts,
335   ({
336     fformat (stdout, "[%d] expected to expire %d\n",
337              e - tm->test_elts,
338              e->expected_to_expire);
339   }));
340   /* *INDENT-ON* */
341
342   pool_free (tm->test_elts);
343   tw_timer_wheel_free_2t_1w_2048sl (&tm->single_wheel);
344   return 0;
345 }
346
347 static clib_error_t *
348 test2_double (tw_timer_test_main_t * tm)
349 {
350   u32 i, j;
351   tw_timer_test_elt_t *e;
352   u32 initial_wheel_offset;
353   u32 expiration_time;
354   u32 max_expiration_time = 0;
355   u32 *deleted_indices = 0;
356   u32 adds = 0, deletes = 0;
357   f64 before, after;
358
359   clib_time_init (&tm->clib_time);
360
361   tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
362                                     expired_timer_double_callback,
363                                     1.0 /* timer interval */ , ~0);
364
365   /* Prime offset */
366   initial_wheel_offset = 7577;
367
368   run_double_wheel (&tm->double_wheel, initial_wheel_offset);
369
370   fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n",
371            tm->double_wheel.current_tick,
372            tm->double_wheel.current_index[TW_TIMER_RING_FAST],
373            tm->double_wheel.current_index[TW_TIMER_RING_SLOW]);
374
375   initial_wheel_offset = tm->double_wheel.current_tick;
376
377   fformat (stdout,
378            "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
379            tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
380
381   before = clib_time_now (&tm->clib_time);
382
383   /* Prime the pump */
384   for (i = 0; i < tm->ntimers; i++)
385     {
386       pool_get (tm->test_elts, e);
387       clib_memset (e, 0, sizeof (*e));
388
389       do
390         {
391           expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
392         }
393       while (expiration_time == 0);
394
395       if (expiration_time > max_expiration_time)
396         max_expiration_time = expiration_time;
397
398       e->expected_to_expire = expiration_time + initial_wheel_offset;
399
400       e->stop_timer_handle =
401         tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts,
402                                      14 /* timer id */ ,
403                                      expiration_time);
404     }
405
406   adds += i;
407
408   for (i = 0; i < tm->niter; i++)
409     {
410       run_double_wheel (&tm->double_wheel, tm->ticks_per_iter);
411
412       j = 0;
413       vec_reset_length (deleted_indices);
414       /* *INDENT-OFF* */
415       pool_foreach (e, tm->test_elts,
416       ({
417         tw_timer_stop_16t_2w_512sl (&tm->double_wheel, e->stop_timer_handle);
418         vec_add1 (deleted_indices, e - tm->test_elts);
419         if (++j >= tm->ntimers / 4)
420           goto del_and_re_add;
421       }));
422       /* *INDENT-ON* */
423
424     del_and_re_add:
425       for (j = 0; j < vec_len (deleted_indices); j++)
426         pool_put_index (tm->test_elts, deleted_indices[j]);
427
428       deletes += j;
429
430       for (j = 0; j < tm->ntimers / 4; j++)
431         {
432           pool_get (tm->test_elts, e);
433           clib_memset (e, 0, sizeof (*e));
434
435           do
436             {
437               expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
438             }
439           while (expiration_time == 0);
440
441           if (expiration_time > max_expiration_time)
442             max_expiration_time = expiration_time;
443
444           e->expected_to_expire = expiration_time +
445             tm->double_wheel.current_tick;
446
447           e->stop_timer_handle = tw_timer_start_16t_2w_512sl
448             (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ ,
449              expiration_time);
450         }
451       adds += j;
452     }
453
454   vec_free (deleted_indices);
455
456   run_double_wheel (&tm->double_wheel, max_expiration_time + 1);
457
458   after = clib_time_now (&tm->clib_time);
459
460   fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
461            tm->double_wheel.current_tick);
462   fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
463            (after - before),
464            ((f64) adds + (f64) deletes +
465             (f64) tm->double_wheel.current_tick) / (after - before));
466
467   if (pool_elts (tm->test_elts))
468     fformat (stdout, "Note: %d elements remain in pool\n",
469              pool_elts (tm->test_elts));
470
471   /* *INDENT-OFF* */
472   pool_foreach (e, tm->test_elts,
473   ({
474     fformat (stdout, "[%d] expected to expire %d\n",
475              e - tm->test_elts,
476              e->expected_to_expire);
477   }));
478   /* *INDENT-ON* */
479
480   pool_free (tm->test_elts);
481   tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
482   return 0;
483 }
484
485 static u32
486 get_expiration_time (tw_timer_test_main_t * tm)
487 {
488   u32 expiration_time;
489   do
490     {
491       expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
492     }
493   while (expiration_time == 0);
494   return expiration_time;
495 }
496
497 static clib_error_t *
498 test2_double_updates (tw_timer_test_main_t * tm)
499 {
500   u32 i, j;
501   tw_timer_test_elt_t *e;
502   u32 initial_wheel_offset;
503   u32 expiration_time;
504   u32 max_expiration_time = 0, updates = 0;
505   f64 before, after;
506
507   clib_time_init (&tm->clib_time);
508   tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
509                                     expired_timer_double_callback,
510                                     1.0 /* timer interval */ , ~0);
511
512   /* Prime offset */
513   initial_wheel_offset = 7577;
514   run_double_wheel (&tm->double_wheel, initial_wheel_offset);
515   fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n",
516            tm->double_wheel.current_tick,
517            tm->double_wheel.current_index[TW_TIMER_RING_FAST],
518            tm->double_wheel.current_index[TW_TIMER_RING_SLOW]);
519
520   initial_wheel_offset = tm->double_wheel.current_tick;
521   fformat (stdout,
522            "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
523            tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
524
525   before = clib_time_now (&tm->clib_time);
526
527   /* Prime the pump */
528   for (i = 0; i < tm->ntimers; i++)
529     {
530       pool_get (tm->test_elts, e);
531       clib_memset (e, 0, sizeof (*e));
532
533       expiration_time = get_expiration_time (tm);
534       max_expiration_time = clib_max (expiration_time, max_expiration_time);
535
536       e->expected_to_expire = expiration_time + initial_wheel_offset;
537       e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel,
538                                                           e - tm->test_elts,
539                                                           14 /* timer id */ ,
540                                                           expiration_time);
541     }
542
543   for (i = 0; i < tm->niter; i++)
544     {
545       run_double_wheel (&tm->double_wheel, tm->ticks_per_iter);
546
547       j = 0;
548
549       /* *INDENT-OFF* */
550       pool_foreach (e, tm->test_elts,
551       ({
552         expiration_time = get_expiration_time (tm);
553         max_expiration_time = clib_max (expiration_time, max_expiration_time);
554         e->expected_to_expire = expiration_time
555                                 + tm->double_wheel.current_tick;
556         tw_timer_update_16t_2w_512sl (&tm->double_wheel, e->stop_timer_handle,
557                                       expiration_time);
558         if (++j >= tm->ntimers / 4)
559           goto done;
560       }));
561       /* *INDENT-ON* */
562
563     done:
564       updates += j;
565     }
566
567   run_double_wheel (&tm->double_wheel, max_expiration_time + 1);
568
569   after = clib_time_now (&tm->clib_time);
570
571   fformat (stdout, "%d updates, %d ticks\n", updates,
572            tm->double_wheel.current_tick);
573   fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
574            (after - before),
575            ((f64) updates + (f64) tm->double_wheel.current_tick) / (after -
576                                                                     before));
577
578   if (pool_elts (tm->test_elts))
579     fformat (stdout, "Note: %d elements remain in pool\n",
580              pool_elts (tm->test_elts));
581
582   /* *INDENT-OFF* */
583   pool_foreach (e, tm->test_elts,
584   ({
585     fformat (stdout, "[%d] expected to expire %d\n",
586              e - tm->test_elts,
587              e->expected_to_expire);
588   }));
589   /* *INDENT-ON* */
590
591   pool_free (tm->test_elts);
592   tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
593   return 0;
594 }
595
596 static clib_error_t *
597 test2_triple (tw_timer_test_main_t * tm)
598 {
599   u32 i, j;
600   tw_timer_test_elt_t *e;
601   u32 initial_wheel_offset = 0;
602   u32 expiration_time;
603   u32 max_expiration_time = 0;
604   u32 *deleted_indices = 0;
605   u32 adds = 0, deletes = 0;
606   f64 before, after;
607
608   clib_time_init (&tm->clib_time);
609
610   tw_timer_wheel_init_4t_3w_256sl (&tm->triple_wheel,
611                                    expired_timer_triple_callback,
612                                    1.0 /* timer interval */ , ~0);
613
614
615   /* Prime offset */
616   initial_wheel_offset = 75700;
617   run_triple_wheel (&tm->triple_wheel, initial_wheel_offset);
618
619   fformat (stdout,
620            "initial wheel time %d, fi %d si %d gi %d\n",
621            tm->triple_wheel.current_tick,
622            tm->triple_wheel.current_index[TW_TIMER_RING_FAST],
623            tm->triple_wheel.current_index[TW_TIMER_RING_SLOW],
624            tm->triple_wheel.current_index[TW_TIMER_RING_GLACIER]);
625
626   initial_wheel_offset = tm->triple_wheel.current_tick;
627
628   fformat (stdout,
629            "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
630            tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
631
632   before = clib_time_now (&tm->clib_time);
633
634   /* Prime the pump */
635   for (i = 0; i < tm->ntimers; i++)
636     {
637       pool_get (tm->test_elts, e);
638       clib_memset (e, 0, sizeof (*e));
639
640       do
641         {
642           expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
643         }
644       while (expiration_time == 0);
645
646       if (expiration_time > max_expiration_time)
647         max_expiration_time = expiration_time;
648
649       e->expected_to_expire = expiration_time + initial_wheel_offset;
650
651       e->stop_timer_handle =
652         tw_timer_start_4t_3w_256sl (&tm->triple_wheel, e - tm->test_elts,
653                                     3 /* timer id */ ,
654                                     expiration_time);
655     }
656
657   adds += i;
658
659   for (i = 0; i < tm->niter; i++)
660     {
661       run_triple_wheel (&tm->triple_wheel, tm->ticks_per_iter);
662
663       j = 0;
664       vec_reset_length (deleted_indices);
665       /* *INDENT-OFF* */
666       pool_foreach (e, tm->test_elts,
667       ({
668         tw_timer_stop_4t_3w_256sl (&tm->triple_wheel, e->stop_timer_handle);
669         vec_add1 (deleted_indices, e - tm->test_elts);
670         if (++j >= tm->ntimers / 4)
671           goto del_and_re_add;
672       }));
673       /* *INDENT-ON* */
674
675     del_and_re_add:
676       for (j = 0; j < vec_len (deleted_indices); j++)
677         pool_put_index (tm->test_elts, deleted_indices[j]);
678
679       deletes += j;
680
681       for (j = 0; j < tm->ntimers / 4; j++)
682         {
683           pool_get (tm->test_elts, e);
684           clib_memset (e, 0, sizeof (*e));
685
686           do
687             {
688               expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
689             }
690           while (expiration_time == 0);
691
692           if (expiration_time > max_expiration_time)
693             max_expiration_time = expiration_time;
694
695           e->expected_to_expire = expiration_time +
696             tm->triple_wheel.current_tick;
697
698           e->stop_timer_handle = tw_timer_start_4t_3w_256sl
699             (&tm->triple_wheel, e - tm->test_elts, 3 /* timer id */ ,
700              expiration_time);
701         }
702       adds += j;
703     }
704
705   vec_free (deleted_indices);
706
707   run_triple_wheel (&tm->triple_wheel, max_expiration_time + 1);
708
709   after = clib_time_now (&tm->clib_time);
710
711   fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
712            tm->triple_wheel.current_tick);
713   fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
714            (after - before),
715            ((f64) adds + (f64) deletes +
716             (f64) tm->triple_wheel.current_tick) / (after - before));
717
718   if (pool_elts (tm->test_elts))
719     fformat (stdout, "Note: %d elements remain in pool\n",
720              pool_elts (tm->test_elts));
721
722   /* *INDENT-OFF* */
723   pool_foreach (e, tm->test_elts,
724   ({
725     fformat (stdout, "[%d] expected to expire %d\n",
726              e - tm->test_elts,
727              e->expected_to_expire);
728   }));
729   /* *INDENT-ON* */
730
731   pool_free (tm->test_elts);
732   tw_timer_wheel_free_4t_3w_256sl (&tm->triple_wheel);
733   return 0;
734 }
735
736 static clib_error_t *
737 test2_triple_ov (tw_timer_test_main_t * tm)
738 {
739   u32 i, j;
740   tw_timer_test_elt_t *e;
741   u32 initial_wheel_offset = 0;
742   u32 expiration_time;
743   u32 max_expiration_time = 0;
744   u32 *deleted_indices = 0;
745   u32 adds = 0, deletes = 0;
746   f64 before, after;
747
748   clib_time_init (&tm->clib_time);
749
750   tw_timer_wheel_init_1t_3w_1024sl_ov (&tm->triple_ov_wheel,
751                                        expired_timer_triple_ov_callback,
752                                        1.0 /* timer interval */ , ~0);
753
754
755   /* Prime offset */
756   initial_wheel_offset = 75700;
757   run_triple_ov_wheel (&tm->triple_ov_wheel, initial_wheel_offset);
758
759   fformat (stdout,
760            "initial wheel time %d, fi %d si %d gi %d\n",
761            tm->triple_ov_wheel.current_tick,
762            tm->triple_ov_wheel.current_index[TW_TIMER_RING_FAST],
763            tm->triple_ov_wheel.current_index[TW_TIMER_RING_SLOW],
764            tm->triple_ov_wheel.current_index[TW_TIMER_RING_GLACIER]);
765
766   initial_wheel_offset = tm->triple_ov_wheel.current_tick;
767
768   fformat (stdout,
769            "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
770            tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
771
772   before = clib_time_now (&tm->clib_time);
773
774   /* Prime the pump */
775   for (i = 0; i < tm->ntimers; i++)
776     {
777       pool_get (tm->test_elts, e);
778       clib_memset (e, 0, sizeof (*e));
779
780       do
781         {
782           expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
783         }
784       while (expiration_time == 0);
785
786       if (expiration_time > max_expiration_time)
787         max_expiration_time = expiration_time;
788
789       e->expected_to_expire = expiration_time + initial_wheel_offset;
790
791       e->stop_timer_handle =
792         tw_timer_start_1t_3w_1024sl_ov (&tm->triple_ov_wheel,
793                                         e - tm->test_elts, 0 /* timer id */ ,
794                                         expiration_time);
795     }
796
797   adds += i;
798
799   for (i = 0; i < tm->niter; i++)
800     {
801       run_triple_ov_wheel (&tm->triple_ov_wheel, tm->ticks_per_iter);
802
803       j = 0;
804       vec_reset_length (deleted_indices);
805       /* *INDENT-OFF* */
806       pool_foreach (e, tm->test_elts,
807       ({
808         tw_timer_stop_1t_3w_1024sl_ov (&tm->triple_ov_wheel,
809                                        e->stop_timer_handle);
810         vec_add1 (deleted_indices, e - tm->test_elts);
811         if (++j >= tm->ntimers / 4)
812           goto del_and_re_add;
813       }));
814       /* *INDENT-ON* */
815
816     del_and_re_add:
817       for (j = 0; j < vec_len (deleted_indices); j++)
818         pool_put_index (tm->test_elts, deleted_indices[j]);
819
820       deletes += j;
821
822       for (j = 0; j < tm->ntimers / 4; j++)
823         {
824           pool_get (tm->test_elts, e);
825           clib_memset (e, 0, sizeof (*e));
826
827           do
828             {
829               expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1);
830             }
831           while (expiration_time == 0);
832
833           if (expiration_time > max_expiration_time)
834             max_expiration_time = expiration_time;
835
836           e->expected_to_expire = expiration_time +
837             tm->triple_ov_wheel.current_tick;
838
839           e->stop_timer_handle = tw_timer_start_1t_3w_1024sl_ov
840             (&tm->triple_ov_wheel, e - tm->test_elts, 0 /* timer id */ ,
841              expiration_time);
842         }
843       adds += j;
844     }
845
846   vec_free (deleted_indices);
847
848   run_triple_ov_wheel (&tm->triple_ov_wheel, max_expiration_time + 1);
849
850   after = clib_time_now (&tm->clib_time);
851
852   fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
853            tm->triple_ov_wheel.current_tick);
854   fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
855            (after - before),
856            ((f64) adds + (f64) deletes +
857             (f64) tm->triple_ov_wheel.current_tick) / (after - before));
858
859   if (pool_elts (tm->test_elts))
860     fformat (stdout, "Note: %d elements remain in pool\n",
861              pool_elts (tm->test_elts));
862
863   /* *INDENT-OFF* */
864   pool_foreach (e, tm->test_elts,
865   ({
866     TWT (tw_timer) * t;
867
868     fformat (stdout, "[%d] expected to expire %d\n",
869              e - tm->test_elts,
870              e->expected_to_expire);
871     t = pool_elt_at_index (tm->triple_ov_wheel.timers, e->stop_timer_handle);
872     fformat (stdout, "  expiration_time %lld\n", t->expiration_time);
873   }));
874   /* *INDENT-ON* */
875
876   pool_free (tm->test_elts);
877   tw_timer_wheel_free_1t_3w_1024sl_ov (&tm->triple_ov_wheel);
878   return 0;
879 }
880
881 static clib_error_t *
882 test1_single (tw_timer_test_main_t * tm)
883 {
884   u32 i;
885   tw_timer_test_elt_t *e;
886   u32 offset;
887
888   tw_timer_wheel_init_2t_1w_2048sl (&tm->single_wheel,
889                                     expired_timer_single_callback,
890                                     1.0 /* timer interval */ , ~0);
891
892   /*
893    * Prime offset, to make sure that the wheel starts in a
894    * non-trivial position
895    */
896   offset = 123;
897
898   run_single_wheel (&tm->single_wheel, offset);
899
900   fformat (stdout, "initial wheel time %d, fast index %d\n",
901            tm->single_wheel.current_tick,
902            tm->single_wheel.current_index[TW_TIMER_RING_FAST]);
903
904   offset = tm->single_wheel.current_tick;
905
906   for (i = 0; i < tm->ntimers; i++)
907     {
908       u32 expected_to_expire;
909       u32 timer_arg;
910
911       timer_arg = 1 + i;
912       timer_arg &= 2047;
913       if (timer_arg == 0)
914         timer_arg = 1;
915
916       expected_to_expire = timer_arg + offset;
917
918       pool_get (tm->test_elts, e);
919       clib_memset (e, 0, sizeof (*e));
920       e->expected_to_expire = expected_to_expire;
921       e->stop_timer_handle = tw_timer_start_2t_1w_2048sl
922         (&tm->single_wheel, e - tm->test_elts, 1 /* timer id */ ,
923          timer_arg);
924     }
925   run_single_wheel (&tm->single_wheel, tm->ntimers + 3);
926
927   if (pool_elts (tm->test_elts))
928     fformat (stdout, "Note: %d elements remain in pool\n",
929              pool_elts (tm->test_elts));
930
931   /* *INDENT-OFF* */
932   pool_foreach (e, tm->test_elts,
933   ({
934     fformat(stdout, "[%d] expected to expire %d\n",
935                      e - tm->test_elts,
936                      e->expected_to_expire);
937   }));
938   /* *INDENT-ON* */
939
940   fformat (stdout,
941            "final wheel time %d, fast index %d\n",
942            tm->single_wheel.current_tick,
943            tm->single_wheel.current_index[TW_TIMER_RING_FAST]);
944
945   pool_free (tm->test_elts);
946   tw_timer_wheel_free_2t_1w_2048sl (&tm->single_wheel);
947   return 0;
948 }
949
950 static clib_error_t *
951 test1_double (tw_timer_test_main_t * tm)
952 {
953   u32 i;
954   tw_timer_test_elt_t *e;
955   u32 offset;
956
957   tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
958                                     expired_timer_double_callback,
959                                     1.0 /* timer interval */ , ~0);
960
961   /*
962    * Prime offset, to make sure that the wheel starts in a
963    * non-trivial position
964    */
965   offset = 227989;
966
967   run_double_wheel (&tm->double_wheel, offset);
968
969   fformat (stdout, "initial wheel time %d, fast index %d\n",
970            tm->double_wheel.current_tick,
971            tm->double_wheel.current_index[TW_TIMER_RING_FAST]);
972
973   for (i = 0; i < tm->ntimers; i++)
974     {
975       pool_get (tm->test_elts, e);
976       clib_memset (e, 0, sizeof (*e));
977
978       e->expected_to_expire = i + offset + 1;
979       e->stop_timer_handle = tw_timer_start_16t_2w_512sl
980         (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ ,
981          i + 1);
982     }
983   run_double_wheel (&tm->double_wheel, tm->ntimers + 3);
984
985   if (pool_elts (tm->test_elts))
986     fformat (stdout, "Note: %d elements remain in pool\n",
987              pool_elts (tm->test_elts));
988
989   /* *INDENT-OFF* */
990   pool_foreach (e, tm->test_elts,
991   ({
992     fformat(stdout, "[%d] expected to expire %d\n",
993                      e - tm->test_elts,
994                      e->expected_to_expire);
995   }));
996   /* *INDENT-ON* */
997
998   fformat (stdout,
999            "final wheel time %d, fast index %d\n",
1000            tm->double_wheel.current_tick,
1001            tm->double_wheel.current_index[TW_TIMER_RING_FAST]);
1002
1003   pool_free (tm->test_elts);
1004   tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
1005   return 0;
1006 }
1007
1008 static clib_error_t *
1009 test3_triple_double (tw_timer_test_main_t * tm)
1010 {
1011   tw_timer_test_elt_t *e;
1012   u32 initial_wheel_offset = 0;
1013   u32 expiration_time;
1014   u32 max_expiration_time = 0;
1015   u32 adds = 0, deletes = 0;
1016   f64 before, after;
1017
1018   clib_time_init (&tm->clib_time);
1019
1020   tw_timer_wheel_init_4t_3w_256sl (&tm->triple_wheel,
1021                                    expired_timer_triple_callback,
1022                                    1.0 /* timer interval */ , ~0);
1023
1024   initial_wheel_offset = 0;
1025   run_triple_wheel (&tm->triple_wheel, initial_wheel_offset);
1026
1027   fformat (stdout,
1028            "initial wheel time %d, fi %d si %d gi %d\n",
1029            tm->triple_wheel.current_tick,
1030            tm->triple_wheel.current_index[TW_TIMER_RING_FAST],
1031            tm->triple_wheel.current_index[TW_TIMER_RING_SLOW],
1032            tm->triple_wheel.current_index[TW_TIMER_RING_GLACIER]);
1033
1034   initial_wheel_offset = tm->triple_wheel.current_tick;
1035
1036   fformat (stdout, "Create a timer which expires at wheel-time (1, 0, 0)\n");
1037
1038   before = clib_time_now (&tm->clib_time);
1039
1040   /* Prime the pump */
1041   pool_get (tm->test_elts, e);
1042   clib_memset (e, 0, sizeof (*e));
1043
1044   /* 1 glacier ring tick from now */
1045   expiration_time = TW_SLOTS_PER_RING * TW_SLOTS_PER_RING;
1046   e->expected_to_expire = expiration_time + initial_wheel_offset;
1047   max_expiration_time = expiration_time;
1048
1049   e->stop_timer_handle =
1050     tw_timer_start_4t_3w_256sl (&tm->triple_wheel, e - tm->test_elts,
1051                                 3 /* timer id */ ,
1052                                 expiration_time);
1053
1054   run_triple_wheel (&tm->triple_wheel, max_expiration_time + 1);
1055
1056   after = clib_time_now (&tm->clib_time);
1057
1058   fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
1059            tm->triple_wheel.current_tick);
1060   fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
1061            (after - before),
1062            ((f64) adds + (f64) deletes +
1063             (f64) tm->triple_wheel.current_tick) / (after - before));
1064
1065   if (pool_elts (tm->test_elts))
1066     fformat (stdout, "Note: %d elements remain in pool\n",
1067              pool_elts (tm->test_elts));
1068
1069   /* *INDENT-OFF* */
1070   pool_foreach (e, tm->test_elts,
1071   ({
1072     fformat (stdout, "[%d] expected to expire %d\n",
1073              e - tm->test_elts,
1074              e->expected_to_expire);
1075   }));
1076   /* *INDENT-ON* */
1077
1078   pool_free (tm->test_elts);
1079   tw_timer_wheel_free_4t_3w_256sl (&tm->triple_wheel);
1080   return 0;
1081 }
1082
1083 static clib_error_t *
1084 test4_double_double (tw_timer_test_main_t * tm)
1085 {
1086   u32 i;
1087   tw_timer_test_elt_t *e;
1088   u32 initial_wheel_offset;
1089   u32 expiration_time;
1090   u32 max_expiration_time = 0;
1091   u32 *deleted_indices = 0;
1092   u32 adds = 0, deletes = 0;
1093   f64 before, after;
1094
1095   clib_time_init (&tm->clib_time);
1096
1097   tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
1098                                     expired_timer_double_callback,
1099                                     1.0 /* timer interval */ , ~0);
1100   /* Prime offset */
1101   initial_wheel_offset = 0;
1102
1103   run_double_wheel (&tm->double_wheel, initial_wheel_offset);
1104
1105   fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n",
1106            tm->double_wheel.current_tick,
1107            tm->double_wheel.current_index[TW_TIMER_RING_FAST],
1108            tm->double_wheel.current_index[TW_TIMER_RING_SLOW]);
1109
1110   initial_wheel_offset = tm->double_wheel.current_tick;
1111
1112   fformat (stdout, "test timer which expires at 512 ticks\n");
1113
1114   before = clib_time_now (&tm->clib_time);
1115
1116   /* Prime the pump */
1117   for (i = 0; i < tm->ntimers; i++)
1118     {
1119       pool_get (tm->test_elts, e);
1120       clib_memset (e, 0, sizeof (*e));
1121
1122       expiration_time = 512;
1123
1124       if (expiration_time > max_expiration_time)
1125         max_expiration_time = expiration_time;
1126
1127       e->expected_to_expire = expiration_time + initial_wheel_offset;
1128       e->stop_timer_handle =
1129         tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts,
1130                                      14 /* timer id */ ,
1131                                      expiration_time);
1132     }
1133
1134   adds = 1;
1135
1136   vec_free (deleted_indices);
1137
1138   run_double_wheel (&tm->double_wheel, max_expiration_time + 1);
1139
1140   after = clib_time_now (&tm->clib_time);
1141
1142   fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
1143            tm->double_wheel.current_tick);
1144   fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
1145            (after - before),
1146            ((f64) adds + (f64) deletes +
1147             (f64) tm->double_wheel.current_tick) / (after - before));
1148
1149   if (pool_elts (tm->test_elts))
1150     fformat (stdout, "Note: %d elements remain in pool\n",
1151              pool_elts (tm->test_elts));
1152
1153   /* *INDENT-OFF* */
1154   pool_foreach (e, tm->test_elts,
1155   ({
1156     fformat (stdout, "[%d] expected to expire %d\n",
1157              e - tm->test_elts,
1158              e->expected_to_expire);
1159   }));
1160   /* *INDENT-ON* */
1161
1162   pool_free (tm->test_elts);
1163   tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
1164   return 0;
1165 }
1166
1167 static clib_error_t *
1168 test5_double (tw_timer_test_main_t * tm)
1169 {
1170   u32 i;
1171   tw_timer_test_elt_t *e;
1172   u32 initial_wheel_offset;
1173   u32 expiration_time;
1174   u32 max_expiration_time = 0;
1175   u32 adds = 0, deletes = 0;
1176   f64 before, after;
1177
1178   clib_time_init (&tm->clib_time);
1179
1180   tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel,
1181                                     expired_timer_double_callback,
1182                                     1.0 /* timer interval */ , ~0);
1183
1184   /* Prime offset */
1185   initial_wheel_offset = 7567;
1186
1187   run_double_wheel (&tm->double_wheel, initial_wheel_offset);
1188
1189   fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n",
1190            tm->double_wheel.current_tick,
1191            tm->double_wheel.current_index[TW_TIMER_RING_FAST],
1192            tm->double_wheel.current_index[TW_TIMER_RING_SLOW]);
1193
1194   initial_wheel_offset = tm->double_wheel.current_tick;
1195
1196   fformat (stdout,
1197            "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n",
1198            tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed);
1199
1200   before = clib_time_now (&tm->clib_time);
1201
1202   /* Prime the pump */
1203   for (i = 0; i < tm->ntimers; i++)
1204     {
1205       pool_get (tm->test_elts, e);
1206       clib_memset (e, 0, sizeof (*e));
1207
1208       expiration_time = i + 1;
1209
1210       if (expiration_time > max_expiration_time)
1211         max_expiration_time = expiration_time;
1212
1213       e->expected_to_expire = expiration_time + initial_wheel_offset;
1214       e->stop_timer_handle =
1215         tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts,
1216                                      14 /* timer id */ ,
1217                                      expiration_time);
1218     }
1219
1220   adds += i;
1221
1222   run_double_wheel (&tm->double_wheel, max_expiration_time + 1);
1223
1224   after = clib_time_now (&tm->clib_time);
1225
1226   fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes,
1227            tm->double_wheel.current_tick);
1228   fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n",
1229            (after - before),
1230            ((f64) adds + (f64) deletes +
1231             (f64) tm->double_wheel.current_tick) / (after - before));
1232
1233   if (pool_elts (tm->test_elts))
1234     fformat (stdout, "Note: %d elements remain in pool\n",
1235              pool_elts (tm->test_elts));
1236
1237   /* *INDENT-OFF* */
1238   pool_foreach (e, tm->test_elts,
1239   ({
1240     fformat (stdout, "[%d] expected to expire %d\n",
1241              e - tm->test_elts,
1242              e->expected_to_expire);
1243   }));
1244   /* *INDENT-ON* */
1245
1246   pool_free (tm->test_elts);
1247   tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel);
1248   return 0;
1249 }
1250
1251 static clib_error_t *
1252 timer_test_command_fn (tw_timer_test_main_t * tm, unformat_input_t * input)
1253 {
1254
1255   int is_test1 = 0, is_updates = 0;
1256   int num_wheels = 1;
1257   int is_test2 = 0;
1258   int is_test3 = 0;
1259   int is_test4 = 0;
1260   int is_test5 = 0;
1261   int overflow = 0;
1262
1263   clib_memset (tm, 0, sizeof (*tm));
1264   /* Default values */
1265   tm->ntimers = 100000;
1266   tm->seed = 0xDEADDABEB00BFACE;
1267   tm->niter = 1000;
1268   tm->ticks_per_iter = 727;
1269
1270   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1271     {
1272       if (unformat (input, "seed %lld", &tm->seed))
1273         ;
1274       else if (unformat (input, "test1"))
1275         is_test1 = 1;
1276       else if (unformat (input, "test2"))
1277         is_test2 = 1;
1278       else if (unformat (input, "overflow"))
1279         overflow = 1;
1280       else if (unformat (input, "lebron"))
1281         is_test3 = 1;
1282       else if (unformat (input, "wilt"))
1283         is_test4 = 1;
1284       else if (unformat (input, "linear"))
1285         is_test5 = 1;
1286       else if (unformat (input, "updates"))
1287         is_updates = 1;
1288       else if (unformat (input, "wheels %d", &num_wheels))
1289         ;
1290       else if (unformat (input, "ntimers %d", &tm->ntimers))
1291         ;
1292       else if (unformat (input, "niter %d", &tm->niter))
1293         ;
1294       else if (unformat (input, "ticks_per_iter %d", &tm->ticks_per_iter))
1295         ;
1296       else
1297         break;
1298     }
1299
1300   if (is_test1 + is_test2 + is_test3 + is_test4 + is_test5 == 0)
1301     return clib_error_return (0, "No test specified [test1..n]");
1302
1303   if (num_wheels < 1 || num_wheels > 3)
1304     return clib_error_return (0, "unsupported... 1 or 2 wheels only");
1305
1306   if (is_test1)
1307     {
1308       if (num_wheels == 1)
1309         return test1_single (tm);
1310       else
1311         return test1_double (tm);
1312     }
1313   if (is_test2)
1314     {
1315       if (num_wheels == 1)
1316         return test2_single (tm);
1317       else if (num_wheels == 2)
1318         if (is_updates)
1319           return test2_double_updates (tm);
1320         else
1321           return test2_double (tm);
1322       else if (num_wheels == 3)
1323         {
1324           if (overflow == 0)
1325             return test2_triple (tm);
1326           else
1327             return test2_triple_ov (tm);
1328         }
1329     }
1330   if (is_test3)
1331     return test3_triple_double (tm);
1332
1333   if (is_test4)
1334     return test4_double_double (tm);
1335
1336   if (is_test5)
1337     return test5_double (tm);
1338
1339   /* NOTREACHED */
1340   return 0;
1341 }
1342
1343 #ifdef CLIB_UNIX
1344 int
1345 main (int argc, char *argv[])
1346 {
1347   unformat_input_t i;
1348   clib_error_t *error;
1349   tw_timer_test_main_t *tm = &tw_timer_test_main;
1350
1351   clib_mem_init (0, 3ULL << 30);
1352
1353   unformat_init_command_line (&i, argv);
1354   error = timer_test_command_fn (tm, &i);
1355   unformat_free (&i);
1356
1357   if (error)
1358     {
1359       clib_error_report (error);
1360       return 1;
1361     }
1362   return 0;
1363 }
1364 #endif /* CLIB_UNIX */
1365
1366 /* For debugging... */
1367 int
1368 pifi (void *p, u32 index)
1369 {
1370   return pool_is_free_index (p, index);
1371 }
1372
1373 u32
1374 vl (void *p)
1375 {
1376   return vec_len (p);
1377 }
1378
1379 uword
1380 pe (void *v)
1381 {
1382   return (pool_elts (v));
1383 }
1384
1385 /*
1386  * fd.io coding-style-patch-verification: ON
1387  *
1388  * Local Variables:
1389  * eval: (c-set-style "gnu")
1390  * End:
1391  */