blob: 7cf6e0c33f4f799eef95fe846c9f0bcab34c5a3b [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
2 * Copyright (c) 2015 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include <vppinfra/bitmap.h>
16#include <vppinfra/error.h>
17#include <vppinfra/format.h>
18#include <vppinfra/pool.h>
19#include <vppinfra/random.h>
20#include <vppinfra/time.h>
21#include <vppinfra/timing_wheel.h>
22#include <vppinfra/zvec.h>
23
24#include <vppinfra/math.h>
25
26#if __GNUC__ < 4
27#define SQRT(a) a
28#else
29#define SQRT(a) sqrt(a)
30#endif
31
Dave Barachc3799992016-08-15 11:12:27 -040032typedef struct
33{
Ed Warnickecb9cada2015-12-08 15:45:58 -070034 uword n_iter;
35
36 u32 n_events;
37 u32 seed;
38 u32 verbose;
39
40 /* Time is "synthetic" e.g. not taken from CPU timer. */
41 u32 synthetic_time;
42
43 clib_time_t time;
44 timing_wheel_t timing_wheel;
45
Dave Barachc3799992016-08-15 11:12:27 -040046 u64 *events;
Ed Warnickecb9cada2015-12-08 15:45:58 -070047
48 f64 max_time;
49 f64 wait_time;
50
51 f64 total_iterate_time;
52 f64 time_iterate_start;
53
54 f64 time_per_status_update;
55 f64 time_next_status_update;
56} test_timing_wheel_main_t;
57
Dave Barachc3799992016-08-15 11:12:27 -040058typedef struct
59{
Matus Fabiand2dc3df2015-12-14 10:31:33 -050060 f64 dt;
61 f64 fraction;
62 u64 count;
63} test_timing_wheel_tmp_t;
64
Dave Barachc3799992016-08-15 11:12:27 -040065static void
66set_event (test_timing_wheel_main_t * tm, uword i)
Ed Warnickecb9cada2015-12-08 15:45:58 -070067{
Dave Barachc3799992016-08-15 11:12:27 -040068 timing_wheel_t *w = &tm->timing_wheel;
Ed Warnickecb9cada2015-12-08 15:45:58 -070069 u64 cpu_time;
70
71 cpu_time = w->current_time_index << w->log2_clocks_per_bin;
72 if (tm->synthetic_time)
73 cpu_time += random_u32 (&tm->seed) % tm->n_iter;
74 else
Dave Barachc3799992016-08-15 11:12:27 -040075 cpu_time +=
76 random_f64 (&tm->seed) * tm->max_time * tm->time.clocks_per_second;
Ed Warnickecb9cada2015-12-08 15:45:58 -070077
78 timing_wheel_insert (w, cpu_time, i);
79 timing_wheel_validate (w);
80 tm->events[i] = cpu_time;
81}
82
Dave Barachc3799992016-08-15 11:12:27 -040083static int
84test_timing_wheel_tmp_cmp (void *a1, void *a2)
Matus Fabiand2dc3df2015-12-14 10:31:33 -050085{
Dave Barachc3799992016-08-15 11:12:27 -040086 test_timing_wheel_tmp_t *f1 = a1;
87 test_timing_wheel_tmp_t *f2 = a2;
Matus Fabiand2dc3df2015-12-14 10:31:33 -050088
89 return f1->dt < f2->dt ? -1 : (f1->dt > f2->dt ? +1 : 0);
90}
91
Ed Warnickecb9cada2015-12-08 15:45:58 -070092clib_error_t *
93test_timing_wheel_main (unformat_input_t * input)
94{
Dave Barachc3799992016-08-15 11:12:27 -040095 clib_error_t *error = 0;
96 test_timing_wheel_main_t _tm, *tm = &_tm;
97 timing_wheel_t *w = &tm->timing_wheel;
Ed Warnickecb9cada2015-12-08 15:45:58 -070098 uword iter, i;
99
100 memset (tm, 0, sizeof (tm[0]));
101 tm->n_iter = 10;
102 tm->time_per_status_update = 0;
103 tm->n_events = 100;
104 tm->seed = 1;
105 tm->synthetic_time = 1;
106 tm->max_time = 1;
107 tm->wait_time = 1e-3;
108
109 w->validate = 0;
110 w->n_wheel_elt_time_bits = 32;
111
112 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
113 {
114 if (unformat (input, "iter %wd", &tm->n_iter))
115 ;
116 else if (unformat (input, "events %d", &tm->n_events))
117 ;
Dave Barachc3799992016-08-15 11:12:27 -0400118 else
119 if (unformat (input, "elt-time-bits %d", &w->n_wheel_elt_time_bits))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700120 ;
121 else if (unformat (input, "seed %d", &tm->seed))
122 ;
123 else if (unformat (input, "verbose"))
124 tm->verbose = 1;
125 else if (unformat (input, "validate"))
126 w->validate = 1;
127
128 else if (unformat (input, "real-time"))
129 tm->synthetic_time = 0;
130 else if (unformat (input, "synthetic-time"))
131 tm->synthetic_time = 1;
132 else if (unformat (input, "max-time %f", &tm->max_time))
133 ;
134 else if (unformat (input, "wait-time %f", &tm->wait_time))
135 ;
136 else if (unformat (input, "iter-time %f", &tm->total_iterate_time))
137 ;
138 else if (unformat (input, "print %f", &tm->time_per_status_update))
139 ;
140
141 else
142 {
143 error = clib_error_create ("unknown input `%U'\n",
144 format_unformat_error, input);
145 goto done;
146 }
147 }
148
Dave Barachc3799992016-08-15 11:12:27 -0400149 if (!tm->seed)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700150 tm->seed = random_default_seed ();
151
152 clib_time_init (&tm->time);
153
154 if (tm->synthetic_time)
155 {
156 w->min_sched_time = tm->time.seconds_per_clock;
157 w->max_sched_time = w->min_sched_time * 256;
158 timing_wheel_init (w, 0, tm->time.clocks_per_second);
159 }
160 else
161 {
162 timing_wheel_init (w, clib_cpu_time_now (), tm->time.clocks_per_second);
163 }
164
165 clib_warning ("iter %wd, events %d, seed %u, %U",
166 tm->n_iter, tm->n_events, tm->seed,
167 format_timing_wheel, &tm->timing_wheel, /* verbose */ 0);
168
169 /* Make some events. */
170 vec_resize (tm->events, tm->n_events);
171 for (i = 0; i < vec_len (tm->events); i++)
172 set_event (tm, i);
173
174 {
Dave Barachc3799992016-08-15 11:12:27 -0400175 u32 *expired = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700176 f64 ave_error = 0;
177 f64 rms_error = 0;
178 f64 max_error = 0, min_error = 1e30;
Dave Barachc3799992016-08-15 11:12:27 -0400179 u32 *error_hist = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700180 uword n_expired = 0;
Dave Barachc3799992016-08-15 11:12:27 -0400181 uword *expired_bitmap[2] = { 0 };
Ed Warnickecb9cada2015-12-08 15:45:58 -0700182 uword n_events_in_wheel = vec_len (tm->events);
183
184 vec_resize (expired, 32);
185 vec_resize (error_hist, 1024);
186
187 tm->time_iterate_start = clib_time_now (&tm->time);
Dave Barachc3799992016-08-15 11:12:27 -0400188 tm->time_next_status_update =
189 tm->time_iterate_start + tm->time_per_status_update;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700190
191 if (tm->total_iterate_time != 0)
192 tm->n_iter = ~0;
193
194 for (iter = 0; iter < tm->n_iter || n_events_in_wheel > 0; iter++)
195 {
196 u64 cpu_time, min_next_time[2];
197
198 if (tm->synthetic_time)
199 cpu_time = iter << w->log2_clocks_per_bin;
200 else
201 cpu_time = clib_cpu_time_now ();
202
203 _vec_len (expired) = 0;
Dave Barachc3799992016-08-15 11:12:27 -0400204 expired =
205 timing_wheel_advance (w, cpu_time, expired, &min_next_time[0]);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700206 timing_wheel_validate (w);
207
208 /* Update bitmap of expired events. */
209 if (w->validate)
210 {
211 for (i = 0; i < vec_len (tm->events); i++)
212 {
213 uword is_expired;
214
Dave Barachc3799992016-08-15 11:12:27 -0400215 is_expired =
216 (cpu_time >> w->log2_clocks_per_bin) >=
217 (tm->events[i] >> w->log2_clocks_per_bin);
218 expired_bitmap[0] =
219 clib_bitmap_set (expired_bitmap[0], i, is_expired);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700220
221 /* Validate min next time. */
222 if (is_expired)
223 ASSERT (min_next_time[0] > tm->events[i]);
224 else
225 ASSERT (min_next_time[0] <= tm->events[i]);
226 }
227 }
228
229 n_expired += vec_len (expired);
230 for (i = 0; i < vec_len (expired); i++)
231 {
232 word j, idt;
233 i64 dt_cpu;
234 f64 fdt_cpu;
235
236 j = expired[i];
237 expired_bitmap[1] = clib_bitmap_ori (expired_bitmap[1], j);
238
239 dt_cpu = cpu_time - tm->events[j];
240
241 /* Event must be scheduled in correct bin. */
242 if (tm->synthetic_time)
243 ASSERT (dt_cpu >= 0 && dt_cpu <= (1 << w->log2_clocks_per_bin));
244
245 fdt_cpu = dt_cpu * tm->time.seconds_per_clock;
246
247 ave_error += fdt_cpu;
248 rms_error += fdt_cpu * fdt_cpu;
249
250 if (fdt_cpu > max_error)
251 max_error = fdt_cpu;
252 if (fdt_cpu < min_error)
253 min_error = fdt_cpu;
254
Dave Barachc3799992016-08-15 11:12:27 -0400255 idt =
256 (cpu_time >> w->log2_clocks_per_bin) -
257 (tm->events[j] >> w->log2_clocks_per_bin);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700258 idt = zvec_signed_to_unsigned (idt);
259 vec_validate (error_hist, idt);
260 error_hist[idt] += 1;
261 }
262
263 if (w->validate)
264 for (i = 0; i < vec_len (tm->events); i++)
265 {
266 int is_expired = clib_bitmap_get (expired_bitmap[0], i);
267 int is_expired_w = clib_bitmap_get (expired_bitmap[1], i);
268 ASSERT (is_expired == is_expired_w);
269 }
270
271 min_next_time[1] = ~0;
272 for (i = 0; i < vec_len (tm->events); i++)
273 {
Dave Barachc3799992016-08-15 11:12:27 -0400274 if (!clib_bitmap_get (expired_bitmap[1], i))
Ed Warnickecb9cada2015-12-08 15:45:58 -0700275 min_next_time[1] = clib_min (min_next_time[1], tm->events[i]);
276 }
277 if (min_next_time[0] != min_next_time[1])
Dave Barachc3799992016-08-15 11:12:27 -0400278 clib_error ("min next time wrong 0x%Lx != 0x%Lx", min_next_time[0],
279 min_next_time[1]);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700280
281 if (tm->time_per_status_update != 0
282 && clib_time_now (&tm->time) >= tm->time_next_status_update)
283 {
284 f64 ave = 0, rms = 0;
285
286 tm->time_next_status_update += tm->time_per_status_update;
287 if (n_expired > 0)
288 {
289 ave = ave_error / n_expired;
290 rms = SQRT (rms_error / n_expired - ave * ave);
291 }
292
Dave Barachc3799992016-08-15 11:12:27 -0400293 clib_warning
294 ("%12wd iter done %10wd expired; ave. error %.4e +- %.4e, range %.4e %.4e",
295 iter, n_expired, ave, rms, min_error, max_error);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700296 }
297
298 if (tm->total_iterate_time != 0
299 && (clib_time_now (&tm->time) - tm->time_iterate_start
300 >= tm->total_iterate_time))
301 tm->n_iter = iter;
302
303 /* Add new events to wheel to replace expired ones. */
304 n_events_in_wheel -= vec_len (expired);
305 if (iter < tm->n_iter)
306 {
307 for (i = 0; i < vec_len (expired); i++)
308 {
309 uword j = expired[i];
310 set_event (tm, j);
Dave Barachc3799992016-08-15 11:12:27 -0400311 expired_bitmap[1] =
312 clib_bitmap_andnoti (expired_bitmap[1], j);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700313 }
314 n_events_in_wheel += vec_len (expired);
315 }
316 }
Dave Barachc3799992016-08-15 11:12:27 -0400317
Ed Warnickecb9cada2015-12-08 15:45:58 -0700318 ave_error /= n_expired;
319 rms_error = SQRT (rms_error / n_expired - ave_error * ave_error);
320
Dave Barachc3799992016-08-15 11:12:27 -0400321 clib_warning
322 ("%wd iter done %wd expired; ave. error %.4e +- %.4e, range %.4e %.4e",
323 1 + iter, n_expired, ave_error, rms_error, min_error, max_error);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700324
325 {
Dave Barachc3799992016-08-15 11:12:27 -0400326 test_timing_wheel_tmp_t *fs, *f;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700327 f64 total_fraction;
328
329 fs = 0;
330 for (i = 0; i < vec_len (error_hist); i++)
331 {
332 if (error_hist[i] == 0)
333 continue;
334 vec_add2 (fs, f, 1);
Dave Barachc3799992016-08-15 11:12:27 -0400335 f->dt =
336 (((i64) zvec_unsigned_to_signed (i) << w->log2_clocks_per_bin) *
337 tm->time.seconds_per_clock);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700338 f->fraction = (f64) error_hist[i] / (f64) n_expired;
339 f->count = error_hist[i];
340 }
341
Matus Fabiand2dc3df2015-12-14 10:31:33 -0500342 vec_sort_with_function (fs, test_timing_wheel_tmp_cmp);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700343
344 total_fraction = 0;
345 vec_foreach (f, fs)
Dave Barachc3799992016-08-15 11:12:27 -0400346 {
347 total_fraction += f->fraction;
348 if (f == fs)
349 fformat (stdout, "%=12s %=16s %=16s %s\n", "Error max", "Fraction",
350 "Total", "Count");
351 fformat (stdout, "%12.4e %16.4f%% %16.4f%% %Ld\n", f->dt,
352 f->fraction * 100, total_fraction * 100, f->count);
353 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700354 }
355
356 clib_warning ("%U", format_timing_wheel, w, /* verbose */ 1);
357 }
358
Dave Barachc3799992016-08-15 11:12:27 -0400359done:
Ed Warnickecb9cada2015-12-08 15:45:58 -0700360 return error;
361}
362
363#ifdef CLIB_UNIX
Dave Barachc3799992016-08-15 11:12:27 -0400364int
365main (int argc, char *argv[])
Ed Warnickecb9cada2015-12-08 15:45:58 -0700366{
367 unformat_input_t i;
Dave Barachc3799992016-08-15 11:12:27 -0400368 clib_error_t *error;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700369
Damjan Marion4dffd1c2018-09-03 12:30:36 +0200370 clib_mem_init (0, 64ULL << 20);
371
Ed Warnickecb9cada2015-12-08 15:45:58 -0700372 unformat_init_command_line (&i, argv);
373 error = test_timing_wheel_main (&i);
374 unformat_free (&i);
375 if (error)
376 {
377 clib_error_report (error);
378 return 1;
379 }
380 else
381 return 0;
382}
383#endif /* CLIB_UNIX */
Dave Barachc3799992016-08-15 11:12:27 -0400384
385/*
386 * fd.io coding-style-patch-verification: ON
387 *
388 * Local Variables:
389 * eval: (c-set-style "gnu")
390 * End:
391 */