diff options
author | Petri Savolainen <petri.savolainen@linaro.org> | 2018-05-02 11:58:33 +0300 |
---|---|---|
committer | Maxim Uvarov <maxim.uvarov@linaro.org> | 2018-05-07 18:16:22 +0300 |
commit | 79781031e28dee010425f575d1bf5de5b18af0d6 (patch) | |
tree | 1bebb99ade44ce0b5fa2e2e79cd6dee75d175bd9 /example | |
parent | 7d91fbd6c1a40e378b06f9be6dd3ef260c66dba9 (diff) |
example: timer_accuracy: simple timer accuracy measurement app
Added simple application to measure timer accuracy. Uses time
API to measure accuracy of received timeouts from timer.
Signed-off-by: Petri Savolainen <petri.savolainen@linaro.org>
Reviewed-by: Bill Fischofer <bill.fischofer@linaro.org>
Signed-off-by: Maxim Uvarov <maxim.uvarov@linaro.org>
Diffstat (limited to 'example')
-rw-r--r-- | example/timer/.gitignore | 1 | ||||
-rw-r--r-- | example/timer/Makefile.am | 13 | ||||
-rw-r--r-- | example/timer/odp_timer_accuracy.c | 461 |
3 files changed, 471 insertions, 4 deletions
diff --git a/example/timer/.gitignore b/example/timer/.gitignore index eaa9a3619..2b077829d 100644 --- a/example/timer/.gitignore +++ b/example/timer/.gitignore @@ -1,4 +1,5 @@ *.log *.trs +odp_timer_accuracy odp_timer_test odp_timer_simple diff --git a/example/timer/Makefile.am b/example/timer/Makefile.am index 64e722a39..c7d58922d 100644 --- a/example/timer/Makefile.am +++ b/example/timer/Makefile.am @@ -1,11 +1,16 @@ include $(top_srcdir)/example/Makefile.inc -bin_PROGRAMS = odp_timer_test \ - odp_timer_simple -odp_timer_test_SOURCES = odp_timer_test.c +bin_PROGRAMS = odp_timer_accuracy \ + odp_timer_simple \ + odp_timer_test + +odp_timer_accuracy_SOURCES = odp_timer_accuracy.c odp_timer_simple_SOURCES = odp_timer_simple.c +odp_timer_test_SOURCES = odp_timer_test.c + if test_example -TESTS = odp_timer_simple +TESTS = odp_timer_accuracy \ + odp_timer_simple endif diff --git a/example/timer/odp_timer_accuracy.c b/example/timer/odp_timer_accuracy.c new file mode 100644 index 000000000..f1a088cb9 --- /dev/null +++ b/example/timer/odp_timer_accuracy.c @@ -0,0 +1,461 @@ +/* Copyright (c) 2018, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <stdlib.h> + +#include <unistd.h> +#include <getopt.h> + +#include <odp_api.h> + +#define START_OFFSET_NS ((uint64_t)(300 * ODP_TIME_MSEC_IN_NS)) + +typedef struct test_global_t { + struct { + unsigned long long int period_ns; + unsigned long long int res_ns; + int num; + int init; + } opt; + + odp_queue_t queue; + odp_timer_pool_t timer_pool; + odp_pool_t timeout_pool; + odp_timer_t *timer; + uint64_t period_ns; + uint64_t first_ns; + +} test_global_t; + +static void print_usage(void) +{ + printf("\n" + "Timer accuracy test application.\n" + "\n" + "OPTIONS:\n" + " -p, --period <nsec> Timeout period. Default: 200 milliseconds\n" + " -r, --resolution <nsec> Timeout resolution. Default: period / 10\n" + " -n, --num <number> Number of timeouts. Default: 50\n" + " -i, --init Set global init parameters. Default: init params not set.\n" + " -h, --help Display help and exit.\n\n"); +} + +static int parse_options(int argc, char *argv[], test_global_t *test_global) +{ + int opt, long_index; + const struct option longopts[] = { + {"period", required_argument, NULL, 'p'}, + {"resolution", required_argument, NULL, 'r'}, + {"num", required_argument, NULL, 'n'}, + {"init", no_argument, NULL, 'i'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} + }; + const char *shortopts = "+p:r:n:ih"; + int ret = 0; + + test_global->opt.period_ns = 200 * ODP_TIME_MSEC_IN_NS; + test_global->opt.res_ns = 0; + test_global->opt.num = 50; + test_global->opt.init = 0; + + while (1) { + opt = getopt_long(argc, argv, shortopts, longopts, &long_index); + + if (opt == -1) + break; /* No more options */ + + switch (opt) { + case 'p': + test_global->opt.period_ns = strtoull(optarg, NULL, 0); + break; + case 'r': + test_global->opt.res_ns = strtoull(optarg, NULL, 0); + break; + case 'n': + test_global->opt.num = atoi(optarg); + break; + case 'i': + test_global->opt.init = 1; + break; + case 'h': + print_usage(); + ret = -1; + break; + default: + print_usage(); + ret = -1; + break; + } + } + + if (test_global->opt.res_ns == 0) + test_global->opt.res_ns = test_global->opt.period_ns / 10; + + return ret; +} + +static int start_timers(test_global_t *test_global) +{ + odp_pool_t pool; + odp_pool_param_t pool_param; + odp_timer_pool_t timer_pool; + odp_timer_pool_param_t timer_param; + odp_timer_capability_t timer_capa; + odp_timer_t timer; + odp_queue_t queue; + odp_queue_param_t queue_param; + uint64_t tick, first_tick, period_tick, offset_tick; + uint64_t period_ns, res_ns, first_ns, res_capa; + odp_event_t event; + odp_timeout_t timeout; + odp_timer_set_t ret; + odp_time_t time; + int i, num; + + num = test_global->opt.num; + period_ns = test_global->opt.period_ns; + test_global->period_ns = period_ns; + + /* Always init globals for destroy calls */ + test_global->queue = ODP_QUEUE_INVALID; + test_global->timer_pool = ODP_TIMER_POOL_INVALID; + test_global->timeout_pool = ODP_POOL_INVALID; + + for (i = 0; i < num; i++) + test_global->timer[i] = ODP_TIMER_INVALID; + + odp_queue_param_init(&queue_param); + queue_param.type = ODP_QUEUE_TYPE_SCHED; + queue_param.sched.prio = ODP_SCHED_PRIO_DEFAULT; + queue_param.sched.sync = ODP_SCHED_SYNC_ATOMIC; + queue_param.sched.group = ODP_SCHED_GROUP_ALL; + + queue = odp_queue_create("timeout_queue", &queue_param); + if (queue == ODP_QUEUE_INVALID) { + printf("Queue create failed.\n"); + return -1; + } + + test_global->queue = queue; + + odp_pool_param_init(&pool_param); + pool_param.type = ODP_POOL_TIMEOUT; + pool_param.tmo.num = num; + + pool = odp_pool_create("timeout pool", &pool_param); + + if (pool == ODP_POOL_INVALID) { + printf("Timeout pool create failed.\n"); + return -1; + } + + test_global->timeout_pool = pool; + + if (odp_timer_capability(ODP_CLOCK_CPU, &timer_capa)) { + printf("Timer capa failed\n"); + return -1; + } + + res_capa = timer_capa.highest_res_ns; + + res_ns = test_global->opt.res_ns; + + if (res_ns < res_capa) { + printf("Resolution %" PRIu64 " nsec too high. " + "Highest resolution %" PRIu64 " nsec. " + "Default resolution is period / 10.\n\n", + res_ns, res_capa); + return -1; + } + + memset(&timer_param, 0, sizeof(odp_timer_pool_param_t)); + + timer_param.res_ns = res_ns; + timer_param.min_tmo = period_ns; + timer_param.max_tmo = START_OFFSET_NS + (num * period_ns); + timer_param.num_timers = num; + timer_param.clk_src = ODP_CLOCK_CPU; + + printf("\nTest parameters:\n"); + printf(" resolution capa: %" PRIu64 " nsec\n", res_capa); + printf(" start offset: %" PRIu64 " nsec\n", START_OFFSET_NS); + printf(" period: %" PRIu64 " nsec\n", period_ns); + printf(" resolution: %" PRIu64 " nsec\n", timer_param.res_ns); + printf(" min timeout: %" PRIu64 " nsec\n", timer_param.min_tmo); + printf(" max timeout: %" PRIu64 " nsec\n", timer_param.max_tmo); + printf(" num timers: %u\n", timer_param.num_timers); + printf(" test run time: %.1f sec\n\n", + timer_param.max_tmo / 1000000000.0); + + timer_pool = odp_timer_pool_create("timer_accuracy", &timer_param); + + if (timer_pool == ODP_TIMER_POOL_INVALID) { + printf("Timer pool create failed\n"); + return -1; + } + + odp_timer_pool_start(); + + test_global->timer_pool = timer_pool; + first_tick = 0; + offset_tick = odp_timer_ns_to_tick(timer_pool, START_OFFSET_NS); + period_tick = odp_timer_ns_to_tick(timer_pool, period_ns); + + for (i = 0; i < num; i++) { + timer = odp_timer_alloc(timer_pool, queue, NULL); + + if (timer == ODP_TIMER_INVALID) { + printf("Timer alloc failed.\n"); + return -1; + } + + test_global->timer[i] = timer; + } + + for (i = 0; i < num; i++) { + timer = test_global->timer[i]; + + timeout = odp_timeout_alloc(pool); + if (timeout == ODP_TIMEOUT_INVALID) { + printf("Timeout alloc failed\n"); + return -1; + } + + event = odp_timeout_to_event(timeout); + + if (i == 0) { + first_tick = odp_timer_current_tick(timer_pool); + time = odp_time_local(); + first_ns = odp_time_to_ns(time); + test_global->first_ns = first_ns + START_OFFSET_NS; + } + + tick = first_tick + offset_tick + (i * period_tick); + ret = odp_timer_set_abs(timer, tick, &event); + + if (ret != ODP_TIMER_SUCCESS) { + printf("Timer[%i] set failed: ret %i\n", i, ret); + return -1; + } + } + + return 0; +} + +static int destroy_timers(test_global_t *test_global) +{ + int i, num; + odp_timer_t timer; + odp_event_t ev; + int ret = 0; + + num = test_global->opt.num; + + for (i = 0; i < num; i++) { + timer = test_global->timer[i]; + + if (timer == ODP_TIMER_INVALID) + break; + + ev = odp_timer_free(timer); + + if (ev != ODP_EVENT_INVALID) + odp_event_free(ev); + } + + if (test_global->timer_pool != ODP_TIMER_POOL_INVALID) + odp_timer_pool_destroy(test_global->timer_pool); + + if (test_global->timeout_pool != ODP_POOL_INVALID) { + if (odp_pool_destroy(test_global->timeout_pool)) { + printf("Pool destroy failed.\n"); + ret = -1; + } + } + + if (test_global->queue != ODP_QUEUE_INVALID) { + if (odp_queue_destroy(test_global->queue)) { + printf("Queue destroy failed.\n"); + ret = -1; + } + } + + return ret; +} + +static void run_test(test_global_t *test_global) +{ + int num, num_left; + odp_event_t ev; + odp_time_t time; + uint64_t time_ns, diff_ns, next_tmo, res_ns; + uint64_t after = 0; + uint64_t min_after = UINT64_MAX; + uint64_t max_after = 0; + uint64_t before = 0; + uint64_t min_before = UINT64_MAX; + uint64_t max_before = 0; + double ave_after = 0.0; + double ave_before = 0.0; + int num_after = 0; + int num_exact = 0; + int num_before = 0; + + res_ns = test_global->opt.res_ns; + num = test_global->opt.num; + num_left = num; + next_tmo = test_global->first_ns; + + while (num_left) { + ev = odp_schedule(NULL, ODP_SCHED_WAIT); + + time = odp_time_local(); + time_ns = odp_time_to_ns(time); + + if (time_ns > next_tmo) { + diff_ns = time_ns - next_tmo; + num_after++; + after += diff_ns; + if (diff_ns < min_after) + min_after = diff_ns; + if (diff_ns > max_after) + max_after = diff_ns; + + } else if (time_ns < next_tmo) { + diff_ns = next_tmo - time_ns; + num_before++; + before += diff_ns; + if (diff_ns < min_before) + min_before = diff_ns; + if (diff_ns > max_before) + max_before = diff_ns; + + } else { + num_exact++; + } + + odp_event_free(ev); + + next_tmo += test_global->period_ns; + num_left--; + } + + /* Free current scheduler context. There should be no more events. */ + while ((ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT)) + != ODP_EVENT_INVALID) { + printf("Dropping extra event\n"); + odp_event_free(ev); + } + + if (num_after) + ave_after = (double)after / num_after; + else + min_after = 0; + + if (num_before) + ave_before = (double)before / num_before; + else + min_before = 0; + + printf("\n Test results:\n"); + printf(" num after: %12i / %.2f%%\n", + num_after, 100.0 * num_after / num); + printf(" num before: %12i / %.2f%%\n", + num_before, 100.0 * num_before / num); + printf(" num exact: %12i / %.2f%%\n", + num_exact, 100.0 * num_exact / num); + printf(" error after (nsec):\n"); + printf(" min: %12" PRIu64 " / %.3fx resolution\n", + min_after, (double)min_after / res_ns); + printf(" max: %12" PRIu64 " / %.3fx resolution\n", + max_after, (double)max_after / res_ns); + printf(" ave: %12.0f / %.3fx resolution\n", + ave_after, ave_after / res_ns); + printf(" error before (nsec):\n"); + printf(" min: %12" PRIu64 " / %.3fx resolution\n", + min_before, (double)min_before / res_ns); + printf(" max: %12" PRIu64 " / %.3fx resolution\n", + max_before, (double)max_before / res_ns); + printf(" ave: %12.0f / %.3fx resolution\n", + ave_before, ave_before / res_ns); + printf("\n"); +} + +int main(int argc, char *argv[]) +{ + odp_instance_t instance; + odp_init_t init; + int num; + test_global_t test_global; + odp_init_t *init_ptr = NULL; + int ret = 0; + + memset(&test_global, 0, sizeof(test_global_t)); + + if (parse_options(argc, argv, &test_global)) + return -1; + + /* List features not to be used (may optimize performance) */ + odp_init_param_init(&init); + init.not_used.feat.cls = 1; + init.not_used.feat.crypto = 1; + init.not_used.feat.ipsec = 1; + init.not_used.feat.tm = 1; + + if (test_global.opt.init) + init_ptr = &init; + + /* Init ODP before calling anything else */ + if (odp_init_global(&instance, init_ptr, NULL)) { + printf("Global init failed.\n"); + return -1; + } + + /* Init this thread */ + if (odp_init_local(instance, ODP_THREAD_WORKER)) { + printf("Local init failed.\n"); + return -1; + } + + odp_sys_info_print(); + + num = test_global.opt.num; + + test_global.timer = calloc(num, sizeof(odp_timer_t)); + + if (test_global.timer == NULL) { + printf("Malloc failed.\n"); + goto quit; + } + + if (start_timers(&test_global)) + goto quit; + + run_test(&test_global); + +quit: + if (destroy_timers(&test_global)) + ret = -1; + + if (test_global.timer) + free(test_global.timer); + + if (odp_term_local()) { + printf("Term local failed.\n"); + ret = -1; + } + + if (odp_term_global(instance)) { + printf("Term global failed.\n"); + ret = -1; + } + + return ret; +} |