API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_timer_perf.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2019-2026 Nokia
3 */
4
13#include <stdio.h>
14#include <string.h>
15#include <stdint.h>
16#include <inttypes.h>
17#include <signal.h>
18#include <stdlib.h>
19#include <getopt.h>
20
21#include <odp_api.h>
22#include <odp/helper/odph_api.h>
23
24#define MODE_SCHED_OVERH 0
25#define MODE_START_CANCEL 1
26#define MODE_START_EXPIRE 2
27#define MODE_TP_CTRL 3
28#define MAX_TIMER_POOLS 32
29#define MAX_TIMERS 10000
30#define START_NS (100 * ODP_TIME_MSEC_IN_NS)
31
32typedef struct test_options_t {
33 uint32_t num_cpu;
34 uint32_t num_tp;
35 uint32_t num_timer;
36 uint64_t res_ns;
37 uint64_t period_ns;
38 int shared;
39 int mode;
40 uint64_t test_rounds;
41
42} test_options_t;
43
44typedef struct time_stat_t {
45 uint64_t num;
46 uint64_t sum_ns;
47 uint64_t max_ns;
48
49} time_stat_t;
50
51typedef struct test_stat_t {
52 uint64_t rounds;
53 uint64_t events;
54 uint64_t nsec;
55 uint64_t cycles_0;
56 uint64_t cycles_1;
57
58 uint64_t cancels;
59 uint64_t starts;
60
61 time_stat_t before;
62 time_stat_t after;
63
64} test_stat_t;
65
66typedef struct test_stat_tp_ctrl_t {
67 uint64_t rounds;
68 uint64_t nsec;
69
70 uint64_t num_tp_create;
71 uint64_t tp_create_cycles;
72
73 uint64_t num_tp_start;
74 uint64_t tp_start_cycles;
75
76 uint64_t num_timer_alloc;
77 uint64_t timer_alloc_cycles;
78
79 uint64_t num_timer_free;
80 uint64_t timer_free_cycles;
81
82 uint64_t num_tp_destroy;
83 uint64_t tp_destroy_cycles;
84
85} test_stat_tp_ctrl_t;
86
87typedef struct test_stat_sum_t {
88 uint64_t rounds;
89 uint64_t events;
90 uint64_t nsec;
91 uint64_t cycles_0;
92 uint64_t cycles_1;
93
94 uint64_t cancels;
95 uint64_t starts;
96
97 time_stat_t before;
98 time_stat_t after;
99
100 double time_ave;
101 uint32_t num;
102
103} test_stat_sum_t;
104
105typedef struct thread_arg_t {
106 void *global;
107 int worker_idx;
108
109} thread_arg_t;
110
111typedef struct timer_ctx_t {
112 uint64_t target_ns;
113 uint64_t target_tick;
114 uint32_t tp_idx;
115 uint32_t timer_idx;
116 int last;
117
118} timer_ctx_t;
119
120typedef struct timer_pool_t {
122 uint64_t start_tick;
123 uint64_t period_tick;
124
125} timer_pool_t;
126
127typedef struct test_global_t {
128 test_options_t test_options;
129 odp_timer_pool_param_t timer_pool_param;
130 odp_atomic_u32_t exit_test;
131 odp_atomic_u32_t timers_started;
132 odp_barrier_t barrier;
133 odp_cpumask_t cpumask;
134 timer_pool_t timer_pool[MAX_TIMER_POOLS];
135 odp_pool_t pool[MAX_TIMER_POOLS];
136 odp_queue_t queue[MAX_TIMER_POOLS];
137 odp_timer_t timer[MAX_TIMER_POOLS][MAX_TIMERS];
138 timer_ctx_t timer_ctx[MAX_TIMER_POOLS][MAX_TIMERS];
139 odph_thread_t thread_tbl[ODP_THREAD_COUNT_MAX];
140 test_stat_t stat[ODP_THREAD_COUNT_MAX];
141 thread_arg_t thread_arg[ODP_THREAD_COUNT_MAX];
142 test_stat_sum_t stat_sum;
143 test_stat_tp_ctrl_t stat_tp_ctrl[ODP_THREAD_COUNT_MAX];
144
145} test_global_t;
146
147test_global_t *test_global;
148
149static void print_usage(void)
150{
151 printf("\n"
152 "Timer performance test\n"
153 "\n"
154 "Usage: odp_timer_perf [options]\n"
155 "\n"
156 " -c, --num_cpu Number of CPUs (worker threads). 0: all available CPUs. Default: 1\n"
157 " -n, --num_tp Number of timer pools. Default: 1\n"
158 " -t, --num_timer Number of timers per timer pool. Default: 10\n"
159 " -r, --res_ns Resolution in nsec. Default: 10000000\n"
160 " -p, --period_ns Timeout period in nsec. Default: 100000000\n"
161 " -s, --shared Shared vs private timer pool. Currently, modes 0-2 support private\n"
162 " pools only with single CPU. Default: 1\n"
163 " 0: Private timer pools\n"
164 " 1: Shared timer pools\n"
165 " -m, --mode Select test mode. Default: 0\n"
166 " 0: Measure odp_schedule() overhead when using timers\n"
167 " 1: Measure timer start + cancel performance\n"
168 " 2: Measure schedule and timer start overhead while continuously\n"
169 " restarting expiring timers\n"
170 " 3: Measure timer pool create/start/destroy and timer alloc/free\n"
171 " performance. Does not measure actual timer usage (start/expire).\n"
172 " Requires num timer pools (-n) >= num CPUs (-c).\n"
173 " -R, --rounds Number of test rounds. Default value is 50 for mode 3, otherwise 100000.\n"
174 " -h, --help This help\n"
175 "\n");
176}
177
178static int parse_options(int argc, char *argv[], test_options_t *test_options)
179{
180 int opt;
181 int ret = 0;
182
183 static const struct option longopts[] = {
184 {"num_cpu", required_argument, NULL, 'c'},
185 {"num_tp ", required_argument, NULL, 'n'},
186 {"num_timer", required_argument, NULL, 't'},
187 {"res_ns", required_argument, NULL, 'r'},
188 {"period_ns", required_argument, NULL, 'p'},
189 {"shared", required_argument, NULL, 's'},
190 {"mode", required_argument, NULL, 'm'},
191 {"rounds", required_argument, NULL, 'R'},
192 {"help", no_argument, NULL, 'h'},
193 {NULL, 0, NULL, 0}
194 };
195
196 static const char *shortopts = "+c:n:t:r:p:s:m:R:h";
197
198 test_options->num_cpu = 1;
199 test_options->num_tp = 1;
200 test_options->num_timer = 10;
201 test_options->res_ns = 10 * ODP_TIME_MSEC_IN_NS;
202 test_options->period_ns = 100 * ODP_TIME_MSEC_IN_NS;
203 test_options->shared = 1;
204 test_options->mode = 0;
205 test_options->test_rounds = 0;
206
207 while (1) {
208 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
209
210 if (opt == -1)
211 break;
212
213 switch (opt) {
214 case 'c':
215 test_options->num_cpu = atoi(optarg);
216 break;
217 case 'n':
218 test_options->num_tp = atoi(optarg);
219 break;
220 case 't':
221 test_options->num_timer = atoi(optarg);
222 break;
223 case 'r':
224 test_options->res_ns = atoll(optarg);
225 break;
226 case 'p':
227 test_options->period_ns = atoll(optarg);
228 break;
229 case 's':
230 test_options->shared = atoi(optarg);
231 break;
232 case 'm':
233 test_options->mode = atoi(optarg);
234 break;
235 case 'R':
236 test_options->test_rounds = atoll(optarg);
237 break;
238 case 'h':
239 /* fall through */
240 default:
241 print_usage();
242 ret = -1;
243 break;
244 }
245 }
246
247 if (test_options->num_timer > MAX_TIMERS) {
248 ODPH_ERR("Too many timers. Max %u\n", MAX_TIMERS);
249 ret = -1;
250 }
251
252 if (test_options->mode < 0 || test_options->mode > MODE_TP_CTRL) {
253 ODPH_ERR("Invalid mode %i\n", test_options->mode);
254 ret = -1;
255 }
256
257 if (test_options->test_rounds == 0) {
258 /* Set default rounds */
259 test_options->test_rounds = 100000;
260
261 if (test_options->mode == MODE_TP_CTRL)
262 test_options->test_rounds = 50;
263 }
264
265 return ret;
266}
267
268static int set_num_cpu(test_global_t *global, int use_workers)
269{
270 int ret;
271 test_options_t *test_options = &global->test_options;
272 uint32_t num_cpu = test_options->num_cpu;
273 int shared = test_options->shared;
274 int mode = test_options->mode;
275
276 /* One thread used for the main thread */
277 if (num_cpu > ODP_THREAD_COUNT_MAX - 1) {
278 ODPH_ERR("Too many workers. Maximum is %i.\n", ODP_THREAD_COUNT_MAX - 1);
279 return -1;
280 }
281
282 ret = odp_cpumask_default_worker(&global->cpumask, num_cpu);
283
284 if (num_cpu && (uint32_t)ret != num_cpu) {
285 ODPH_ERR("Too many workers. Max supported %i\n.", ret);
286 return -1;
287 }
288
289 if (shared == 0 && num_cpu != 1 && mode != MODE_TP_CTRL) {
290 ODPH_ERR("Private pool test supports only single CPU\n.");
291 return -1;
292 }
293
294 /* Zero: all available workers */
295 if (num_cpu == 0) {
296 num_cpu = ret;
297 test_options->num_cpu = num_cpu;
298 }
299
300 /* Timer pool control mode partitions the timer pools across workers,
301 * so each worker needs at least one timer pool. */
302 if (mode == MODE_TP_CTRL && test_options->num_tp < num_cpu) {
303 ODPH_ERR("Mode %i requires num timer pools (%u) >= num workers (%u)\n",
304 MODE_TP_CTRL, test_options->num_tp, num_cpu);
305 return -1;
306 }
307
308 if (use_workers) /* Main thread + all workers */
309 odp_barrier_init(&global->barrier, num_cpu + 1);
310 else /* Only the main thread */
311 odp_barrier_init(&global->barrier, 1);
312
313 return 0;
314}
315
316static void init_global_handles(test_global_t *global)
317{
318 uint32_t i, j;
319
320 for (i = 0; i < MAX_TIMER_POOLS; i++) {
321 global->timer_pool[i].tp = ODP_TIMER_POOL_INVALID;
322 global->pool[i] = ODP_POOL_INVALID;
323 global->queue[i] = ODP_QUEUE_INVALID;
324
325 for (j = 0; j < MAX_TIMERS; j++)
326 global->timer[i][j] = ODP_TIMER_INVALID;
327 }
328}
329
330static int prepare_timer_pool_param(test_global_t *global, odp_timer_pool_param_t *timer_pool_param)
331{
332 odp_timer_capability_t timer_capa;
333 odp_timer_res_capability_t timer_res_capa;
334 uint64_t max_tmo_ns, min_tmo_ns;
335 uint32_t max_timers;
336 int priv;
337 test_options_t *test_options = &global->test_options;
338 uint32_t num_cpu = test_options->num_cpu;
339 uint32_t num_tp = test_options->num_tp;
340 uint32_t num_timer = test_options->num_timer;
341 uint64_t res_ns = test_options->res_ns;
342 uint64_t period_ns = test_options->period_ns;
343 int mode = test_options->mode;
344
345 max_tmo_ns = START_NS + (num_timer * period_ns);
346 min_tmo_ns = START_NS / 2;
347
348 if (mode == MODE_START_EXPIRE) {
349 /*
350 * Timers are started 1-2 periods from current time. Add an
351 * arbitrary margin of one period, resulting in maximum of
352 * three periods.
353 */
354 max_tmo_ns = period_ns * 3;
355 min_tmo_ns = test_options->res_ns / 2;
356 }
357
358 priv = 0;
359 if (test_options->shared == 0)
360 priv = 1;
361
362 printf("\nTimer performance test\n");
363 printf(" mode %i\n", mode);
364 printf(" num cpu %u\n", num_cpu);
365 printf(" private pool %i\n", priv);
366 printf(" num timer pool %u\n", num_tp);
367 printf(" num timer %u\n", num_timer);
368 printf(" resolution %" PRIu64 " nsec\n", res_ns);
369 printf(" period %" PRIu64 " nsec\n", period_ns);
370 printf(" max timeout %" PRIu64 " nsec\n", max_tmo_ns);
371 printf(" min timeout %" PRIu64 " nsec\n", min_tmo_ns);
372 printf(" first timer at %.2f sec\n", (double)START_NS / ODP_TIME_SEC_IN_NS);
373 if (mode == MODE_SCHED_OVERH)
374 printf(" test duration %.2f sec\n", (double)max_tmo_ns / ODP_TIME_SEC_IN_NS);
375 else
376 printf(" test rounds %" PRIu64 "\n", test_options->test_rounds);
377
378 if (odp_timer_capability(ODP_CLOCK_DEFAULT, &timer_capa)) {
379 ODPH_ERR("Timer capability failed\n");
380 return -1;
381 }
382
383 memset(&timer_res_capa, 0, sizeof(odp_timer_res_capability_t));
384 timer_res_capa.res_ns = res_ns;
385 if (odp_timer_res_capability(ODP_CLOCK_DEFAULT, &timer_res_capa)) {
386 ODPH_ERR("Timer resolution capability failed\n");
387 return -1;
388 }
389
390 if (res_ns < timer_capa.max_res.res_ns) {
391 ODPH_ERR("Too high resolution\n");
392 return -1;
393 }
394
395 if (min_tmo_ns < timer_res_capa.min_tmo) {
396 ODPH_ERR("Too short min timeout\n");
397 return -1;
398 }
399
400 if (max_tmo_ns > timer_res_capa.max_tmo) {
401 ODPH_ERR("Too long max timeout\n");
402 return -1;
403 }
404
405 max_timers = timer_capa.max_timers;
406 if (max_timers && num_timer > max_timers) {
407 ODPH_ERR("Too many timers (max %u)\n", max_timers);
408 return -1;
409 }
410
411 if (num_tp > MAX_TIMER_POOLS || num_tp > timer_capa.max_pools) {
412 ODPH_ERR("Too many timer pools (max supported %u, max capa %u)\n",
413 MAX_TIMER_POOLS, timer_capa.max_pools);
414 return -1;
415 }
416
417 odp_timer_pool_param_init(timer_pool_param);
418 timer_pool_param->res_ns = res_ns;
419 timer_pool_param->min_tmo = min_tmo_ns;
420 timer_pool_param->max_tmo = max_tmo_ns;
421 timer_pool_param->num_timers = num_timer;
422 timer_pool_param->priv = priv;
423 timer_pool_param->clk_src = ODP_CLOCK_DEFAULT;
424
425 return 0;
426}
427
428static int create_timer_pools(test_global_t *global, odp_timer_pool_param_t *timer_pool_param)
429{
431 uint32_t i;
432 test_options_t *test_options = &global->test_options;
433 uint32_t num_tp = test_options->num_tp;
434 char tp_name[] = "timer_pool_00";
435
436 for (i = 0; i < num_tp; i++) {
437 if (num_tp < 100) {
438 tp_name[11] = '0' + i / 10;
439 tp_name[12] = '0' + i % 10;
440 }
441
442 tp = odp_timer_pool_create(tp_name, timer_pool_param);
443 global->timer_pool[i].tp = tp;
444 if (tp == ODP_TIMER_POOL_INVALID) {
445 ODPH_ERR("Timer pool create failed (%u)\n", i);
446 return -1;
447 }
448
449 if (odp_timer_pool_start_multi(&tp, 1) != 1) {
450 ODPH_ERR("Timer pool start failed (%u)\n", i);
451 return -1;
452 }
453
454 global->timer_pool[i].period_tick = odp_timer_ns_to_tick(tp,
455 test_options->period_ns);
456 global->timer_pool[i].start_tick = odp_timer_ns_to_tick(tp, START_NS);
457 }
458
459 printf(" start %" PRIu64 " tick\n", global->timer_pool[0].start_tick);
460 printf(" period %" PRIu64 " ticks\n", global->timer_pool[0].period_tick);
461 printf("\n");
462
463 return 0;
464}
465
466static int create_event_pools(test_global_t *global)
467{
468 odp_pool_param_t pool_param;
469 odp_pool_t pool;
470 uint32_t i;
471 test_options_t *test_options = &global->test_options;
472 uint32_t num_tp = test_options->num_tp;
473 uint32_t num_timer = test_options->num_timer;
474 char name[] = "timer_pool_00";
475
476 odp_pool_param_init(&pool_param);
477 pool_param.type = ODP_POOL_TIMEOUT;
478 pool_param.tmo.num = num_timer;
479
480 for (i = 0; i < num_tp; i++) {
481 if (num_tp < 100) {
482 name[11] = '0' + i / 10;
483 name[12] = '0' + i % 10;
484 }
485
486 pool = odp_pool_create(name, &pool_param);
487 global->pool[i] = pool;
488 if (pool == ODP_POOL_INVALID) {
489 ODPH_ERR("Pool create failed (%u)\n", i);
490 return -1;
491 }
492 }
493
494 return 0;
495}
496
497static int create_event_queues(test_global_t *global)
498{
499 odp_queue_param_t queue_param;
500 odp_queue_t queue;
501 uint32_t i;
502 test_options_t *test_options = &global->test_options;
503 uint32_t num_tp = test_options->num_tp;
504 char name[] = "timer_pool_00";
505
506 odp_queue_param_init(&queue_param);
507 queue_param.type = ODP_QUEUE_TYPE_SCHED;
508 queue_param.sched.prio = odp_schedule_default_prio();
509 queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC;
510 queue_param.sched.group = ODP_SCHED_GROUP_ALL;
511
512 for (i = 0; i < num_tp; i++) {
513 if (num_tp < 100) {
514 name[11] = '0' + i / 10;
515 name[12] = '0' + i % 10;
516 }
517
518 queue = odp_queue_create(name, &queue_param);
519 global->queue[i] = queue;
520 if (queue == ODP_QUEUE_INVALID) {
521 ODPH_ERR("Queue create failed (%u)\n", i);
522 return -1;
523 }
524 }
525
526 return 0;
527}
528
529static int start_timers(test_global_t *global)
530{
531 odp_timer_pool_info_t timer_pool_info;
533 odp_timer_t timer;
534 odp_pool_t pool;
535 odp_queue_t queue;
536 odp_time_t time;
537 uint64_t tick_cur, nsec, time_ns;
538 uint64_t max_tmo_ns;
539 uint32_t i, j;
540 test_options_t *test_options = &global->test_options;
541 uint32_t num_tp = test_options->num_tp;
542 uint32_t num_timer = test_options->num_timer;
543 uint64_t period_ns = test_options->period_ns;
544
545 max_tmo_ns = START_NS + (num_timer * period_ns);
546
547 for (i = 0; i < num_tp; i++) {
548 tp = global->timer_pool[i].tp;
549 pool = global->pool[i];
550 queue = global->queue[i];
551
552 nsec = max_tmo_ns;
553 tick_cur = odp_timer_current_tick(tp);
554 time = odp_time_global();
555 time_ns = odp_time_to_ns(time);
556
557 for (j = 0; j < num_timer; j++) {
558 uint64_t tick_ns;
559 odp_timeout_t timeout;
560 odp_event_t ev;
561 int status;
562 timer_ctx_t *ctx = &global->timer_ctx[i][j];
563 odp_timer_start_t start_param;
564
565 /* Start timers backwards, the last timer is started first */
566 if (j == 0)
567 ctx->last = 1;
568
569 ctx->target_ns = time_ns + nsec;
570 ctx->tp_idx = i;
571 ctx->timer_idx = j;
572
573 timeout = odp_timeout_alloc(pool);
574 ev = odp_timeout_to_event(timeout);
575
576 timer = odp_timer_alloc(tp, queue, ctx);
577 global->timer[i][j] = timer;
578
579 tick_ns = odp_timer_ns_to_tick(tp, nsec);
580 nsec = nsec - period_ns;
581
582 start_param.tick_type = ODP_TIMER_TICK_ABS;
583 start_param.tick = tick_cur + tick_ns;
584 start_param.tmo_ev = ev;
585
586 if (test_options->mode == MODE_START_EXPIRE) {
587 uint64_t offset_ns = period_ns + j * period_ns / num_timer;
588
589 ctx->target_ns = time_ns + offset_ns;
590 ctx->target_tick = tick_cur + odp_timer_ns_to_tick(tp, offset_ns);
591 start_param.tick = ctx->target_tick;
592 }
593
594 status = odp_timer_start(timer, &start_param);
595 if (status != ODP_TIMER_SUCCESS) {
596 ODPH_ERR("Timer start %i/%i (ret %i)\n", i, j, status);
597 return -1;
598 }
599 }
600
601 if (odp_timer_pool_info(tp, &timer_pool_info)) {
602 ODPH_ERR("Timer pool info failed\n");
603 return -1;
604 }
605
606 printf("Timer pool info [%i]:\n", i);
607 printf(" cur_timers %u\n", timer_pool_info.cur_timers);
608 printf(" hwm_timers %u\n", timer_pool_info.hwm_timers);
609 printf("\n");
610 }
611
612 return 0;
613}
614
615static int destroy_timer_pool(test_global_t *global)
616{
618 odp_pool_t pool;
619 odp_queue_t queue;
620 odp_timer_t timer;
621 uint32_t i, j;
622 test_options_t *test_options = &global->test_options;
623 uint32_t num_timer = test_options->num_timer;
624 uint32_t num_tp = test_options->num_tp;
625
626 for (i = 0; i < num_tp; i++) {
627 for (j = 0; j < num_timer; j++) {
628 timer = global->timer[i][j];
629
630 if (timer == ODP_TIMER_INVALID)
631 break;
632
633 if (odp_timer_free(timer))
634 printf("Timer free failed: %i/%i\n", i, j);
635 }
636
637 queue = global->queue[i];
638 if (queue != ODP_QUEUE_INVALID)
639 odp_queue_destroy(queue);
640
641 pool = global->pool[i];
642 if (pool != ODP_POOL_INVALID)
643 odp_pool_destroy(pool);
644
645 tp = global->timer_pool[i].tp;
646 if (tp != ODP_TIMER_POOL_INVALID)
648 }
649
650 return 0;
651}
652
653static int sched_mode_worker(void *arg)
654{
655 int thr;
656 uint32_t exit_test;
657 odp_event_t ev;
658 odp_timeout_t tmo;
659 uint64_t c2, diff, nsec, time_ns, target_ns;
660 odp_time_t t1, t2, time;
661 time_stat_t before, after;
662 timer_ctx_t *ctx;
663 thread_arg_t *thread_arg = arg;
664 test_global_t *global = thread_arg->global;
665 test_options_t *test_options = &global->test_options;
666 uint32_t num_tp = test_options->num_tp;
667 uint64_t cycles = 0;
668 uint64_t events = 0;
669 uint64_t rounds = 0;
670 uint64_t c1 = 0;
671 int meas = 1;
672 int ret = 0;
673
674 memset(&before, 0, sizeof(time_stat_t));
675 memset(&after, 0, sizeof(time_stat_t));
676
677 thr = odp_thread_id();
678
679 /* Start all workers at the same time */
680 odp_barrier_wait(&global->barrier);
681
682 t1 = odp_time_local();
683
684 while (1) {
685 if (meas) {
686 c1 = odp_cpu_cycles();
687 meas = 0;
688 }
689
691 rounds++;
692
693 exit_test = odp_atomic_load_u32(&global->exit_test);
694 if (odp_likely(ev == ODP_EVENT_INVALID && exit_test < num_tp))
695 continue;
696
697 c2 = odp_cpu_cycles();
698 diff = odp_cpu_cycles_diff(c2, c1);
699 cycles += diff;
700
701 if (ev == ODP_EVENT_INVALID && exit_test >= num_tp)
702 break;
703
704 time = odp_time_global();
705 time_ns = odp_time_to_ns(time);
706 events++;
707 meas = 1;
708
709 tmo = odp_timeout_from_event(ev);
710 ctx = odp_timeout_user_ptr(tmo);
711 odp_timeout_free(tmo);
712
713 target_ns = ctx->target_ns;
714 if (time_ns < target_ns) {
715 diff = target_ns - time_ns;
716 before.num++;
717 before.sum_ns += diff;
718 if (diff > before.max_ns)
719 before.max_ns = diff;
720
721 ODPH_DBG("before %" PRIu64 "\n", diff);
722 } else {
723 diff = time_ns - target_ns;
724 after.num++;
725 after.sum_ns += diff;
726 if (diff > after.max_ns)
727 after.max_ns = diff;
728
729 ODPH_DBG("after %" PRIu64 "\n", time_ns - target_ns);
730 }
731
732 if (ctx->last)
733 odp_atomic_inc_u32(&global->exit_test);
734 }
735
736 t2 = odp_time_local();
737 nsec = odp_time_diff_ns(t2, t1);
738
739 /* Update stats*/
740 global->stat[thr].events = events;
741 global->stat[thr].cycles_0 = cycles;
742 global->stat[thr].rounds = rounds;
743 global->stat[thr].nsec = nsec;
744 global->stat[thr].before = before;
745 global->stat[thr].after = after;
746
747 return ret;
748}
749
750static int cancel_timers(test_global_t *global, uint32_t worker_idx)
751{
752 uint32_t i, j;
753 int r;
754 odp_timer_t timer;
755 odp_event_t ev;
756 test_options_t *test_options = &global->test_options;
757 uint32_t num_tp = test_options->num_tp;
758 uint32_t num_timer = test_options->num_timer;
759 uint32_t num_worker = test_options->num_cpu;
760 int ret = 0;
761
762 for (i = 0; i < num_tp; i++) {
763 for (j = worker_idx; j < num_timer; j += num_worker) {
764 timer = global->timer[i][j];
765 if (timer == ODP_TIMER_INVALID)
766 continue;
767
768 r = odp_timer_cancel(timer, &ev);
769
770 if (r == ODP_TIMER_SUCCESS) {
771 odp_event_free(ev);
772 } else if (r == ODP_TIMER_TOO_NEAR) {
773 ret = 1;
774 } else {
775 ret = -1;
776 break;
777 }
778 }
779 }
780
781 return ret;
782}
783
784static int start_cancel_mode_worker(void *arg)
785{
786 uint64_t tick, start_tick, period_tick, nsec;
787 uint64_t c1, c2;
788 int thr, status;
789 uint32_t i, j, worker_idx;
790 odp_event_t ev;
791 odp_time_t t1, t2;
792 odp_timer_t timer;
794 odp_timeout_t tmo;
795 odp_timer_start_t start_param;
796 thread_arg_t *thread_arg = arg;
797 test_global_t *global = thread_arg->global;
798 test_options_t *test_options = &global->test_options;
799 uint32_t num_tp = test_options->num_tp;
800 uint32_t num_timer = test_options->num_timer;
801 uint32_t num_worker = test_options->num_cpu;
802 int ret = 0;
803 int started = 0;
804 uint64_t test_rounds = test_options->test_rounds;
805 uint64_t num_tmo = 0;
806 uint64_t num_cancel = 0;
807 uint64_t num_start = 0;
808 uint64_t cancel_cycles = 0, start_cycles = 0;
809 odp_event_t ev_tbl[MAX_TIMERS];
810
811 thr = odp_thread_id();
812 worker_idx = thread_arg->worker_idx;
813 t1 = ODP_TIME_NULL;
814
815 /* Start all workers at the same time */
816 odp_barrier_wait(&global->barrier);
817
818 while (1) {
820
821 if (odp_unlikely(ev != ODP_EVENT_INVALID)) {
822 /* Timeout, start timer again. When start_tick is large enough, this should
823 * not happen. */
824 timer_ctx_t *ctx;
825
826 tmo = odp_timeout_from_event(ev);
827 ctx = odp_timeout_user_ptr(tmo);
828 i = ctx->tp_idx;
829 j = ctx->timer_idx;
830 timer = global->timer[i][j];
831 start_tick = global->timer_pool[i].start_tick;
832 period_tick = global->timer_pool[i].period_tick;
833 tick = start_tick + j * period_tick;
834
835 start_param.tick_type = ODP_TIMER_TICK_REL;
836 start_param.tick = tick;
837 start_param.tmo_ev = ev;
838
839 status = odp_timer_start(timer, &start_param);
840 num_tmo++;
841 num_start++;
842
843 if (status != ODP_TIMER_SUCCESS) {
844 ODPH_ERR("Timer start (tmo) failed (ret %i)\n", status);
845 ret = -1;
846 break;
847 }
848
849 continue;
850 }
851
852 if (odp_unlikely(odp_atomic_load_u32(&global->exit_test)))
853 break;
854
855 if (odp_unlikely(started == 0)) {
856 /* Run schedule loop while waiting for timers to be created */
857 if (odp_atomic_load_acq_u32(&global->timers_started) == 0)
858 continue;
859
860 /* Start measurements */
861 started = 1;
862 t1 = odp_time_local();
863 }
864
865 /* Cancel and start timers again */
866 for (i = 0; i < num_tp; i++) {
867 tp = global->timer_pool[i].tp;
868 if (tp == ODP_TIMER_POOL_INVALID)
869 continue;
870
871 start_tick = global->timer_pool[i].start_tick;
872 period_tick = global->timer_pool[i].period_tick;
873
874 tick = odp_timer_current_tick(tp) + start_tick;
875 c1 = odp_cpu_cycles();
876
877 for (j = worker_idx; j < num_timer; j += num_worker) {
878 ev_tbl[j] = ODP_EVENT_INVALID;
879
880 timer = global->timer[i][j];
881 if (timer == ODP_TIMER_INVALID)
882 continue;
883
884 status = odp_timer_cancel(timer, &ev_tbl[j]);
885 num_cancel++;
886
887 if (odp_unlikely(status == ODP_TIMER_TOO_NEAR)) {
888 continue;
889 } else if (odp_unlikely(status != ODP_TIMER_SUCCESS)) {
890 ODPH_ERR("Timer (%u/%u) cancel failed (ret %i)\n", i, j,
891 status);
892 ret = -1;
893 break;
894 }
895 }
896
897 c2 = odp_cpu_cycles();
898 cancel_cycles += odp_cpu_cycles_diff(c2, c1);
899 c1 = c2;
900
901 for (j = worker_idx; j < num_timer; j += num_worker) {
902 if (ev_tbl[j] == ODP_EVENT_INVALID)
903 continue;
904
905 timer = global->timer[i][j];
906 if (timer == ODP_TIMER_INVALID)
907 continue;
908
909 start_param.tick_type = ODP_TIMER_TICK_ABS;
910 start_param.tick = tick + j * period_tick;
911 start_param.tmo_ev = ev_tbl[j];
912
913 status = odp_timer_start(timer, &start_param);
914 num_start++;
915
916 if (status != ODP_TIMER_SUCCESS) {
917 ODPH_ERR("Timer (%u/%u) start failed (ret %i)\n", i, j,
918 status);
919 ret = -1;
920 break;
921 }
922 }
923
924 c2 = odp_cpu_cycles();
925 start_cycles += odp_cpu_cycles_diff(c2, c1);
926 }
927
928 if (test_rounds) {
929 test_rounds--;
930 if (test_rounds == 0)
931 break;
932 }
933 }
934
935 t2 = odp_time_local();
936 nsec = odp_time_diff_ns(t2, t1);
937
938 /* Cancel all timers that belong to this thread */
939 if (cancel_timers(global, worker_idx))
940 ODPH_ERR("Timer cancel failed\n");
941
942 /* Update stats */
943 global->stat[thr].events = num_tmo;
944 global->stat[thr].rounds = test_options->test_rounds - test_rounds;
945 global->stat[thr].nsec = nsec;
946 global->stat[thr].cycles_0 = cancel_cycles;
947 global->stat[thr].cycles_1 = start_cycles;
948
949 global->stat[thr].cancels = num_cancel;
950 global->stat[thr].starts = num_start;
951
952 return ret;
953}
954
955static int start_expire_mode_worker(void *arg)
956{
957 int status, thr;
958 uint32_t i, j, exit_test;
959 odp_event_t ev;
960 odp_timeout_t tmo;
961 uint64_t c2, c3, c4, diff, nsec, time_ns, target_ns, period_tick, wait;
962 odp_timer_t timer;
963 odp_timer_start_t start_param;
964 odp_time_t t1, t2;
965 time_stat_t before, after;
966 timer_ctx_t *ctx;
967 thread_arg_t *thread_arg = arg;
968 test_global_t *global = thread_arg->global;
969 test_options_t *opt = &global->test_options;
970 uint32_t num_tp = opt->num_tp;
971 uint64_t sched_cycles = 0;
972 uint64_t start_cycles = 0;
973 uint64_t events = 0;
974 uint64_t rounds = 0;
975 uint64_t c1 = 0;
976 int meas = 1;
977 int ret = 0;
978
979 memset(&before, 0, sizeof(time_stat_t));
980 memset(&after, 0, sizeof(time_stat_t));
981
982 thr = odp_thread_id();
983
984 /* Start all workers at the same time */
985 odp_barrier_wait(&global->barrier);
986
987 t1 = odp_time_local();
988
989 while (events < opt->test_rounds * opt->num_timer / opt->num_cpu) {
990 if (meas) {
991 c1 = odp_cpu_cycles();
992 meas = 0;
993 }
994
996 rounds++;
997
998 exit_test = odp_atomic_load_u32(&global->exit_test);
999 if (odp_likely(ev == ODP_EVENT_INVALID && exit_test < num_tp))
1000 continue;
1001
1002 c2 = odp_cpu_cycles();
1003 diff = odp_cpu_cycles_diff(c2, c1);
1004 sched_cycles += diff;
1005
1006 if (ev == ODP_EVENT_INVALID && exit_test >= num_tp)
1007 break;
1008
1009 events++;
1010 meas = 1;
1011 tmo = odp_timeout_from_event(ev);
1012 ctx = odp_timeout_user_ptr(tmo);
1013 i = ctx->tp_idx;
1014 j = ctx->timer_idx;
1015 timer = global->timer[i][j];
1016 period_tick = global->timer_pool[i].period_tick;
1017 time_ns = odp_time_global_ns();
1018 target_ns = ctx->target_ns;
1019
1020 if (time_ns < target_ns) {
1021 diff = target_ns - time_ns;
1022 before.num++;
1023 before.sum_ns += diff;
1024 if (diff > before.max_ns)
1025 before.max_ns = diff;
1026
1027 ODPH_DBG("before %" PRIu64 "\n", diff);
1028 } else {
1029 diff = time_ns - target_ns;
1030 after.num++;
1031 after.sum_ns += diff;
1032 if (diff > after.max_ns)
1033 after.max_ns = diff;
1034
1035 ODPH_DBG("after %" PRIu64 "\n", diff);
1036 }
1037
1038 /* Start the timer again */
1039 start_param.tick_type = ODP_TIMER_TICK_ABS;
1040 ctx->target_ns += opt->period_ns;
1041 ctx->target_tick += period_tick;
1042 start_param.tick = ctx->target_tick;
1043 start_param.tmo_ev = ev;
1044 c3 = odp_cpu_cycles();
1045
1046 status = odp_timer_start(timer, &start_param);
1047
1048 c4 = odp_cpu_cycles();
1049 diff = odp_cpu_cycles_diff(c4, c3);
1050 start_cycles += diff;
1051
1052 if (status != ODP_TIMER_SUCCESS) {
1053 ODPH_ERR("Timer start (tmo) failed (ret %i)\n", status);
1054 ret = -1;
1055 break;
1056 }
1057 }
1058
1059 t2 = odp_time_local();
1060 nsec = odp_time_diff_ns(t2, t1);
1061
1062 /* Cancel all timers that belong to this thread */
1063 status = cancel_timers(global, thread_arg->worker_idx);
1064
1065 wait = ODP_SCHED_NO_WAIT;
1066 if (status > 0)
1067 wait = odp_schedule_wait_time(opt->period_ns);
1068
1069 /* Wait and free remaining events */
1070 while (1) {
1071 ev = odp_schedule(NULL, wait);
1072 if (ev == ODP_EVENT_INVALID)
1073 break;
1074 odp_event_free(ev);
1075 }
1076
1077 /* Update stats*/
1078 global->stat[thr].events = events;
1079 global->stat[thr].cycles_0 = sched_cycles;
1080 global->stat[thr].cycles_1 = start_cycles;
1081 global->stat[thr].rounds = rounds;
1082 global->stat[thr].nsec = nsec;
1083 global->stat[thr].before = before;
1084 global->stat[thr].after = after;
1085
1086 return ret;
1087}
1088
1089static int timer_pool_ctrl_mode_worker(void *arg)
1090{
1091 odp_timer_pool_t tp[MAX_TIMER_POOLS];
1092 odp_queue_t queue[MAX_TIMER_POOLS];
1093 odp_timer_t timer;
1094 odp_time_t t1, t2;
1095 uint64_t c1, c2;
1096 uint32_t i, j, idx;
1097 uint64_t round;
1098 int thr, rv;
1099 int ret = 0;
1100 thread_arg_t *thread_arg = arg;
1101 test_global_t *global = thread_arg->global;
1102 test_options_t *test_options = &global->test_options;
1103 odp_timer_pool_param_t *timer_pool_param = &global->timer_pool_param;
1104 uint32_t num_tp = test_options->num_tp;
1105 uint32_t num_timer = test_options->num_timer;
1106 uint32_t num_worker = test_options->num_cpu;
1107 uint64_t rounds = test_options->test_rounds;
1108 uint32_t worker_idx = thread_arg->worker_idx;
1109 test_stat_tp_ctrl_t *stat;
1110 char tp_name[] = "timer_pool_00";
1111 /* Partition timer pools evenly across workers. The first 'extra'
1112 * workers handle one additional timer pool when the division is not
1113 * even. As num_tp >= num_worker, every worker handles at least one
1114 * timer pool. */
1115 uint32_t base = num_tp / num_worker;
1116 uint32_t extra = num_tp % num_worker;
1117 uint32_t num_tp_worker = base + (worker_idx < extra ? 1 : 0);
1118 uint32_t first_tp = (worker_idx * base) + (worker_idx < extra ? worker_idx : extra);
1119
1120 thr = odp_thread_id();
1121 stat = &global->stat_tp_ctrl[thr];
1122 memset(stat, 0, sizeof(test_stat_tp_ctrl_t));
1123
1124 /* This worker's pre-created queues */
1125 for (i = 0; i < num_tp_worker; i++)
1126 queue[i] = global->queue[first_tp + i];
1127
1128 /* Start all workers at the same time */
1129 odp_barrier_wait(&global->barrier);
1130
1131 t1 = odp_time_local();
1132
1133 for (round = 0; round < rounds; round++) {
1134 /* Create timer pools */
1135 for (i = 0; i < num_tp_worker; i++) {
1136 idx = first_tp + i;
1137
1138 if (num_tp < 100) {
1139 tp_name[11] = '0' + idx / 10;
1140 tp_name[12] = '0' + idx % 10;
1141 }
1142
1143 c1 = odp_cpu_cycles_strict();
1144 tp[i] = odp_timer_pool_create(tp_name, timer_pool_param);
1145 c2 = odp_cpu_cycles_strict();
1146
1147 if (tp[i] == ODP_TIMER_POOL_INVALID) {
1148 ODPH_ERR("Timer pool create failed (%u)\n", idx);
1149 ret = -1;
1150 goto error;
1151 }
1152
1153 stat->tp_create_cycles += odp_cpu_cycles_diff(c2, c1);
1154 stat->num_tp_create++;
1155 }
1156
1157 /* Start timer pools */
1158 for (i = 0; i < num_tp_worker; i++) {
1159 c1 = odp_cpu_cycles_strict();
1160 rv = odp_timer_pool_start_multi(&tp[i], 1);
1161 c2 = odp_cpu_cycles_strict();
1162
1163 if (rv != 1) {
1164 ODPH_ERR("Timer pool start failed (%u)\n", first_tp + i);
1165 ret = -1;
1166 goto error;
1167 }
1168
1169 stat->tp_start_cycles += odp_cpu_cycles_diff(c2, c1);
1170 stat->num_tp_start++;
1171 }
1172
1173 /* Allocate timers */
1174 for (i = 0; i < num_tp_worker; i++) {
1175 idx = first_tp + i;
1176
1177 for (j = 0; j < num_timer; j++) {
1178 c1 = odp_cpu_cycles_strict();
1179 timer = odp_timer_alloc(tp[i], queue[i], NULL);
1180 c2 = odp_cpu_cycles_strict();
1181
1182 global->timer[idx][j] = timer;
1183 if (timer == ODP_TIMER_INVALID) {
1184 ODPH_ERR("Timer alloc failed (%u/%u)\n", idx, j);
1185 ret = -1;
1186 goto error;
1187 }
1188
1189 stat->timer_alloc_cycles += odp_cpu_cycles_diff(c2, c1);
1190 stat->num_timer_alloc++;
1191 }
1192 }
1193
1194 /* Free timers */
1195 for (i = 0; i < num_tp_worker; i++) {
1196 idx = first_tp + i;
1197
1198 for (j = 0; j < num_timer; j++) {
1199 timer = global->timer[idx][j];
1200
1201 c1 = odp_cpu_cycles_strict();
1202 rv = odp_timer_free(timer);
1203 c2 = odp_cpu_cycles_strict();
1204
1205 global->timer[idx][j] = ODP_TIMER_INVALID;
1206 if (rv) {
1207 ODPH_ERR("Timer free failed (%u/%u)\n", idx, j);
1208 ret = -1;
1209 goto error;
1210 }
1211
1212 stat->timer_free_cycles += odp_cpu_cycles_diff(c2, c1);
1213 stat->num_timer_free++;
1214 }
1215 }
1216
1217 /* Destroy timer pools */
1218 for (i = 0; i < num_tp_worker; i++) {
1219 c1 = odp_cpu_cycles_strict();
1221 c2 = odp_cpu_cycles_strict();
1222
1223 tp[i] = ODP_TIMER_POOL_INVALID;
1224 stat->tp_destroy_cycles += odp_cpu_cycles_diff(c2, c1);
1225 stat->num_tp_destroy++;
1226 }
1227
1228 stat->rounds++;
1229 }
1230
1231error:
1232 t2 = odp_time_local();
1233 stat->nsec = odp_time_diff_ns(t2, t1);
1234
1235 return ret;
1236}
1237
1238static int start_workers(test_global_t *global, odp_instance_t instance)
1239{
1240 odph_thread_common_param_t thr_common;
1241 int i, ret;
1242 test_options_t *test_options = &global->test_options;
1243 int num_cpu = test_options->num_cpu;
1244 odph_thread_param_t thr_param[num_cpu];
1245
1246 memset(global->thread_tbl, 0, sizeof(global->thread_tbl));
1247 odph_thread_common_param_init(&thr_common);
1248
1249 thr_common.instance = instance;
1250 thr_common.cpumask = &global->cpumask;
1251
1252 for (i = 0; i < num_cpu; i++) {
1253 odph_thread_param_init(&thr_param[i]);
1254
1255 if (test_options->mode == MODE_SCHED_OVERH)
1256 thr_param[i].start = sched_mode_worker;
1257 else if (test_options->mode == MODE_START_CANCEL)
1258 thr_param[i].start = start_cancel_mode_worker;
1259 else if (test_options->mode == MODE_TP_CTRL)
1260 thr_param[i].start = timer_pool_ctrl_mode_worker;
1261 else
1262 thr_param[i].start = start_expire_mode_worker;
1263
1264 thr_param[i].arg = &global->thread_arg[i];
1265 thr_param[i].thr_type = ODP_THREAD_WORKER;
1266 }
1267
1268 ret = odph_thread_create(global->thread_tbl, &thr_common, thr_param,
1269 num_cpu);
1270
1271 if (ret != num_cpu) {
1272 ODPH_ERR("Thread create failed %i\n", ret);
1273 return -1;
1274 }
1275
1276 return 0;
1277}
1278
1279static void sum_stat(test_global_t *global)
1280{
1281 int i;
1282 test_stat_sum_t *sum = &global->stat_sum;
1283
1284 memset(sum, 0, sizeof(test_stat_sum_t));
1285
1286 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1287 if (global->stat[i].rounds == 0)
1288 continue;
1289
1290 sum->num++;
1291 sum->events += global->stat[i].events;
1292 sum->rounds += global->stat[i].rounds;
1293 sum->cycles_0 += global->stat[i].cycles_0;
1294 sum->cycles_1 += global->stat[i].cycles_1;
1295 sum->nsec += global->stat[i].nsec;
1296 sum->cancels += global->stat[i].cancels;
1297 sum->starts += global->stat[i].starts;
1298
1299 sum->before.num += global->stat[i].before.num;
1300 sum->before.sum_ns += global->stat[i].before.sum_ns;
1301 sum->after.num += global->stat[i].after.num;
1302 sum->after.sum_ns += global->stat[i].after.sum_ns;
1303
1304 if (global->stat[i].before.max_ns > sum->before.max_ns)
1305 sum->before.max_ns = global->stat[i].before.max_ns;
1306
1307 if (global->stat[i].after.max_ns > sum->after.max_ns)
1308 sum->after.max_ns = global->stat[i].after.max_ns;
1309 }
1310
1311 if (sum->num)
1312 sum->time_ave = ((double)sum->nsec / sum->num) / ODP_TIME_SEC_IN_NS;
1313}
1314
1315static void print_stat_sched_mode(test_global_t *global)
1316{
1317 int i;
1318 test_stat_sum_t *sum = &global->stat_sum;
1319 double round_ave = 0.0;
1320 double before_ave = 0.0;
1321 double after_ave = 0.0;
1322 int num = 0;
1323
1324 printf("\n");
1325 printf("RESULTS - schedule() cycles per thread:\n");
1326 printf("----------------------------------------------\n");
1327 printf(" 1 2 3 4 5 6 7 8 9 10");
1328
1329 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1330 if (global->stat[i].rounds) {
1331 if ((num % 10) == 0)
1332 printf("\n ");
1333
1334 printf("%6.1f ", (double)global->stat[i].cycles_0 / global->stat[i].rounds);
1335 num++;
1336 }
1337 }
1338
1339 printf("\n\n");
1340
1341 if (sum->num)
1342 round_ave = (double)sum->rounds / sum->num;
1343
1344 if (sum->before.num)
1345 before_ave = (double)sum->before.sum_ns / sum->before.num;
1346
1347 if (sum->after.num)
1348 after_ave = (double)sum->after.sum_ns / sum->after.num;
1349
1350 printf("TOTAL (%i workers)\n", sum->num);
1351 printf(" events: %" PRIu64 "\n", sum->events);
1352 printf(" ave time: %.2f sec\n", sum->time_ave);
1353 printf(" ave rounds per sec: %.2fM\n", (round_ave / sum->time_ave) / 1000000.0);
1354 printf(" num before: %" PRIu64 "\n", sum->before.num);
1355 printf(" ave before: %.1f nsec\n", before_ave);
1356 printf(" max before: %" PRIu64 " nsec\n", sum->before.max_ns);
1357 printf(" num after: %" PRIu64 "\n", sum->after.num);
1358 printf(" ave after: %.1f nsec\n", after_ave);
1359 printf(" max after: %" PRIu64 " nsec\n", sum->after.max_ns);
1360 printf("\n");
1361}
1362
1363static void print_stat_start_cancel_mode(test_global_t *global)
1364{
1365 int i;
1366 test_stat_sum_t *sum = &global->stat_sum;
1367 double start_ave = 0.0;
1368 int num = 0;
1369
1370 printf("\n");
1371 printf("RESULTS\n");
1372 printf("odp_timer_cancel() cycles per thread:\n");
1373 printf("-------------------------------------------------\n");
1374 printf(" 1 2 3 4 5 6 7 8 9 10");
1375
1376 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1377 const test_stat_t *si = &global->stat[i];
1378
1379 if (si->cancels) {
1380 if ((num % 10) == 0)
1381 printf("\n ");
1382
1383 printf("%6.1f ", (double)si->cycles_0 / si->cancels);
1384 num++;
1385 }
1386 }
1387
1388 printf("\n\n");
1389
1390 num = 0;
1391 printf("odp_timer_start() cycles per thread:\n");
1392 printf("-------------------------------------------------\n");
1393 printf(" 1 2 3 4 5 6 7 8 9 10");
1394
1395 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1396 const test_stat_t *si = &global->stat[i];
1397
1398 if (si->starts) {
1399 if ((num % 10) == 0)
1400 printf("\n ");
1401
1402 printf("%6.1f ", (double)si->cycles_1 / si->starts);
1403 num++;
1404 }
1405 }
1406
1407 if (sum->num)
1408 start_ave = (double)sum->starts / sum->num;
1409
1410 printf("\n\n");
1411 printf("TOTAL (%i workers)\n", sum->num);
1412 printf(" rounds: %" PRIu64 "\n", sum->rounds);
1413 printf(" timeouts: %" PRIu64 "\n", sum->events);
1414 printf(" timer cancels: %" PRIu64 "\n", sum->cancels);
1415 printf(" cancels failed: %" PRIu64 "\n", sum->cancels - sum->starts);
1416 printf(" timer starts: %" PRIu64 "\n", sum->starts);
1417 printf(" ave time: %.2f sec\n", sum->time_ave);
1418 printf(" cancel+start per cpu: %.2fM per sec\n", (start_ave / sum->time_ave) / 1000000.0);
1419 printf("\n");
1420}
1421
1422static void print_stat_expire_mode(test_global_t *global)
1423{
1424 int i;
1425 test_stat_sum_t *sum = &global->stat_sum;
1426 double round_ave = 0.0;
1427 double before_ave = 0.0;
1428 double after_ave = 0.0;
1429 int num = 0;
1430
1431 printf("\n");
1432 printf("RESULTS\n");
1433 printf("odp_schedule() cycles per thread:\n");
1434 printf("-------------------------------------------------\n");
1435 printf(" 1 2 3 4 5 6 7 8 9 10");
1436
1437 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1438 if (global->stat[i].rounds) {
1439 if ((num % 10) == 0)
1440 printf("\n ");
1441
1442 printf("%6.1f ", (double)global->stat[i].cycles_0 / global->stat[i].rounds);
1443 num++;
1444 }
1445 }
1446
1447 printf("\n\n");
1448
1449 num = 0;
1450 printf("odp_timer_start() cycles per thread:\n");
1451 printf("-------------------------------------------------\n");
1452 printf(" 1 2 3 4 5 6 7 8 9 10");
1453
1454 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1455 if (global->stat[i].events) {
1456 if ((num % 10) == 0)
1457 printf("\n ");
1458
1459 printf("%6.1f ", (double)global->stat[i].cycles_1 / global->stat[i].events);
1460 num++;
1461 }
1462 }
1463
1464 printf("\n\n");
1465
1466 if (sum->num)
1467 round_ave = (double)sum->rounds / sum->num;
1468
1469 if (sum->before.num)
1470 before_ave = (double)sum->before.sum_ns / sum->before.num;
1471
1472 if (sum->after.num)
1473 after_ave = (double)sum->after.sum_ns / sum->after.num;
1474
1475 printf("TOTAL (%i workers)\n", sum->num);
1476 printf(" events: %" PRIu64 "\n", sum->events);
1477 printf(" ave time: %.2f sec\n", sum->time_ave);
1478 printf(" ave rounds per sec: %.2fM\n", (round_ave / sum->time_ave) / 1000000.0);
1479 printf(" num before: %" PRIu64 "\n", sum->before.num);
1480 printf(" ave before: %.1f nsec\n", before_ave);
1481 printf(" max before: %" PRIu64 " nsec\n", sum->before.max_ns);
1482 printf(" num after: %" PRIu64 "\n", sum->after.num);
1483 printf(" ave after: %.1f nsec\n", after_ave);
1484 printf(" max after: %" PRIu64 " nsec\n", sum->after.max_ns);
1485 printf("\n");
1486}
1487
1488static void print_stat_timer_pool_ctrl_mode(test_global_t *global)
1489{
1490 int i;
1491 test_stat_tp_ctrl_t sum;
1492 double create_ave = 0.0, start_ave = 0.0, alloc_ave = 0.0;
1493 double free_ave = 0.0, destroy_ave = 0.0;
1494 double time_ave = 0.0;
1495 uint64_t nsec_sum = 0;
1496 int num = 0;
1497
1498 memset(&sum, 0, sizeof(sum));
1499
1500 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1501 const test_stat_tp_ctrl_t *stat = &global->stat_tp_ctrl[i];
1502
1503 if (stat->rounds == 0)
1504 continue;
1505
1506 num++;
1507 sum.rounds = stat->rounds;
1508 sum.num_tp_create += stat->num_tp_create;
1509 sum.tp_create_cycles += stat->tp_create_cycles;
1510 sum.num_tp_start += stat->num_tp_start;
1511 sum.tp_start_cycles += stat->tp_start_cycles;
1512 sum.num_timer_alloc += stat->num_timer_alloc;
1513 sum.timer_alloc_cycles += stat->timer_alloc_cycles;
1514 sum.num_timer_free += stat->num_timer_free;
1515 sum.timer_free_cycles += stat->timer_free_cycles;
1516 sum.num_tp_destroy += stat->num_tp_destroy;
1517 sum.tp_destroy_cycles += stat->tp_destroy_cycles;
1518 nsec_sum += stat->nsec;
1519 }
1520
1521 if (sum.num_tp_create)
1522 create_ave = (double)sum.tp_create_cycles / sum.num_tp_create;
1523
1524 if (sum.num_tp_start)
1525 start_ave = (double)sum.tp_start_cycles / sum.num_tp_start;
1526
1527 if (sum.num_timer_alloc)
1528 alloc_ave = (double)sum.timer_alloc_cycles / sum.num_timer_alloc;
1529
1530 if (sum.num_timer_free)
1531 free_ave = (double)sum.timer_free_cycles / sum.num_timer_free;
1532
1533 if (sum.num_tp_destroy)
1534 destroy_ave = (double)sum.tp_destroy_cycles / sum.num_tp_destroy;
1535
1536 if (num)
1537 time_ave = ((double)nsec_sum / num) / ODP_TIME_SEC_IN_NS;
1538
1539 printf("\n");
1540 printf("RESULTS\n");
1541 printf("-------------------------------------------------\n");
1542 printf("TOTAL (%i workers)\n", num);
1543 printf(" rounds per worker: %" PRIu64 "\n", sum.rounds);
1544 printf(" ave time: %.2f sec\n", time_ave);
1545 printf("\n");
1546 printf(" odp_timer_pool_create()\n");
1547 printf(" calls: %" PRIu64 "\n", sum.num_tp_create);
1548 printf(" ave cycles per call: %.1f\n", create_ave);
1549 printf(" odp_timer_pool_start_multi()\n");
1550 printf(" calls: %" PRIu64 "\n", sum.num_tp_start);
1551 printf(" ave cycles per call: %.1f\n", start_ave);
1552 printf(" odp_timer_alloc()\n");
1553 printf(" calls: %" PRIu64 "\n", sum.num_timer_alloc);
1554 printf(" ave cycles per call: %.1f\n", alloc_ave);
1555 printf(" odp_timer_free()\n");
1556 printf(" calls: %" PRIu64 "\n", sum.num_timer_free);
1557 printf(" ave cycles per call: %.1f\n", free_ave);
1558 printf(" odp_timer_pool_destroy()\n");
1559 printf(" calls: %" PRIu64 "\n", sum.num_tp_destroy);
1560 printf(" ave cycles per call: %.1f\n", destroy_ave);
1561 printf("\n");
1562}
1563
1564static void sig_handler(int signo)
1565{
1566 (void)signo;
1567
1568 if (test_global == NULL)
1569 return;
1570 odp_atomic_add_u32(&test_global->exit_test, MAX_TIMER_POOLS);
1571}
1572
1573int main(int argc, char **argv)
1574{
1575 odph_helper_options_t helper_options;
1576 odp_instance_t instance;
1577 odp_init_t init;
1578 odp_shm_t shm;
1579 test_global_t *global;
1580 test_options_t *test_options;
1581 int i, use_workers, mode;
1582
1583 signal(SIGINT, sig_handler);
1584
1585 /* Let helper collect its own arguments (e.g. --odph_proc) */
1586 argc = odph_parse_options(argc, argv);
1587 if (odph_options(&helper_options)) {
1588 ODPH_ERR("Reading ODP helper options failed.\n");
1589 exit(EXIT_FAILURE);
1590 }
1591
1592 /* List features not to be used */
1593 odp_init_param_init(&init);
1594 init.not_used.feat.cls = 1;
1595 init.not_used.feat.compress = 1;
1596 init.not_used.feat.crypto = 1;
1597 init.not_used.feat.ipsec = 1;
1598 init.not_used.feat.tm = 1;
1599
1600 init.mem_model = helper_options.mem_model;
1601
1602 /* Init ODP before calling anything else */
1603 if (odp_init_global(&instance, &init, NULL)) {
1604 ODPH_ERR("Global init failed.\n");
1605 return -1;
1606 }
1607
1608 /* Init this thread */
1609 if (odp_init_local(instance, ODP_THREAD_CONTROL)) {
1610 ODPH_ERR("Local init failed.\n");
1611 return -1;
1612 }
1613
1614 shm = odp_shm_reserve("timer_perf_global", sizeof(test_global_t), ODP_CACHE_LINE_SIZE, 0);
1615 if (shm == ODP_SHM_INVALID) {
1616 ODPH_ERR("Shared mem reserve failed.\n");
1617 exit(EXIT_FAILURE);
1618 }
1619
1620 global = odp_shm_addr(shm);
1621 if (global == NULL) {
1622 ODPH_ERR("Shared mem alloc failed\n");
1623 exit(EXIT_FAILURE);
1624 }
1625 test_global = global;
1626
1627 memset(global, 0, sizeof(test_global_t));
1628 odp_atomic_init_u32(&global->exit_test, 0);
1629 odp_atomic_init_u32(&global->timers_started, 0);
1630
1631 for (i = 0; i < ODP_THREAD_COUNT_MAX; i++) {
1632 global->thread_arg[i].global = global;
1633 global->thread_arg[i].worker_idx = i;
1634 }
1635
1636 if (parse_options(argc, argv, &global->test_options))
1637 return -1;
1638
1639 test_options = &global->test_options;
1640 mode = test_options->mode;
1641
1642 use_workers = 1;
1643 if (!test_options->shared && mode != MODE_TP_CTRL)
1644 use_workers = 0;
1645
1647
1648 odp_schedule_config(NULL);
1649
1650 if (set_num_cpu(global, use_workers))
1651 return -1;
1652
1653 init_global_handles(global);
1654
1655 if (prepare_timer_pool_param(global, &global->timer_pool_param))
1656 return -1;
1657
1658 /*
1659 * Timer pool control mode creates and destroys the timer pools and
1660 * timers inside the worker loop. It does not use timeout event pools.
1661 * Other modes create those here up front.
1662 */
1663 if (mode != MODE_TP_CTRL) {
1664 if (create_timer_pools(global, &global->timer_pool_param))
1665 return -1;
1666
1667 if (create_event_pools(global))
1668 return -1;
1669 }
1670
1671 if (create_event_queues(global))
1672 return -1;
1673
1674 if (use_workers) {
1675 /* Start worker threads */
1676 start_workers(global, instance);
1677
1678 /* Wait until workers have started.
1679 * Scheduler calls from workers may be needed to run timer
1680 * pools in a software implementation. Wait 1 msec to ensure
1681 * that timer pools are running before starting timers. */
1682 odp_barrier_wait(&global->barrier);
1684 }
1685
1686 /* Start timers. Force workers to exit on failure. Timer pool control
1687 * mode does not start timers. */
1688 if (mode != MODE_TP_CTRL) {
1689 if (start_timers(global))
1690 odp_atomic_add_u32(&global->exit_test, MAX_TIMER_POOLS);
1691 else
1692 odp_atomic_store_rel_u32(&global->timers_started, 1);
1693 }
1694
1695 if (!use_workers) {
1696 /* Test private pools on the master thread. Timer pool control
1697 * mode supports private and shared pools with workers, as it
1698 * does not share timer pools between threads. */
1699 if (mode == MODE_SCHED_OVERH) {
1700 if (sched_mode_worker(&global->thread_arg[0])) {
1701 ODPH_ERR("Sched_mode_worker failed\n");
1702 return -1;
1703 }
1704 } else if (mode == MODE_START_CANCEL) {
1705 if (start_cancel_mode_worker(&global->thread_arg[0])) {
1706 ODPH_ERR("Start_cancel_mode_worker failed\n");
1707 return -1;
1708 }
1709 } else {
1710 if (start_expire_mode_worker(&global->thread_arg[0])) {
1711 ODPH_ERR("Start_expire_mode_worker failed\n");
1712 return -1;
1713 }
1714 }
1715 } else {
1716 /* Wait workers to exit */
1717 odph_thread_join(global->thread_tbl,
1718 global->test_options.num_cpu);
1719 }
1720
1721 if (mode == MODE_TP_CTRL) {
1722 print_stat_timer_pool_ctrl_mode(global);
1723 } else {
1724 sum_stat(global);
1725
1726 if (mode == MODE_SCHED_OVERH)
1727 print_stat_sched_mode(global);
1728 else if (mode == MODE_START_CANCEL)
1729 print_stat_start_cancel_mode(global);
1730 else
1731 print_stat_expire_mode(global);
1732 }
1733
1734 destroy_timer_pool(global);
1735
1736 if (odp_shm_free(shm)) {
1737 ODPH_ERR("Shared mem free failed.\n");
1738 exit(EXIT_FAILURE);
1739 }
1740
1741 if (odp_term_local()) {
1742 ODPH_ERR("Term local failed.\n");
1743 return -1;
1744 }
1745
1746 if (odp_term_global(instance)) {
1747 ODPH_ERR("Term global failed.\n");
1748 return -1;
1749 }
1750
1751 return 0;
1752}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
void odp_atomic_add_u32(odp_atomic_u32_t *atom, uint32_t val)
Add to atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_inc_u32(odp_atomic_u32_t *atom)
Increment atomic uint32 variable.
uint32_t odp_atomic_load_acq_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable using ACQUIRE memory ordering.
void odp_atomic_store_rel_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable using RELEASE memory ordering.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
#define odp_unlikely(x)
Branch unlikely taken.
Definition spec/hints.h:64
#define odp_likely(x)
Branch likely taken.
Definition spec/hints.h:59
uint64_t odp_cpu_cycles_diff(uint64_t c2, uint64_t c1)
CPU cycle count difference.
uint64_t odp_cpu_cycles(void)
Current CPU cycle count.
uint64_t odp_cpu_cycles_strict(void)
Current CPU cycle count (strict)
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
void odp_event_free(odp_event_t event)
Free event.
#define ODP_EVENT_INVALID
Invalid event.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_TIMEOUT
Timeout pool.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
#define ODP_SCHED_SYNC_ATOMIC
Atomic queue synchronization.
#define ODP_SCHED_NO_WAIT
Do not wait.
int odp_schedule_default_prio(void)
Default scheduling priority level.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
uint64_t odp_schedule_wait_time(uint64_t ns)
Schedule wait time.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
void odp_sys_info_print(void)
Print system info.
#define ODP_THREAD_COUNT_MAX
Maximum number of threads supported in build time.
int odp_thread_id(void)
Get thread identifier.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
uint64_t odp_time_to_ns(odp_time_t time)
Convert time to nanoseconds.
#define ODP_TIME_SEC_IN_NS
A second in nanoseconds.
void odp_time_wait_ns(uint64_t ns)
Wait the specified number of nanoseconds.
odp_time_t odp_time_global(void)
Current global time.
odp_time_t odp_time_local(void)
Current local time.
#define ODP_TIME_NULL
Zero time stamp.
#define ODP_TIME_MSEC_IN_NS
A millisecond in nanoseconds.
uint64_t odp_time_global_ns(void)
Current global time in nanoseconds.
uint64_t odp_time_diff_ns(odp_time_t t2, odp_time_t t1)
Time difference in nanoseconds.
void odp_timeout_free(odp_timeout_t tmo)
Timeout free.
int odp_timer_pool_start_multi(odp_timer_pool_t timer_pool[], int num)
Start timer pools.
void * odp_timeout_user_ptr(odp_timeout_t tmo)
Return user pointer for the timeout.
odp_timeout_t odp_timeout_alloc(odp_pool_t pool)
Timeout alloc.
int odp_timer_free(odp_timer_t timer)
Free a timer.
odp_timeout_t odp_timeout_from_event(odp_event_t ev)
Get timeout handle from an ODP_EVENT_TIMEOUT type event.
#define ODP_TIMER_POOL_INVALID
Invalid timer pool handle.
odp_timer_pool_t odp_timer_pool_create(const char *name, const odp_timer_pool_param_t *params)
Create a timer pool.
int odp_timer_cancel(odp_timer_t timer, odp_event_t *tmo_ev)
Cancel a single shot timer.
uint64_t odp_timer_current_tick(odp_timer_pool_t timer_pool)
Current tick value.
int odp_timer_capability(odp_timer_clk_src_t clk_src, odp_timer_capability_t *capa)
Query timer capabilities per clock source.
uint64_t odp_timer_ns_to_tick(odp_timer_pool_t timer_pool, uint64_t ns)
Convert nanoseconds to timer ticks.
int odp_timer_start(odp_timer_t timer, const odp_timer_start_t *start_param)
Start a single shot timer.
int odp_timer_res_capability(odp_timer_clk_src_t clk_src, odp_timer_res_capability_t *res_capa)
Timer resolution capability.
odp_event_t odp_timeout_to_event(odp_timeout_t tmo)
Convert timeout handle to event handle.
int odp_timer_pool_info(odp_timer_pool_t timer_pool, odp_timer_pool_info_t *info)
Query timer pool configuration and current state.
odp_timer_t odp_timer_alloc(odp_timer_pool_t timer_pool, odp_queue_t queue, const void *user_ptr)
Allocate a single shot timer.
#define ODP_CLOCK_DEFAULT
The default clock source.
#define ODP_TIMER_INVALID
Invalid timer handle.
void odp_timer_pool_param_init(odp_timer_pool_param_t *param)
Initialize timer pool parameters.
void odp_timer_pool_destroy(odp_timer_pool_t timer_pool)
Destroy a timer pool.
@ ODP_TIMER_SUCCESS
Timer operation succeeded.
@ ODP_TIMER_TOO_NEAR
Timer operation failed, too near to the current time.
@ ODP_TIMER_TICK_REL
Relative ticks.
@ ODP_TIMER_TICK_ABS
Absolute ticks.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
odp_feature_t not_used
Unused features.
uint32_t num
Number of buffers in the pool.
odp_pool_type_t type
Pool type.
struct odp_pool_param_t::@140 tmo
Parameters for timeout pools.
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
odp_queue_type_t type
Queue type.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
uint32_t max_timers
Maximum number of single shot timers in a pool.
uint32_t max_pools
Maximum number of timer pools for single shot timers (per clock source)
odp_timer_res_capability_t max_res
Maximum resolution.
ODP timer pool information and configuration.
uint32_t hwm_timers
High watermark of allocated timers.
uint32_t cur_timers
Number of currently allocated timers.
Timer pool parameters.
uint64_t res_ns
Timeout resolution in nanoseconds.
int priv
Thread private timer pool.
uint64_t min_tmo
Minimum relative timeout in nanoseconds.
uint32_t num_timers
Number of timers in the pool.
odp_timer_clk_src_t clk_src
Clock source for timers.
uint64_t max_tmo
Maximum relative timeout in nanoseconds.
Timer resolution capability.
uint64_t max_tmo
Maximum relative timeout in nanoseconds.
uint64_t min_tmo
Minimum relative timeout in nanoseconds.
uint64_t res_ns
Timeout resolution in nanoseconds.
Timer start parameters.
uint64_t tick
Expiration time in ticks.
odp_event_t tmo_ev
Timeout event.
odp_timer_tick_type_t tick_type
Tick type.
uint32_t tm
Traffic Manager APIs, e.g., odp_tm_xxx()
uint32_t crypto
Crypto APIs, e.g., odp_crypto_xxx()
uint32_t ipsec
IPsec APIs, e.g., odp_ipsec_xxx()
uint32_t cls
Classifier APIs, e.g., odp_cls_xxx(), odp_cos_xxx()
struct odp_feature_t::@174 feat
Individual feature bits.
uint32_t compress
Compression APIs, e.g., odp_comp_xxx()