#include <vppinfra/time.h> #include <vppinfra/cache.h> #include <vppinfra/error.h> #include <vppinfra/tw_timer_2t_1w_2048sl.h> #include <vppinfra/tw_timer_2t_2w_512sl.h> #include <vppinfra/tw_timer_16t_2w_512sl.h> #include <vppinfra/tw_timer_4t_3w_256sl.h> #include <vppinfra/tw_timer_1t_3w_1024sl_ov.h> typedef struct { /** Handle returned from tw_start_timer */ u32 stop_timer_handle; /** Test item should expire at this clock tick */ u64 expected_to_expire; } tw_timer_test_elt_t; typedef struct { /** Pool of test objects */ tw_timer_test_elt_t *test_elts; /** The single-wheel */ tw_timer_wheel_2t_1w_2048sl_t single_wheel; /** The double-wheel */ tw_timer_wheel_16t_2w_512sl_t double_wheel; /* The triple wheel */ tw_timer_wheel_4t_3w_256sl_t triple_wheel; /* The triple wheel with overflow vector */ tw_timer_wheel_1t_3w_1024sl_ov_t triple_ov_wheel; /* Another two timer wheel geometry */ tw_timer_wheel_2t_2w_512sl_t two_timer_double_wheel; /** random number seed */ u64 seed; /** number of timers */ u32 ntimers; /** number of "churn" iterations */ u32 niter; /** number of clock ticks per churn iteration */ u32 ticks_per_iter; /** cpu timer */ clib_time_t clib_time; } tw_timer_test_main_t; tw_timer_test_main_t tw_timer_test_main; static void run_single_wheel (tw_timer_wheel_2t_1w_2048sl_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_2t_1w_2048sl (tw, now); now += 1.01; } } static void run_double_wheel (tw_timer_wheel_16t_2w_512sl_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_16t_2w_512sl (tw, now); now += 1.01; } } static void run_two_timer_double_wheel (tw_timer_wheel_2t_2w_512sl_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_2t_2w_512sl (tw, now); now += 1.01; } } static void run_triple_wheel (tw_timer_wheel_4t_3w_256sl_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_4t_3w_256sl (tw, now); now += 1.01; } } static void run_triple_ov_wheel (tw_timer_wheel_1t_3w_1024sl_ov_t * tw, u32 n_ticks) { u32 i; f64 now = tw->last_run_time + 1.01; for (i = 0; i < n_ticks; i++) { tw_timer_expire_timers_1t_3w_1024sl_ov (tw, now); now += 1.01; } } static void expired_timer_single_callback (u32 * expired_timers) { int i; u32 pool_index, timer_id; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i] & 0x7FFFFFFF; timer_id = expired_timers[i] >> 31; ASSERT (timer_id == 1); e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->single_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->single_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static void expired_timer_double_callback (u32 * expired_timers) { int i; u32 pool_index, timer_id; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i] & 0x0FFFFFFF; timer_id = expired_timers[i] >> 28; ASSERT (timer_id == 14); e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->double_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->double_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static void expired_timer_two_timer_double_callback (u32 * expired_timers) { int i; u32 pool_index, timer_id; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i] & 0x7FFFFFFF; timer_id = expired_timers[i] >> 31; ASSERT (timer_id == 1); e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->two_timer_double_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->two_timer_double_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static void expired_timer_triple_callback (u32 * expired_timers) { int i; u32 pool_index, timer_id; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i] & 0x3FFFFFFF; timer_id = expired_timers[i] >> 30; ASSERT (timer_id == 3); e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->triple_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->triple_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static void expired_timer_triple_ov_callback (u32 * expired_timers) { int i; u32 pool_index; tw_timer_test_elt_t *e; tw_timer_test_main_t *tm = &tw_timer_test_main; for (i = 0; i < vec_len (expired_timers); i++) { pool_index = expired_timers[i]; e = pool_elt_at_index (tm->test_elts, pool_index); if (e->expected_to_expire != tm->triple_ov_wheel.current_tick) { fformat (stdout, "[%d] expired at %lld not %lld\n", e - tm->test_elts, tm->triple_ov_wheel.current_tick, e->expected_to_expire); } pool_put (tm->test_elts, e); } } static clib_error_t * test2_single (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset; u64 expiration_time; u32 max_expiration_time = 0; u32 *deleted_indices = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_2t_1w_2048sl (&tm->single_wheel, expired_timer_single_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 757; run_single_wheel (&tm->single_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fast index %d\n", tm->single_wheel.current_tick, tm->single_wheel.current_index[TW_TIMER_RING_FAST]); initial_wheel_offset = tm->single_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & (2047); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_2t_1w_2048sl (&tm->single_wheel, e - tm->test_elts, 1 /* timer id */ , expiration_time); } adds += i; for (i = 0; i < tm->niter; i++) { run_single_wheel (&tm->single_wheel, tm->ticks_per_iter); j = 0; vec_reset_length (deleted_indices); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ tw_timer_stop_2t_1w_2048sl (&tm->single_wheel, e->stop_timer_handle); vec_add1 (deleted_indices, e - tm->test_elts); if (++j >= tm->ntimers / 4) goto del_and_re_add; })); /* *INDENT-ON* */ del_and_re_add: for (j = 0; j < vec_len (deleted_indices); j++) { pool_put_index (tm->test_elts, deleted_indices[j]); } deletes += j; for (j = 0; j < tm->ntimers / 4; j++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & (2047); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + tm->single_wheel.current_tick; e->stop_timer_handle = tw_timer_start_2t_1w_2048sl (&tm->single_wheel, e - tm->test_elts, 1 /* timer id */ , expiration_time); } adds += j; } vec_free (deleted_indices); run_single_wheel (&tm->single_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->single_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->single_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_2t_1w_2048sl (&tm->single_wheel); return 0; } static clib_error_t * test2_double (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset; u32 expiration_time; u32 max_expiration_time = 0; u32 *deleted_indices = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel, expired_timer_double_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 7577; run_double_wheel (&tm->double_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n", tm->double_wheel.current_tick, tm->double_wheel.current_index[TW_TIMER_RING_FAST], tm->double_wheel.current_index[TW_TIMER_RING_SLOW]); initial_wheel_offset = tm->double_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ , expiration_time); } adds += i; for (i = 0; i < tm->niter; i++) { run_double_wheel (&tm->double_wheel, tm->ticks_per_iter); j = 0; vec_reset_length (deleted_indices); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ tw_timer_stop_16t_2w_512sl (&tm->double_wheel, e->stop_timer_handle); vec_add1 (deleted_indices, e - tm->test_elts); if (++j >= tm->ntimers / 4) goto del_and_re_add; })); /* *INDENT-ON* */ del_and_re_add: for (j = 0; j < vec_len (deleted_indices); j++) pool_put_index (tm->test_elts, deleted_indices[j]); deletes += j; for (j = 0; j < tm->ntimers / 4; j++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + tm->double_wheel.current_tick; e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ , expiration_time); } adds += j; } vec_free (deleted_indices); run_double_wheel (&tm->double_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->double_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->double_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel); return 0; } static u32 get_expiration_time (tw_timer_test_main_t * tm) { u32 expiration_time; do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); return expiration_time; } static clib_error_t * test2_double_updates (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset; u32 expiration_time; u32 max_expiration_time = 0, updates = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel, expired_timer_double_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 7577; run_double_wheel (&tm->double_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n", tm->double_wheel.current_tick, tm->double_wheel.current_index[TW_TIMER_RING_FAST], tm->double_wheel.current_index[TW_TIMER_RING_SLOW]); initial_wheel_offset = tm->double_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); expiration_time = get_expiration_time (tm); max_expiration_time = clib_max (expiration_time, max_expiration_time); e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ , expiration_time); } for (i = 0; i < tm->niter; i++) { run_double_wheel (&tm->double_wheel, tm->ticks_per_iter); j = 0; /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ expiration_time = get_expiration_time (tm); max_expiration_time = clib_max (expiration_time, max_expiration_time); e->expected_to_expire = expiration_time + tm->double_wheel.current_tick; tw_timer_update_16t_2w_512sl (&tm->double_wheel, e->stop_timer_handle, expiration_time); if (++j >= tm->ntimers / 4) goto done; })); /* *INDENT-ON* */ done: updates += j; } run_double_wheel (&tm->double_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d updates, %d ticks\n", updates, tm->double_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) updates + (f64) tm->double_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel); return 0; } static clib_error_t * test2_triple (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset = 0; u32 expiration_time; u32 max_expiration_time = 0; u32 *deleted_indices = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_4t_3w_256sl (&tm->triple_wheel, expired_timer_triple_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 75700; run_triple_wheel (&tm->triple_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fi %d si %d gi %d\n", tm->triple_wheel.current_tick, tm->triple_wheel.current_index[TW_TIMER_RING_FAST], tm->triple_wheel.current_index[TW_TIMER_RING_SLOW], tm->triple_wheel.current_index[TW_TIMER_RING_GLACIER]); initial_wheel_offset = tm->triple_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_4t_3w_256sl (&tm->triple_wheel, e - tm->test_elts, 3 /* timer id */ , expiration_time); } adds += i; for (i = 0; i < tm->niter; i++) { run_triple_wheel (&tm->triple_wheel, tm->ticks_per_iter); j = 0; vec_reset_length (deleted_indices); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ tw_timer_stop_4t_3w_256sl (&tm->triple_wheel, e->stop_timer_handle); vec_add1 (deleted_indices, e - tm->test_elts); if (++j >= tm->ntimers / 4) goto del_and_re_add; })); /* *INDENT-ON* */ del_and_re_add: for (j = 0; j < vec_len (deleted_indices); j++) pool_put_index (tm->test_elts, deleted_indices[j]); deletes += j; for (j = 0; j < tm->ntimers / 4; j++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + tm->triple_wheel.current_tick; e->stop_timer_handle = tw_timer_start_4t_3w_256sl (&tm->triple_wheel, e - tm->test_elts, 3 /* timer id */ , expiration_time); } adds += j; } vec_free (deleted_indices); run_triple_wheel (&tm->triple_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->triple_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->triple_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_4t_3w_256sl (&tm->triple_wheel); return 0; } static clib_error_t * test2_triple_ov (tw_timer_test_main_t * tm) { u32 i, j; tw_timer_test_elt_t *e; u32 initial_wheel_offset = 0; u32 expiration_time; u32 max_expiration_time = 0; u32 *deleted_indices = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_1t_3w_1024sl_ov (&tm->triple_ov_wheel, expired_timer_triple_ov_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 75700; run_triple_ov_wheel (&tm->triple_ov_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fi %d si %d gi %d\n", tm->triple_ov_wheel.current_tick, tm->triple_ov_wheel.current_index[TW_TIMER_RING_FAST], tm->triple_ov_wheel.current_index[TW_TIMER_RING_SLOW], tm->triple_ov_wheel.current_index[TW_TIMER_RING_GLACIER]); initial_wheel_offset = tm->triple_ov_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_1t_3w_1024sl_ov (&tm->triple_ov_wheel, e - tm->test_elts, 0 /* timer id */ , expiration_time); } adds += i; for (i = 0; i < tm->niter; i++) { run_triple_ov_wheel (&tm->triple_ov_wheel, tm->ticks_per_iter); j = 0; vec_reset_length (deleted_indices); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ tw_timer_stop_1t_3w_1024sl_ov (&tm->triple_ov_wheel, e->stop_timer_handle); vec_add1 (deleted_indices, e - tm->test_elts); if (++j >= tm->ntimers / 4) goto del_and_re_add; })); /* *INDENT-ON* */ del_and_re_add: for (j = 0; j < vec_len (deleted_indices); j++) pool_put_index (tm->test_elts, deleted_indices[j]); deletes += j; for (j = 0; j < tm->ntimers / 4; j++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); do { expiration_time = random_u64 (&tm->seed) & ((1 << 17) - 1); } while (expiration_time == 0); if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + tm->triple_ov_wheel.current_tick; e->stop_timer_handle = tw_timer_start_1t_3w_1024sl_ov (&tm->triple_ov_wheel, e - tm->test_elts, 0 /* timer id */ , expiration_time); } adds += j; } vec_free (deleted_indices); run_triple_ov_wheel (&tm->triple_ov_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->triple_ov_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->triple_ov_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ TWT (tw_timer) * t; fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); t = pool_elt_at_index (tm->triple_ov_wheel.timers, e->stop_timer_handle); fformat (stdout, " expiration_time %lld\n", t->expiration_time); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_1t_3w_1024sl_ov (&tm->triple_ov_wheel); return 0; } static clib_error_t * test1_single (tw_timer_test_main_t * tm) { u32 i; tw_timer_test_elt_t *e; u32 offset; tw_timer_wheel_init_2t_1w_2048sl (&tm->single_wheel, expired_timer_single_callback, 1.0 /* timer interval */ , ~0); /* * Prime offset, to make sure that the wheel starts in a * non-trivial position */ offset = 123; run_single_wheel (&tm->single_wheel, offset); fformat (stdout, "initial wheel time %d, fast index %d\n", tm->single_wheel.current_tick, tm->single_wheel.current_index[TW_TIMER_RING_FAST]); offset = tm->single_wheel.current_tick; for (i = 0; i < tm->ntimers; i++) { u32 expected_to_expire; u32 timer_arg; timer_arg = 1 + i; timer_arg &= 2047; if (timer_arg == 0) timer_arg = 1; expected_to_expire = timer_arg + tm->single_wheel.current_tick; pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); e->expected_to_expire = expected_to_expire; e->stop_timer_handle = tw_timer_start_2t_1w_2048sl (&tm->single_wheel, e - tm->test_elts, 1 /* timer id */ , timer_arg); } run_single_wheel (&tm->single_wheel, tm->ntimers + 3); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat(stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ fformat (stdout, "final wheel time %d, fast index %d\n", tm->single_wheel.current_tick, tm->single_wheel.current_index[TW_TIMER_RING_FAST]); pool_free (tm->test_elts); tw_timer_wheel_free_2t_1w_2048sl (&tm->single_wheel); return 0; } static clib_error_t * test1_double (tw_timer_test_main_t * tm) { u32 i; tw_timer_test_elt_t *e; u32 offset; tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel, expired_timer_double_callback, 1.0 /* timer interval */ , ~0); /* * Prime offset, to make sure that the wheel starts in a * non-trivial position */ offset = 227989; run_double_wheel (&tm->double_wheel, offset); fformat (stdout, "initial wheel time %d, fast index %d\n", tm->double_wheel.current_tick, tm->double_wheel.current_index[TW_TIMER_RING_FAST]); for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); e->expected_to_expire = i + tm->double_wheel.current_tick + 1; e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ , i + 1); } run_double_wheel (&tm->double_wheel, tm->ntimers + 3); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat(stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ fformat (stdout, "final wheel time %d, fast index %d\n", tm->double_wheel.current_tick, tm->double_wheel.current_index[TW_TIMER_RING_FAST]); pool_free (tm->test_elts); tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel); return 0; } static clib_error_t * test1_two_timer_double (tw_timer_test_main_t * tm) { u32 i; tw_timer_test_elt_t *e; u32 offset; tw_timer_wheel_init_2t_2w_512sl (&tm->two_timer_double_wheel, expired_timer_two_timer_double_callback, 1.0 /* timer interval */ , ~0); /* * Prime offset, to make sure that the wheel starts in a * non-trivial position */ offset = 2745; run_two_timer_double_wheel (&tm->two_timer_double_wheel, offset); fformat (stdout, "initial wheel time %d, fast index %d\n", tm->two_timer_double_wheel.current_tick, tm->two_timer_double_wheel.current_index[TW_TIMER_RING_FAST]); for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); e->expected_to_expire = i + tm->two_timer_double_wheel.current_tick + 1; e->stop_timer_handle = tw_timer_start_2t_2w_512sl (&tm->two_timer_double_wheel, e - tm->test_elts, 1 /* timer id */ , i + 1); } run_two_timer_double_wheel (&tm->two_timer_double_wheel, tm->ntimers + 3); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat(stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ fformat (stdout, "final wheel time %d, fast index %d\n", tm->two_timer_double_wheel.current_tick, tm->two_timer_double_wheel.current_index[TW_TIMER_RING_FAST]); pool_free (tm->test_elts); tw_timer_wheel_free_2t_2w_512sl (&tm->two_timer_double_wheel); return 0; } static clib_error_t * test3_triple_double (tw_timer_test_main_t * tm) { tw_timer_test_elt_t *e; u32 initial_wheel_offset = 0; u32 expiration_time; u32 max_expiration_time = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_4t_3w_256sl (&tm->triple_wheel, expired_timer_triple_callback, 1.0 /* timer interval */ , ~0); initial_wheel_offset = 0; run_triple_wheel (&tm->triple_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fi %d si %d gi %d\n", tm->triple_wheel.current_tick, tm->triple_wheel.current_index[TW_TIMER_RING_FAST], tm->triple_wheel.current_index[TW_TIMER_RING_SLOW], tm->triple_wheel.current_index[TW_TIMER_RING_GLACIER]); initial_wheel_offset = tm->triple_wheel.current_tick; fformat (stdout, "Create a timer which expires at wheel-time (1, 0, 0)\n"); before = clib_time_now (&tm->clib_time); /* Prime the pump */ pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); /* 1 glacier ring tick from now */ expiration_time = TW_SLOTS_PER_RING * TW_SLOTS_PER_RING; e->expected_to_expire = expiration_time + initial_wheel_offset; max_expiration_time = expiration_time; e->stop_timer_handle = tw_timer_start_4t_3w_256sl (&tm->triple_wheel, e - tm->test_elts, 3 /* timer id */ , expiration_time); run_triple_wheel (&tm->triple_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->triple_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->triple_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_4t_3w_256sl (&tm->triple_wheel); return 0; } static clib_error_t * test4_double_double (tw_timer_test_main_t * tm) { u32 i; tw_timer_test_elt_t *e; u32 initial_wheel_offset; u32 expiration_time; u32 max_expiration_time = 0; u32 *deleted_indices = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel, expired_timer_double_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 0; run_double_wheel (&tm->double_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n", tm->double_wheel.current_tick, tm->double_wheel.current_index[TW_TIMER_RING_FAST], tm->double_wheel.current_index[TW_TIMER_RING_SLOW]); initial_wheel_offset = tm->double_wheel.current_tick; fformat (stdout, "test timer which expires at 512 ticks\n"); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); expiration_time = 512; if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ , expiration_time); } adds = 1; vec_free (deleted_indices); run_double_wheel (&tm->double_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->double_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->double_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel); return 0; } static clib_error_t * test5_double (tw_timer_test_main_t * tm) { u32 i; tw_timer_test_elt_t *e; u32 initial_wheel_offset; u32 expiration_time; u32 max_expiration_time = 0; u32 adds = 0, deletes = 0; f64 before, after; clib_time_init (&tm->clib_time); tw_timer_wheel_init_16t_2w_512sl (&tm->double_wheel, expired_timer_double_callback, 1.0 /* timer interval */ , ~0); /* Prime offset */ initial_wheel_offset = 7567; run_double_wheel (&tm->double_wheel, initial_wheel_offset); fformat (stdout, "initial wheel time %d, fast index %d slow index %d\n", tm->double_wheel.current_tick, tm->double_wheel.current_index[TW_TIMER_RING_FAST], tm->double_wheel.current_index[TW_TIMER_RING_SLOW]); initial_wheel_offset = tm->double_wheel.current_tick; fformat (stdout, "test %d timers, %d iter, %d ticks per iter, 0x%llx seed\n", tm->ntimers, tm->niter, tm->ticks_per_iter, tm->seed); before = clib_time_now (&tm->clib_time); /* Prime the pump */ for (i = 0; i < tm->ntimers; i++) { pool_get (tm->test_elts, e); clib_memset (e, 0, sizeof (*e)); expiration_time = i + 1; if (expiration_time > max_expiration_time) max_expiration_time = expiration_time; e->expected_to_expire = expiration_time + initial_wheel_offset; e->stop_timer_handle = tw_timer_start_16t_2w_512sl (&tm->double_wheel, e - tm->test_elts, 14 /* timer id */ , expiration_time); } adds += i; run_double_wheel (&tm->double_wheel, max_expiration_time + 1); after = clib_time_now (&tm->clib_time); fformat (stdout, "%d adds, %d deletes, %d ticks\n", adds, deletes, tm->double_wheel.current_tick); fformat (stdout, "test ran %.2f seconds, %.2f ops/second\n", (after - before), ((f64) adds + (f64) deletes + (f64) tm->double_wheel.current_tick) / (after - before)); if (pool_elts (tm->test_elts)) fformat (stdout, "Note: %d elements remain in pool\n", pool_elts (tm->test_elts)); /* *INDENT-OFF* */ pool_foreach (e, tm->test_elts, ({ fformat (stdout, "[%d] expected to expire %d\n", e - tm->test_elts, e->expected_to_expire); })); /* *INDENT-ON* */ pool_free (tm->test_elts); tw_timer_wheel_free_16t_2w_512sl (&tm->double_wheel); return 0; } static clib_error_t * timer_test_command_fn (tw_timer_test_main_t * tm, unformat_input_t * input) { int is_test1 = 0, is_updates = 0; int num_wheels = 1; int is_test2 = 0; int is_test3 = 0; int is_test4 = 0; int is_test5 = 0; int overflow = 0; clib_memset (tm, 0, sizeof (*tm)); /* Default values */ tm->ntimers = 100000; tm->seed = 0xDEADDABEB00BFACE; tm->niter = 1000; tm->ticks_per_iter = 727; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "seed %lld", &tm->seed)) ; else if (unformat (input, "test1")) is_test1 = 1; else if (unformat (input, "test2")) is_test2 = 1; else if (unformat (input, "overflow")) overflow = 1; else if (unformat (input, "lebron")) is_test3 = 1; else if (unformat (input, "wilt")) is_test4 = 1; else if (unformat (input, "linear")) is_test5 = 1; else if (unformat (input, "updates")) is_updates = 1; else if (unformat (input, "wheels %d", &num_wheels)) ; else if (unformat (input, "ntimers %d", &tm->ntimers)) ; else if (unformat (input, "niter %d", &tm->niter)) ; else if (unformat (input, "ticks_per_iter %d", &tm->ticks_per_iter)) ; else break; } if (is_test1 + is_test2 + is_test3 + is_test4 + is_test5 == 0) return clib_error_return (0, "No test specified [test1..n]"); if (num_wheels < 1 || num_wheels > 3) return clib_error_return (0, "unsupported... 1 or 2 wheels only"); if (is_test1) { if (num_wheels == 1) return test1_single (tm); else { (void) test1_double (tm); return test1_two_timer_double (tm); } } if (is_test2) { if (num_wheels == 1) return test2_single (tm); else if (num_wheels == 2) if (is_updates) return test2_double_updates (tm); else return test2_double (tm); else if (num_wheels == 3) { if (overflow == 0) return test2_triple (tm); else return test2_triple_ov (tm); } } if (is_test3) return test3_triple_double (tm); if (is_test4) return test4_double_double (tm); if (is_test5) return test5_double (tm); /* NOTREACHED */ return 0; } #ifdef CLIB_UNIX int main (int argc, char *argv[]) { unformat_input_t i; clib_error_t *error; tw_timer_test_main_t *tm = &tw_timer_test_main; clib_mem_init (0, 3ULL << 30); unformat_init_command_line (&i, argv); error = timer_test_command_fn (tm, &i); unformat_free (&i); if (error) { clib_error_report (error); return 1; } return 0; } #endif /* CLIB_UNIX */ /* For debugging... */ int pifi (void *p, u32 index) { return pool_is_free_index (p, index); } u32 vl (void *p) { return vec_len (p); } uword pe (void *v) { return (pool_elts (v)); } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */