diff options
Diffstat (limited to 'example/timer/odp_timer_accuracy.c')
-rw-r--r-- | example/timer/odp_timer_accuracy.c | 1435 |
1 files changed, 0 insertions, 1435 deletions
diff --git a/example/timer/odp_timer_accuracy.c b/example/timer/odp_timer_accuracy.c deleted file mode 100644 index e5df1c24e..000000000 --- a/example/timer/odp_timer_accuracy.c +++ /dev/null @@ -1,1435 +0,0 @@ -/* SPDX-License-Identifier: BSD-3-Clause - * Copyright (c) 2018 Linaro Limited - * Copyright (c) 2019-2023 Nokia - */ - -/** - * @example odp_timer_accuracy.c - * - * ODP timer accuracy test application - * - * @cond _ODP_HIDE_FROM_DOXYGEN_ - */ - -#include <errno.h> -#include <stdio.h> -#include <string.h> -#include <inttypes.h> -#include <stdlib.h> - -#include <unistd.h> -#include <getopt.h> - -#include <odp_api.h> -#include <odp/helper/odph_api.h> - -#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1) -#define MAX_QUEUES 1024 -#define MAX_FILENAME 128 - -enum mode_e { - MODE_ONESHOT = 0, - MODE_RESTART_ABS, - MODE_RESTART_REL, - MODE_PERIODIC, -}; - -typedef struct test_opt_t { - int cpu_count; - unsigned long long period_ns; - long long res_ns; - unsigned long long res_hz; - unsigned long long offset_ns; - unsigned long long max_tmo_ns; - unsigned long long num; - unsigned long long num_warmup; - unsigned long long burst; - unsigned long long burst_gap; - odp_fract_u64_t freq; - unsigned long long max_multiplier; - unsigned long long multiplier; - enum mode_e mode; - int clk_src; - odp_queue_type_t queue_type; - int num_queue; - int groups; - int init; - int output; - int early_retry; - uint64_t warmup_timers; - uint64_t tot_timers; - uint64_t alloc_timers; - char filename[MAX_FILENAME + 1]; -} test_opt_t; - -typedef struct timer_ctx_t { - odp_timer_t timer; - odp_event_t event; - uint64_t nsec; - uint64_t count; - uint64_t first_period; - int tmo_tick; - int64_t first_tmo_diff; - int64_t nsec_final; - -} timer_ctx_t; - -typedef struct { - uint64_t nsec_before_sum; - uint64_t nsec_before_min; - uint64_t nsec_before_min_idx; - uint64_t nsec_before_max; - uint64_t nsec_before_max_idx; - - uint64_t nsec_after_sum; - uint64_t nsec_after_min; - uint64_t nsec_after_min_idx; - uint64_t nsec_after_max; - uint64_t nsec_after_max_idx; - - uint64_t num_before; - uint64_t num_exact; - uint64_t num_after; - - uint64_t num_too_near; - -} test_stat_t; - -typedef struct test_log_t { - uint64_t tmo_ns; - int64_t diff_ns; - int tid; - -} test_log_t; - -typedef struct test_global_t { - test_opt_t opt; - - test_stat_t stat[MAX_WORKERS]; - - odp_queue_t queue[MAX_QUEUES]; - odp_schedule_group_t group[MAX_WORKERS]; - odp_timer_pool_t timer_pool; - odp_pool_t timeout_pool; - timer_ctx_t *timer_ctx; - double res_ns; - uint64_t start_tick; - uint64_t start_ns; - uint64_t period_tick; - double period_dbl; - odp_fract_u64_t base_freq; - test_log_t *log; - FILE *file; - odp_barrier_t barrier; - odp_atomic_u64_t events; - odp_atomic_u64_t last_events; - -} test_global_t; - -static void print_usage(void) -{ - printf("\n" - "Timer accuracy test application.\n" - "\n" - "OPTIONS:\n" - " -c, --count <num> CPU count, 0=all available, default=1\n" - " -p, --period <nsec> Timeout period in nsec. Not used in periodic mode. Default: 200 msec\n" - " -r, --res_ns <nsec> Timeout resolution in nsec. Default value is 0. Special values:\n" - " 0: Use period / 10 as the resolution\n" - " -1: In periodic mode, use resolution from capabilities\n" - " -R, --res_hz <hertz> Timeout resolution in hertz. Set resolution either with -r (nsec) or -R (hertz),\n" - " and leave other to 0. Default: 0 (not used)\n" - " -f, --first <nsec> First timer offset in nsec. Default: 0 for periodic mode, otherwise 300 msec\n" - " -x, --max_tmo <nsec> Maximum timeout in nsec. Not used in periodic mode.\n" - " When 0, max tmo is calculated from other options. Default: 0\n" - " -n, --num <number> Number of timeout periods. Default: 50\n" - " -w, --warmup <number> Number of warmup periods. Default: 0\n" - " -b, --burst <number> Number of timers per a timeout period. Default: 1\n" - " -g, --burst_gap <nsec> Gap (in nsec) between timers within a burst. Default: 0\n" - " In periodic mode, first + burst * burst_gap must be less than period length.\n" - " -m, --mode <number> Test mode select (default: 0):\n" - " 0: One-shot. Start all timers at init phase.\n" - " 1: One-shot. Each period, restart timers with absolute time.\n" - " 2: One-shot. Each period, restart timers with relative time.\n" - " 3: Periodic.\n" - " -P, --periodic <freq_integer:freq_numer:freq_denom:max_multiplier>\n" - " Periodic timer pool parameters. Default: 5:0:0:1 (5 Hz)\n" - " -M, --multiplier Periodic timer multiplier. Default: 1\n" - " -o, --output <file> Output file for measurement logs\n" - " -e, --early_retry <num> When timer restart fails due to ODP_TIMER_TOO_NEAR, retry this many times\n" - " with expiration time incremented by the period. Default: 0\n" - " -s, --clk_src Clock source select (default 0):\n" - " 0: ODP_CLOCK_DEFAULT\n" - " 1: ODP_CLOCK_SRC_1, ...\n" - " -t, --queue_type Queue sync type. Default is 0 (PARALLEL).\n" - " 0: PARALLEL\n" - " 1: ATOMIC\n" - " 2: ORDERED\n" - " -q, --num_queue Number of queues. Default is 1.\n" - " -G, --sched_groups Use dedicated schedule group for each worker.\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_opt_t *test_opt) -{ - int opt, long_index; - const struct option longopts[] = { - {"count", required_argument, NULL, 'c'}, - {"period", required_argument, NULL, 'p'}, - {"res_ns", required_argument, NULL, 'r'}, - {"res_hz", required_argument, NULL, 'R'}, - {"first", required_argument, NULL, 'f'}, - {"max_tmo", required_argument, NULL, 'x'}, - {"num", required_argument, NULL, 'n'}, - {"warmup", required_argument, NULL, 'w'}, - {"burst", required_argument, NULL, 'b'}, - {"burst_gap", required_argument, NULL, 'g'}, - {"mode", required_argument, NULL, 'm'}, - {"periodic", required_argument, NULL, 'P'}, - {"multiplier", required_argument, NULL, 'M'}, - {"output", required_argument, NULL, 'o'}, - {"early_retry", required_argument, NULL, 'e'}, - {"clk_src", required_argument, NULL, 's'}, - {"queue_type", required_argument, NULL, 't'}, - {"num_queue", required_argument, NULL, 'q'}, - {"sched_groups", no_argument, NULL, 'G'}, - {"init", no_argument, NULL, 'i'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0} - }; - const char *shortopts = "+c:p:r:R:f:x:n:w:b:g:m:P:M:o:e:s:t:q:Gih"; - int ret = 0; - - memset(test_opt, 0, sizeof(*test_opt)); - - test_opt->cpu_count = 1; - test_opt->period_ns = 200 * ODP_TIME_MSEC_IN_NS; - test_opt->res_ns = 0; - test_opt->res_hz = 0; - test_opt->offset_ns = UINT64_MAX; - test_opt->max_tmo_ns = 0; - test_opt->num = 50; - test_opt->num_warmup = 0; - test_opt->burst = 1; - test_opt->burst_gap = 0; - test_opt->mode = MODE_ONESHOT; - test_opt->freq.integer = ODP_TIME_SEC_IN_NS / test_opt->period_ns; - test_opt->freq.numer = 0; - test_opt->freq.denom = 0; - test_opt->max_multiplier = 1; - test_opt->multiplier = 1; - test_opt->clk_src = ODP_CLOCK_DEFAULT; - test_opt->queue_type = ODP_SCHED_SYNC_PARALLEL; - test_opt->groups = 0; - test_opt->num_queue = 1; - test_opt->init = 0; - test_opt->output = 0; - test_opt->early_retry = 0; - - while (1) { - opt = getopt_long(argc, argv, shortopts, longopts, &long_index); - - if (opt == -1) - break; /* No more options */ - - switch (opt) { - case 'c': - test_opt->cpu_count = atoi(optarg); - break; - case 'p': - test_opt->period_ns = strtoull(optarg, NULL, 0); - break; - case 'r': - test_opt->res_ns = strtoll(optarg, NULL, 0); - break; - case 'R': - test_opt->res_hz = strtoull(optarg, NULL, 0); - break; - case 'f': - test_opt->offset_ns = strtoull(optarg, NULL, 0); - break; - case 'x': - test_opt->max_tmo_ns = strtoull(optarg, NULL, 0); - break; - case 'n': - test_opt->num = strtoull(optarg, NULL, 0); - break; - case 'w': - test_opt->num_warmup = strtoull(optarg, NULL, 0); - break; - case 'b': - test_opt->burst = strtoull(optarg, NULL, 0); - break; - case 'g': - test_opt->burst_gap = strtoull(optarg, NULL, 0); - break; - case 'm': - test_opt->mode = atoi(optarg); - break; - case 'P': - sscanf(optarg, "%" SCNu64 ":%" SCNu64 ":%" SCNu64 ":%llu", - &test_opt->freq.integer, &test_opt->freq.numer, - &test_opt->freq.denom, &test_opt->max_multiplier); - break; - case 'M': - test_opt->multiplier = strtoull(optarg, NULL, 0); - break; - case 'o': - test_opt->output = 1; - /* filename is NULL terminated in anycase */ - strncpy(test_opt->filename, optarg, MAX_FILENAME); - break; - case 'e': - test_opt->early_retry = atoi(optarg); - break; - case 's': - test_opt->clk_src = atoi(optarg); - break; - case 't': - switch (atoi(optarg)) { - case 1: - test_opt->queue_type = ODP_SCHED_SYNC_ATOMIC; - break; - case 2: - test_opt->queue_type = ODP_SCHED_SYNC_ORDERED; - break; - default: - test_opt->queue_type = ODP_SCHED_SYNC_PARALLEL; - break; - } - break; - case 'q': - test_opt->num_queue = atoi(optarg); - break; - case 'G': - test_opt->groups = 1; - break; - case 'i': - test_opt->init = 1; - break; - case 'h': - print_usage(); - ret = -1; - break; - default: - print_usage(); - ret = -1; - break; - } - } - - if (test_opt->mode == MODE_PERIODIC) { - if ((test_opt->freq.integer == 0 && test_opt->freq.numer == 0) || - (test_opt->freq.numer != 0 && test_opt->freq.denom == 0)) { - printf("Bad frequency\n"); - return -1; - } - - test_opt->period_ns = - ODP_TIME_SEC_IN_NS / odp_fract_u64_to_dbl(&test_opt->freq); - - if (test_opt->offset_ns == UINT64_MAX) - test_opt->offset_ns = 0; - } else { - if (test_opt->res_ns < 0) { - printf("Resolution (res_ns) must be >= 0 with single shot timer\n"); - return -1; - } - - if (test_opt->offset_ns == UINT64_MAX) - test_opt->offset_ns = 300 * ODP_TIME_MSEC_IN_NS; - } - - test_opt->warmup_timers = test_opt->num_warmup * test_opt->burst; - test_opt->tot_timers = - test_opt->warmup_timers + test_opt->num * test_opt->burst; - - if (test_opt->mode == MODE_ONESHOT) - test_opt->alloc_timers = test_opt->tot_timers; - else - test_opt->alloc_timers = test_opt->burst; - - return ret; -} - -static int single_shot_params(test_global_t *test_global, odp_timer_pool_param_t *timer_param, - odp_timer_capability_t *timer_capa) -{ - uint64_t res_ns, res_hz; - uint64_t max_res_ns, max_res_hz; - uint64_t period_ns = test_global->opt.period_ns; - uint64_t num_tmo = test_global->opt.num + test_global->opt.num_warmup; - uint64_t offset_ns = test_global->opt.offset_ns; - enum mode_e mode = test_global->opt.mode; - - max_res_ns = timer_capa->max_res.res_ns; - max_res_hz = timer_capa->max_res.res_hz; - - /* Default resolution */ - if (test_global->opt.res_ns == 0 && test_global->opt.res_hz == 0) { - res_ns = test_global->opt.period_ns / 10; - res_hz = 0; - } else if (test_global->opt.res_ns) { - res_ns = test_global->opt.res_ns; - res_hz = 0; - } else { - res_ns = 0; - res_hz = test_global->opt.res_hz; - } - - if (res_ns && res_ns < max_res_ns) { - printf("Resolution %" PRIu64 " nsec too high. Highest resolution %" PRIu64 " nsec. " - "Default resolution is period / 10.\n\n", - res_ns, max_res_ns); - return -1; - } - - if (res_hz && res_hz > max_res_hz) { - printf("Resolution %" PRIu64 " hz too high. Highest resolution %" PRIu64 " hz. " - "Default resolution is period / 10.\n\n", - res_hz, max_res_hz); - return -1; - } - - if (res_ns) - timer_param->res_ns = res_ns; - else - timer_param->res_hz = res_hz; - - if (mode == MODE_ONESHOT) { - timer_param->min_tmo = offset_ns / 2; - timer_param->max_tmo = offset_ns + ((num_tmo + 1) * period_ns); - } else { - timer_param->min_tmo = period_ns / 10; - timer_param->max_tmo = offset_ns + (2 * period_ns); - } - - if (test_global->opt.max_tmo_ns) { - if (test_global->opt.max_tmo_ns < timer_param->max_tmo) { - printf("Max tmo is too small. Must be at least %" PRIu64 " nsec.\n", - timer_param->max_tmo); - return -1; - } - - timer_param->max_tmo = test_global->opt.max_tmo_ns; - } - - printf(" period: %" PRIu64 " nsec\n", period_ns); - printf(" max res nsec: %" PRIu64 "\n", max_res_ns); - printf(" max res hertz: %" PRIu64 "\n", max_res_hz); - - test_global->period_dbl = period_ns; - - return 0; -} - -static int periodic_params(test_global_t *test_global, odp_timer_pool_param_t *timer_param, - odp_timer_capability_t *timer_capa) -{ - int ret; - uint64_t res_ns; - odp_timer_periodic_capability_t capa; - double freq_dbl, min_freq, max_freq; - double opt_freq = odp_fract_u64_to_dbl(&test_global->opt.freq); - odp_fract_u64_t freq = test_global->opt.freq; - uint64_t res_hz = test_global->opt.res_hz; - uint64_t max_multiplier = test_global->opt.max_multiplier; - uint64_t multiplier = test_global->opt.multiplier; - - if (res_hz) { - res_ns = ODP_TIME_SEC_IN_NS / res_hz; - } else { - res_ns = test_global->opt.res_ns; - - /* Default resolution */ - if (res_ns == 0) - res_ns = ODP_TIME_SEC_IN_NS / (10 * multiplier * opt_freq); - } - - if (res_ns == 0) { - printf("Too high resolution\n"); - return -1; - } - - /* Resolution from capa */ - if (test_global->opt.res_ns < 0) - res_ns = 0; - - min_freq = odp_fract_u64_to_dbl(&timer_capa->periodic.min_base_freq_hz); - max_freq = odp_fract_u64_to_dbl(&timer_capa->periodic.max_base_freq_hz); - - capa.base_freq_hz = freq; - capa.max_multiplier = max_multiplier; - capa.res_ns = res_ns; - - ret = odp_timer_periodic_capability(test_global->opt.clk_src, &capa); - - if (ret < 0) { - printf("Requested periodic timer capabilities are not supported.\n" - "Capabilities: min base freq %g Hz, max base freq %g Hz, " - "max res %" PRIu64 " Hz\n", min_freq, max_freq, timer_capa->max_res.res_hz); - return -1; - } - - if (ret == 0) { - printf("Requested base frequency is not met. Using %.2f Hz instead of %.2f Hz.\n", - odp_fract_u64_to_dbl(&capa.base_freq_hz), opt_freq); - - freq = capa.base_freq_hz; - } - - if (res_ns == 0) - res_ns = capa.res_ns; - - freq_dbl = odp_fract_u64_to_dbl(&freq); - test_global->base_freq = freq; - test_global->period_dbl = ODP_TIME_SEC_IN_NS / (multiplier * freq_dbl); - - /* Min/max tmo are ignored, leave those to default values */ - timer_param->timer_type = ODP_TIMER_TYPE_PERIODIC; - timer_param->periodic.base_freq_hz = freq; - timer_param->periodic.max_multiplier = max_multiplier; - - if (res_hz) - timer_param->res_hz = res_hz; - else - timer_param->res_ns = res_ns; - - printf(" min freq capa: %.2f hz\n", min_freq); - printf(" max freq capa: %.2f hz\n", max_freq); - printf(" freq option: %.2f hz\n", opt_freq); - printf(" freq: %.2f hz\n", freq_dbl); - printf(" freq integer: %" PRIu64 "\n", freq.integer); - printf(" freq numer: %" PRIu64 "\n", freq.numer); - printf(" freq denom: %" PRIu64 "\n", freq.denom); - printf(" max_multiplier: %" PRIu64 "\n", max_multiplier); - printf(" multiplier: %" PRIu64 "\n", multiplier); - printf(" timer freq: %.2f hz\n", multiplier * freq_dbl); - printf(" timer period: %.2f nsec\n", test_global->period_dbl); - printf(" resolution capa: %" PRIu64 " nsec\n", capa.res_ns); - - return 0; -} - -static int create_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_schedule_group_t *group; - odp_queue_param_t queue_param; - uint64_t offset_ns; - uint32_t max_timers; - odp_event_t event; - odp_timeout_t timeout; - uint64_t i, num_tmo, num_warmup, burst, burst_gap; - uint64_t tot_timers, alloc_timers; - enum mode_e mode; - odp_timer_clk_src_t clk_src; - int ret; - - mode = test_global->opt.mode; - alloc_timers = test_global->opt.alloc_timers; - tot_timers = test_global->opt.tot_timers; - num_warmup = test_global->opt.num_warmup; - num_tmo = num_warmup + test_global->opt.num; - burst = test_global->opt.burst; - burst_gap = test_global->opt.burst_gap; - offset_ns = test_global->opt.offset_ns; - queue = test_global->queue; - group = test_global->group; - - /* Always init globals for destroy calls */ - test_global->timer_pool = ODP_TIMER_POOL_INVALID; - test_global->timeout_pool = ODP_POOL_INVALID; - - for (i = 0; i < alloc_timers; i++) { - test_global->timer_ctx[i].timer = ODP_TIMER_INVALID; - test_global->timer_ctx[i].event = ODP_EVENT_INVALID; - } - - if (test_global->opt.groups) { - /* Create groups */ - - odp_thrmask_t zero; - - odp_thrmask_zero(&zero); - - for (i = 0; i < (uint64_t)test_global->opt.cpu_count; i++) { - group[i] = odp_schedule_group_create(NULL, &zero); - - if (group[i] == ODP_SCHED_GROUP_INVALID) { - printf("Group create failed.\n"); - return -1; - } - } - } - - odp_queue_param_init(&queue_param); - queue_param.type = ODP_QUEUE_TYPE_SCHED; - queue_param.sched.prio = odp_schedule_default_prio(); - queue_param.sched.sync = test_global->opt.queue_type; - queue_param.sched.group = ODP_SCHED_GROUP_ALL; - - for (i = 0; i < (uint64_t)test_global->opt.num_queue; i++) { - if (test_global->opt.groups) - queue_param.sched.group = group[i % test_global->opt.cpu_count]; - - queue[i] = odp_queue_create(NULL, &queue_param); - if (queue[i] == ODP_QUEUE_INVALID) { - printf("Queue create failed.\n"); - return -1; - } - } - - odp_pool_param_init(&pool_param); - pool_param.type = ODP_POOL_TIMEOUT; - pool_param.tmo.num = alloc_timers; - - 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; - clk_src = test_global->opt.clk_src; - - if (odp_timer_capability(clk_src, &timer_capa)) { - printf("Timer capa failed\n"); - return -1; - } - - max_timers = timer_capa.max_timers; - - if (mode == MODE_PERIODIC) { - if (timer_capa.periodic.max_pools < 1) { - printf("Error: Periodic timers not supported.\n"); - return -1; - } - max_timers = timer_capa.periodic.max_timers; - } - - if (max_timers && test_global->opt.alloc_timers > max_timers) { - printf("Error: Too many timers: %" PRIu64 ".\n" - " Max timers: %u\n", - test_global->opt.alloc_timers, max_timers); - return -1; - } - - printf("\nTest parameters:\n"); - printf(" clock source: %i\n", clk_src); - printf(" max timers capa: %" PRIu32 "\n", max_timers); - printf(" mode: %i\n", mode); - printf(" queue type: %i\n", test_global->opt.queue_type); - printf(" num queue: %i\n", test_global->opt.num_queue); - printf(" sched groups: %s\n", test_global->opt.groups ? "yes" : "no"); - - odp_timer_pool_param_init(&timer_param); - - if (mode == MODE_PERIODIC) - ret = periodic_params(test_global, &timer_param, &timer_capa); - else - ret = single_shot_params(test_global, &timer_param, &timer_capa); - - if (ret) - return ret; - - if (timer_param.res_hz) { - test_global->res_ns = 1000000000.0 / timer_param.res_hz; - printf(" resolution: %" PRIu64 " Hz\n", timer_param.res_hz); - } else { - test_global->res_ns = timer_param.res_ns; - printf(" resolution: %" PRIu64 " nsec\n", timer_param.res_ns); - } - - timer_param.num_timers = alloc_timers; - timer_param.clk_src = clk_src; - - printf(" restart retries: %i\n", test_global->opt.early_retry); - if (test_global->opt.output) - printf(" log file: %s\n", test_global->opt.filename); - printf(" start offset: %" PRIu64 " nsec\n", offset_ns); - printf(" min timeout: %" PRIu64 " nsec\n", timer_param.min_tmo); - printf(" max timeout: %" PRIu64 " nsec\n", timer_param.max_tmo); - printf(" num timeout: %" PRIu64 "\n", num_tmo); - printf(" num warmup: %" PRIu64 "\n", num_warmup); - printf(" burst size: %" PRIu64 "\n", burst); - printf(" burst gap: %" PRIu64 "\n", burst_gap); - printf(" total timers: %" PRIu64 "\n", tot_timers); - printf(" warmup timers: %" PRIu64 "\n", test_global->opt.warmup_timers); - printf(" alloc timers: %" PRIu64 "\n", alloc_timers); - printf(" warmup time: %.2f sec\n", - (offset_ns + (num_warmup * test_global->period_dbl)) / 1000000000.0); - printf(" test run time: %.2f sec\n\n", - (offset_ns + (num_tmo * test_global->period_dbl)) / 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; - } - - if (odp_timer_pool_start_multi(&timer_pool, 1) != 1) { - ODPH_ERR("Timer pool start failed\n"); - return -1; - } - - odp_timer_pool_print(timer_pool); - - /* Spend some time so that current tick would not be zero */ - odp_time_wait_ns(100 * ODP_TIME_MSEC_IN_NS); - - test_global->timer_pool = timer_pool; - - for (i = 0; i < alloc_timers; i++) { - timer_ctx_t *ctx = &test_global->timer_ctx[i]; - - timer = odp_timer_alloc(timer_pool, queue[i % test_global->opt.num_queue], ctx); - - if (timer == ODP_TIMER_INVALID) { - printf("Timer alloc failed.\n"); - return -1; - } - - ctx->timer = timer; - - timeout = odp_timeout_alloc(pool); - if (timeout == ODP_TIMEOUT_INVALID) { - printf("Timeout alloc failed\n"); - return -1; - } - - ctx->event = odp_timeout_to_event(timeout); - } - - /* Run scheduler few times to ensure that (software) timer is active */ - for (i = 0; i < 1000; i++) { - event = odp_schedule(NULL, ODP_SCHED_NO_WAIT); - - if (event != ODP_EVENT_INVALID) { - printf("Spurious event received\n"); - odp_event_free(event); - return -1; - } - } - - return 0; -} - -static int start_timers(test_global_t *test_global) -{ - odp_timer_pool_t timer_pool; - uint64_t start_tick; - uint64_t period_ns, start_ns, nsec, offset_ns; - odp_time_t time; - uint64_t i, j, idx, num_tmo, num_warmup, burst, burst_gap; - enum mode_e mode; - - mode = test_global->opt.mode; - num_warmup = test_global->opt.num_warmup; - num_tmo = num_warmup + test_global->opt.num; - burst = test_global->opt.burst; - burst_gap = test_global->opt.burst_gap; - period_ns = test_global->opt.period_ns; - offset_ns = test_global->opt.offset_ns; - timer_pool = test_global->timer_pool; - idx = 0; - - /* Record test start time and tick. Memory barriers forbid compiler and out-of-order - * CPU to move samples apart. */ - odp_mb_full(); - start_tick = odp_timer_current_tick(timer_pool); - time = odp_time_global(); - odp_mb_full(); - - start_ns = odp_time_to_ns(time); - test_global->start_tick = start_tick; - test_global->start_ns = start_ns; - test_global->period_tick = odp_timer_ns_to_tick(timer_pool, period_ns); - - /* When mode is not one-shot, set only one burst of timers initially */ - if (mode != MODE_ONESHOT) - num_tmo = 1; - - for (i = 0; i < num_tmo; i++) { - odp_timer_retval_t retval; - - for (j = 0; j < burst; j++) { - timer_ctx_t *ctx = &test_global->timer_ctx[idx]; - odp_timer_start_t start_param; - - if (mode == MODE_PERIODIC) { - odp_timer_periodic_start_t periodic_start; - - nsec = offset_ns + (j * burst_gap); - - /* By default, timer starts one period after current time. Round - * floating point to closest integer number. */ - ctx->nsec = start_ns + test_global->period_dbl + 0.5; - if (nsec) - ctx->nsec = start_ns + nsec; - - ctx->count = 0; - ctx->first_period = start_tick + - odp_timer_ns_to_tick(timer_pool, - test_global->period_dbl + 0.5); - periodic_start.freq_multiplier = test_global->opt.multiplier; - periodic_start.first_tick = 0; - if (nsec) - periodic_start.first_tick = - start_tick + odp_timer_ns_to_tick(timer_pool, nsec); - periodic_start.tmo_ev = ctx->event; - retval = odp_timer_periodic_start(ctx->timer, &periodic_start); - } else { - nsec = offset_ns + (i * period_ns) + (j * burst_gap); - ctx->nsec = start_ns + nsec; - start_param.tick_type = ODP_TIMER_TICK_ABS; - start_param.tick = - start_tick + odp_timer_ns_to_tick(timer_pool, nsec); - start_param.tmo_ev = ctx->event; - retval = odp_timer_start(ctx->timer, &start_param); - } - - if (retval != ODP_TIMER_SUCCESS) { - printf("Timer[%" PRIu64 "] set failed: %i\n", idx, retval); - return -1; - } - - idx++; - } - } - - printf("\nStarting timers took %" PRIu64 " nsec\n", odp_time_global_ns() - start_ns); - - return 0; -} - -static int destroy_timers(test_global_t *test_global) -{ - uint64_t i, alloc_timers; - odp_timer_t timer; - int ret = 0; - - alloc_timers = test_global->opt.alloc_timers; - - for (i = 0; i < alloc_timers; i++) { - timer = test_global->timer_ctx[i].timer; - - if (timer == ODP_TIMER_INVALID) - break; - - if (odp_timer_free(timer)) { - printf("Timer free failed: %" PRIu64 "\n", i); - ret = -1; - } - } - - 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; - } - } - - for (i = 0; i < (uint64_t)test_global->opt.num_queue; i++) { - if (odp_queue_destroy(test_global->queue[i])) { - printf("Queue destroy failed.\n"); - ret = -1; - } - } - - if (test_global->opt.groups) { - for (i = 0; i < (uint64_t)test_global->opt.cpu_count; i++) { - if (odp_schedule_group_destroy(test_global->group[i])) { - printf("Group destroy failed.\n"); - ret = -1; - } - } - } - - return ret; -} - -static void print_nsec_error(const char *str, int64_t nsec, double res_ns, - int tid, int idx) -{ - printf(" %s: %12" PRIi64 " / %.3fx resolution", - str, nsec, (double)nsec / res_ns); - if (tid >= 0) - printf(", thread %d", tid); - if (idx >= 0) - printf(", event %d", idx); - printf("\n"); -} - -static void print_stat(test_global_t *test_global) -{ - test_stat_t test_stat; - test_stat_t *stat = &test_stat; - uint64_t tot_timers; - test_stat_t *s = test_global->stat; - test_log_t *log = test_global->log; - double res_ns = test_global->res_ns; - uint64_t ave_after = 0; - uint64_t ave_before = 0; - uint64_t nsec_before_min_tid = 0; - uint64_t nsec_before_max_tid = 0; - uint64_t nsec_after_min_tid = 0; - uint64_t nsec_after_max_tid = 0; - - memset(stat, 0, sizeof(*stat)); - stat->nsec_before_min = UINT64_MAX; - stat->nsec_after_min = UINT64_MAX; - - for (int i = 1; i < test_global->opt.cpu_count + 1; i++) { - stat->nsec_before_sum += s[i].nsec_before_sum; - stat->nsec_after_sum += s[i].nsec_after_sum; - stat->num_before += s[i].num_before; - stat->num_exact += s[i].num_exact; - stat->num_after += s[i].num_after; - stat->num_too_near += s[i].num_too_near; - - if (s[i].nsec_before_min < stat->nsec_before_min) { - stat->nsec_before_min = s[i].nsec_before_min; - stat->nsec_before_min_idx = s[i].nsec_before_min_idx; - nsec_before_min_tid = i; - } - - if (s[i].nsec_after_min < stat->nsec_after_min) { - stat->nsec_after_min = s[i].nsec_after_min; - stat->nsec_after_min_idx = s[i].nsec_after_min_idx; - nsec_after_min_tid = i; - } - - if (s[i].nsec_before_max > stat->nsec_before_max) { - stat->nsec_before_max = s[i].nsec_before_max; - stat->nsec_before_max_idx = s[i].nsec_before_max_idx; - nsec_before_max_tid = i; - } - - if (s[i].nsec_after_max > stat->nsec_after_max) { - stat->nsec_after_max = s[i].nsec_after_max; - stat->nsec_after_max_idx = s[i].nsec_after_max_idx; - nsec_after_max_tid = i; - } - } - - if (stat->num_after) - ave_after = stat->nsec_after_sum / stat->num_after; - else - stat->nsec_after_min = 0; - - if (stat->num_before) - ave_before = stat->nsec_before_sum / stat->num_before; - else - stat->nsec_before_min = 0; - - tot_timers = stat->num_before + stat->num_after + stat->num_exact; - - if (log) { - FILE *file = test_global->file; - - fprintf(file, " Timer thread tmo(ns) diff(ns)\n"); - - for (uint64_t i = 0; i < tot_timers; i++) { - fprintf(file, "%8" PRIu64 " %7u %12" PRIu64 " %10" - PRIi64 "\n", i, log[i].tid, log[i].tmo_ns, log[i].diff_ns); - } - - fprintf(file, "\n"); - } - - printf("\nTest results:\n"); - printf(" num after: %12" PRIu64 " / %.2f%%\n", - stat->num_after, 100.0 * stat->num_after / tot_timers); - printf(" num before: %12" PRIu64 " / %.2f%%\n", - stat->num_before, 100.0 * stat->num_before / tot_timers); - printf(" num exact: %12" PRIu64 " / %.2f%%\n", - stat->num_exact, 100.0 * stat->num_exact / tot_timers); - printf(" num retry: %12" PRIu64 " / %.2f%%\n", - stat->num_too_near, 100.0 * stat->num_too_near / tot_timers); - printf(" error after (nsec):\n"); - print_nsec_error("min", stat->nsec_after_min, res_ns, nsec_after_min_tid, - stat->nsec_after_min_idx); - print_nsec_error("max", stat->nsec_after_max, res_ns, nsec_after_max_tid, - stat->nsec_after_max_idx); - print_nsec_error("ave", ave_after, res_ns, -1, -1); - printf(" error before (nsec):\n"); - print_nsec_error("min", stat->nsec_before_min, res_ns, nsec_before_min_tid, - stat->nsec_before_min_idx); - print_nsec_error("max", stat->nsec_before_max, res_ns, nsec_before_max_tid, - stat->nsec_before_max_idx); - print_nsec_error("ave", ave_before, res_ns, -1, -1); - - if (test_global->opt.mode == MODE_PERIODIC && !test_global->opt.offset_ns) { - int idx = 0; - int64_t max = 0; - - for (int i = 0; i < (int)test_global->opt.alloc_timers; i++) { - timer_ctx_t *t = &test_global->timer_ctx[i]; - int64_t v = t->first_tmo_diff; - - if (ODPH_ABS(v) > ODPH_ABS(max)) { - max = v; - idx = i; - } - } - - printf(" first timeout difference to one period, based on %s (nsec):\n", - test_global->timer_ctx[idx].tmo_tick ? "timeout tick" : "time"); - print_nsec_error("max", max, res_ns, -1, -1); - } - - int64_t max = 0; - - for (int i = 0; i < (int)test_global->opt.alloc_timers; i++) { - timer_ctx_t *t = &test_global->timer_ctx[i]; - int64_t v = t->nsec_final; - - if (ODPH_ABS(v) > ODPH_ABS(max)) - max = v; - } - - printf(" final timeout error (nsec):\n"); - print_nsec_error("max", max, res_ns, -1, -1); - - printf("\n"); -} - -static void cancel_periodic_timers(test_global_t *test_global) -{ - uint64_t i, alloc_timers; - odp_timer_t timer; - - alloc_timers = test_global->opt.alloc_timers; - - for (i = 0; i < alloc_timers; i++) { - timer = test_global->timer_ctx[i].timer; - - if (timer == ODP_TIMER_INVALID) - break; - - if (odp_timer_periodic_cancel(timer)) - printf("Failed to cancel periodic timer.\n"); - } -} - -static int run_test(void *arg) -{ - test_global_t *test_global = (test_global_t *)arg; - odp_event_t ev; - odp_time_t time; - uint64_t time_ns, diff_ns; - odp_timeout_t tmo; - uint64_t tmo_ns; - timer_ctx_t *ctx; - odp_thrmask_t mask; - uint64_t wait = odp_schedule_wait_time(10 * ODP_TIME_MSEC_IN_NS); - odp_schedule_group_t group = ODP_SCHED_GROUP_INVALID; - test_log_t *log = test_global->log; - enum mode_e mode = test_global->opt.mode; - uint64_t tot_timers = test_global->opt.tot_timers; - double period_dbl = test_global->period_dbl; - odp_timer_pool_t tp = test_global->timer_pool; - int tid = odp_thread_id(); - - if (tid > test_global->opt.cpu_count) { - printf("Error: tid %d is larger than cpu_count %d.\n", tid, - test_global->opt.cpu_count); - return 0; - } - - test_stat_t *stat = &test_global->stat[tid]; - - memset(stat, 0, sizeof(*stat)); - stat->nsec_before_min = UINT64_MAX; - stat->nsec_after_min = UINT64_MAX; - - if (test_global->opt.groups) { - odp_thrmask_zero(&mask); - odp_thrmask_set(&mask, tid); - group = test_global->group[tid - 1]; - - if (odp_schedule_group_join(group, &mask)) { - printf("odp_schedule_group_join() failed\n"); - return 0; - } - } - - odp_barrier_wait(&test_global->barrier); - - while (1) { - ev = odp_schedule(NULL, wait); - time = odp_time_global_strict(); - - if (ev == ODP_EVENT_INVALID) { - if (mode == MODE_PERIODIC) { - if (odp_atomic_load_u64(&test_global->last_events) >= - test_global->opt.alloc_timers) - break; - - } else if (odp_atomic_load_u64(&test_global->events) >= tot_timers) { - break; - } - - continue; - } - - time_ns = odp_time_to_ns(time); - tmo = odp_timeout_from_event(ev); - ctx = odp_timeout_user_ptr(tmo); - tmo_ns = ctx->nsec; - - if (mode == MODE_PERIODIC) { - if (!ctx->count && !test_global->opt.offset_ns) { - /* - * If first_tick is zero, the API allows the implementation to - * place the timer where it can, so we have to adjust our - * expectation of the timeout time. - */ - - uint64_t tmo_tick = odp_timeout_tick(tmo); - - if (tmo_tick) { - /* - * Adjust by the difference between one period after start - * time and the timeout tick. - */ - ctx->tmo_tick = 1; - ctx->first_tmo_diff = - (int64_t)odp_timer_tick_to_ns(tp, tmo_tick) - - (int64_t)odp_timer_tick_to_ns(tp, ctx->first_period); - tmo_ns += ctx->first_tmo_diff; - } else { - /* - * Timeout tick is not provided, so the best we can do is - * to just take the current time as a baseline. - */ - ctx->first_tmo_diff = (int64_t)time_ns - (int64_t)tmo_ns; - tmo_ns = ctx->nsec = time_ns; - } - - ctx->nsec = tmo_ns; - } - - /* round to closest integer number */ - tmo_ns += ctx->count * period_dbl + 0.5; - ctx->count++; - } - - uint64_t events = odp_atomic_fetch_inc_u64(&test_global->events); - - if (events >= test_global->opt.warmup_timers && events < tot_timers) { - uint64_t i = events - test_global->opt.warmup_timers; - - ctx->nsec_final = (int64_t)time_ns - (int64_t)tmo_ns; - - if (log) { - log[i].tmo_ns = tmo_ns; - log[i].tid = tid; - } - - if (time_ns > tmo_ns) { - diff_ns = time_ns - tmo_ns; - stat->num_after++; - stat->nsec_after_sum += diff_ns; - if (diff_ns < stat->nsec_after_min) { - stat->nsec_after_min = diff_ns; - stat->nsec_after_min_idx = i; - } - if (diff_ns > stat->nsec_after_max) { - stat->nsec_after_max = diff_ns; - stat->nsec_after_max_idx = i; - } - if (log) - log[i].diff_ns = diff_ns; - - } else if (time_ns < tmo_ns) { - diff_ns = tmo_ns - time_ns; - stat->num_before++; - stat->nsec_before_sum += diff_ns; - if (diff_ns < stat->nsec_before_min) { - stat->nsec_before_min = diff_ns; - stat->nsec_before_min_idx = i; - } - if (diff_ns > stat->nsec_before_max) { - stat->nsec_before_max = diff_ns; - stat->nsec_before_max_idx = i; - } - if (log) - log[i].diff_ns = -diff_ns; - } else { - stat->num_exact++; - } - } - - if ((mode == MODE_RESTART_ABS || mode == MODE_RESTART_REL) && - events < tot_timers - 1) { - /* Reset timer for next period */ - odp_timer_t tim; - uint64_t nsec, tick; - odp_timer_retval_t ret; - unsigned int j; - unsigned int retries = test_global->opt.early_retry; - uint64_t start_ns = test_global->start_ns; - uint64_t period_ns = test_global->opt.period_ns; - odp_timer_start_t start_param; - - tim = ctx->timer; - - /* Depending on the option, retry when expiration - * time is too early */ - for (j = 0; j < retries + 1; j++) { - if (mode == MODE_RESTART_ABS) { - /* Absolute time */ - ctx->nsec += period_ns; - nsec = ctx->nsec - start_ns; - tick = test_global->start_tick + - odp_timer_ns_to_tick(tp, nsec); - start_param.tick_type = ODP_TIMER_TICK_ABS; - } else { - /* Relative time */ - tick = test_global->period_tick; - time = odp_time_local(); - time_ns = odp_time_to_ns(time); - ctx->nsec = time_ns + period_ns; - start_param.tick_type = ODP_TIMER_TICK_REL; - } - - start_param.tmo_ev = ev; - start_param.tick = tick; - - ret = odp_timer_start(tim, &start_param); - if (ret == ODP_TIMER_TOO_NEAR) { - if (events >= test_global->opt.warmup_timers) - stat->num_too_near++; - } else { - break; - } - } - - if (ret != ODP_TIMER_SUCCESS) { - printf("Timer set failed: %i. Timeout nsec " - "%" PRIu64 "\n", ret, ctx->nsec); - return 0; - } - } else if (mode == MODE_PERIODIC) { - int ret = odp_timer_periodic_ack(ctx->timer, ev); - - if (ret < 0) - printf("Failed to ack a periodic timer.\n"); - - if (ret == 2) - odp_atomic_inc_u64(&test_global->last_events); - - if (ret == 2 || ret < 0) - odp_event_free(ev); - } else { - odp_event_free(ev); - } - } - - if (test_global->opt.groups) { - if (odp_schedule_group_leave(group, &mask)) - printf("odp_schedule_group_leave() failed\n"); - } - - return 0; -} - -int main(int argc, char *argv[]) -{ - odp_instance_t instance; - odp_init_t init; - test_opt_t test_opt; - test_global_t *test_global; - odph_helper_options_t helper_options; - odp_init_t *init_ptr = NULL; - int ret = 0; - - /* Let helper collect its own arguments (e.g. --odph_proc) */ - argc = odph_parse_options(argc, argv); - if (odph_options(&helper_options)) { - ODPH_ERR("Reading ODP helper options failed.\n"); - exit(EXIT_FAILURE); - } - - if (parse_options(argc, argv, &test_opt)) - 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.compress = 1; - init.not_used.feat.crypto = 1; - init.not_used.feat.ipsec = 1; - init.not_used.feat.tm = 1; - - init.mem_model = helper_options.mem_model; - - if (test_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_CONTROL)) { - printf("Local init failed.\n"); - return -1; - } - - odp_sys_info_print(); - - /* Configure scheduler */ - odp_schedule_config(NULL); - - odp_shm_t shm = ODP_SHM_INVALID, shm_ctx = ODP_SHM_INVALID, shm_log = ODP_SHM_INVALID; - uint64_t size = sizeof(test_global_t); - - shm = odp_shm_reserve("timer_accuracy", size, - ODP_CACHE_LINE_SIZE, ODP_SHM_SINGLE_VA); - - if (shm == ODP_SHM_INVALID) { - printf("Shm alloc failed.\n"); - return -1; - } - - test_global = odp_shm_addr(shm); - memset(test_global, 0, size); - memcpy(&test_global->opt, &test_opt, sizeof(test_opt_t)); - - size = test_global->opt.alloc_timers * sizeof(timer_ctx_t); - shm_ctx = odp_shm_reserve("timer_accuracy_ctx", size, - ODP_CACHE_LINE_SIZE, ODP_SHM_SINGLE_VA); - - if (shm_ctx == ODP_SHM_INVALID) { - printf("Timer context alloc failed.\n"); - ret = -1; - goto quit; - } - - test_global->timer_ctx = odp_shm_addr(shm_ctx); - memset(test_global->timer_ctx, 0, size); - - if (test_global->opt.output) { - test_global->file = fopen(test_global->opt.filename, "w"); - if (test_global->file == NULL) { - printf("Failed to open output file %s: %s\n", - test_global->opt.filename, strerror(errno)); - ret = -1; - goto quit; - } - - size = (test_global->opt.tot_timers - test_global->opt.warmup_timers) * - sizeof(test_log_t); - shm_log = odp_shm_reserve("timer_accuracy_log", size, sizeof(test_log_t), - ODP_SHM_SINGLE_VA); - - if (shm_log == ODP_SHM_INVALID) { - printf("Test log alloc failed.\n"); - ret = -1; - goto quit; - } - - test_global->log = odp_shm_addr(shm_log); - memset(test_global->log, 0, size); - } - - odph_thread_t thread_tbl[MAX_WORKERS]; - int num_workers; - odp_cpumask_t cpumask; - char cpumaskstr[ODP_CPUMASK_STR_SIZE]; - odph_thread_common_param_t thr_common; - odph_thread_param_t thr_param; - - memset(thread_tbl, 0, sizeof(thread_tbl)); - - num_workers = MAX_WORKERS; - if (test_global->opt.cpu_count && test_global->opt.cpu_count < MAX_WORKERS) - num_workers = test_global->opt.cpu_count; - num_workers = odp_cpumask_default_worker(&cpumask, num_workers); - test_global->opt.cpu_count = num_workers; - odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr)); - - printf("num worker threads: %i\n", num_workers); - printf("first CPU: %i\n", odp_cpumask_first(&cpumask)); - printf("cpu mask: %s\n", cpumaskstr); - - ret = create_timers(test_global); - if (ret) - goto quit; - - odp_barrier_init(&test_global->barrier, num_workers + 1); - odp_atomic_init_u64(&test_global->events, 0); - odp_atomic_init_u64(&test_global->last_events, 0); - - odph_thread_param_init(&thr_param); - thr_param.start = run_test; - thr_param.arg = (void *)test_global; - thr_param.thr_type = ODP_THREAD_WORKER; - - odph_thread_common_param_init(&thr_common); - thr_common.instance = instance; - thr_common.cpumask = &cpumask; - thr_common.share_param = 1; - - odph_thread_create(thread_tbl, &thr_common, &thr_param, num_workers); - odp_barrier_wait(&test_global->barrier); - - ret = start_timers(test_global); - if (ret) - goto quit; - - if (test_global->opt.mode == MODE_PERIODIC) { - while (odp_atomic_load_u64(&test_global->events) < test_global->opt.tot_timers) - odp_time_wait_ns(10 * ODP_TIME_MSEC_IN_NS); - - cancel_periodic_timers(test_global); - } - - odph_thread_join(thread_tbl, num_workers); - print_stat(test_global); - -quit: - if (test_global->file) - fclose(test_global->file); - - if (destroy_timers(test_global)) - ret = -1; - - if (shm_log != ODP_SHM_INVALID && odp_shm_free(shm_log)) - ret = -1; - - if (shm_ctx != ODP_SHM_INVALID && odp_shm_free(shm_ctx)) - ret = -1; - - if (odp_shm_free(shm)) - ret = -1; - - 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; -} |