For series: Reviewed-and-tested-by: Brian Brooks <brian.bro...@linaro.org>
On 09/14 11:53:06, Matias Elo wrote: > Add new scheduling latency benchmark application. The application > measures delays (avg, min, max) for high and low priority events. > > The test has a configurable number of TRAFFIC events and few SAMPLE events > (one common or one per priority). The scheduling latency is only measured > from the SAMPLE events to minimize measurement overhead. > > The application's command line arguments enable configuring: > - Number of processing threads > - Number of high/low priority queues > - Number of high/low priority events > - Use separate SAMPLE events for each priority > - Scheduled queue type (PARALLEL, ATOMIC, ORDERED) > > Signed-off-by: Matias Elo <matias....@nokia.com> > --- > > V2: > - Remove unnecessary 'num_workers' initialization (Maxim) > > test/common_plat/performance/.gitignore | 1 + > test/common_plat/performance/Makefile.am | 4 + > test/common_plat/performance/odp_sched_latency.c | 767 > +++++++++++++++++++++++ > 3 files changed, 772 insertions(+) > create mode 100644 test/common_plat/performance/odp_sched_latency.c > > diff --git a/test/common_plat/performance/.gitignore > b/test/common_plat/performance/.gitignore > index edcc832..1527d25 100644 > --- a/test/common_plat/performance/.gitignore > +++ b/test/common_plat/performance/.gitignore > @@ -4,4 +4,5 @@ odp_atomic > odp_crypto > odp_l2fwd > odp_pktio_perf > +odp_sched_latency > odp_scheduling > diff --git a/test/common_plat/performance/Makefile.am > b/test/common_plat/performance/Makefile.am > index d23bb3e..f5dd8dd 100644 > --- a/test/common_plat/performance/Makefile.am > +++ b/test/common_plat/performance/Makefile.am > @@ -5,6 +5,7 @@ TESTS_ENVIRONMENT += TEST_DIR=${builddir} > EXECUTABLES = odp_crypto$(EXEEXT) odp_pktio_perf$(EXEEXT) > > COMPILE_ONLY = odp_l2fwd$(EXEEXT) \ > + odp_sched_latency$(EXEEXT) \ > odp_scheduling$(EXEEXT) > > TESTSCRIPTS = odp_l2fwd_run.sh \ > @@ -20,6 +21,8 @@ bin_PROGRAMS = $(EXECUTABLES) $(COMPILE_ONLY) > > odp_crypto_LDFLAGS = $(AM_LDFLAGS) -static > odp_crypto_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test > +odp_sched_latency_LDFLAGS = $(AM_LDFLAGS) -static > +odp_sched_latency_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test > odp_scheduling_LDFLAGS = $(AM_LDFLAGS) -static > odp_scheduling_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/test > > @@ -27,6 +30,7 @@ noinst_HEADERS = \ > $(top_srcdir)/test/test_debug.h > > dist_odp_crypto_SOURCES = odp_crypto.c > +dist_odp_sched_latency_SOURCES = odp_sched_latency.c > dist_odp_scheduling_SOURCES = odp_scheduling.c > dist_odp_pktio_perf_SOURCES = odp_pktio_perf.c > > diff --git a/test/common_plat/performance/odp_sched_latency.c > b/test/common_plat/performance/odp_sched_latency.c > new file mode 100644 > index 0000000..063fb21 > --- /dev/null > +++ b/test/common_plat/performance/odp_sched_latency.c > @@ -0,0 +1,767 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +/** > + * @file > + * > + * @example odp_sched_latency.c ODP scheduling latency benchmark application > + */ > + > +#include <string.h> > +#include <stdlib.h> > +#include <inttypes.h> > + > +#include <test_debug.h> > + > +/* ODP main header */ > +#include <odp_api.h> > + > +/* ODP helper for Linux apps */ > +#include <odp/helper/linux.h> > + > +/* GNU lib C */ > +#include <getopt.h> > + > +#define MAX_WORKERS 64 /**< Maximum number of worker threads */ > +#define MAX_QUEUES 4096 /**< Maximum number of queues */ > +#define EVENT_POOL_SIZE (1024 * 1024) /**< Event pool size */ > +#define TEST_ROUNDS (4 * 1024 * 1024) /**< Test rounds for each > thread */ > +#define MAIN_THREAD 1 /**< Thread ID performing maintenance tasks */ > + > +/* Default values for command line arguments */ > +#define SAMPLE_EVENT_PER_PRIO 0 /**< Allocate a separate sample > event for > + each priority */ > +#define HI_PRIO_EVENTS 0 /**< Number of high priority events > */ > +#define LO_PRIO_EVENTS 32 /**< Number of low priority events > */ > +#define HI_PRIO_QUEUES 16 /**< Number of high priority queues > */ > +#define LO_PRIO_QUEUES 64 /**< Number of low priority queues > */ > + > +#define EVENTS_PER_HI_PRIO_QUEUE 0 /**< Alloc HI_PRIO_QUEUES x > HI_PRIO_EVENTS > + events */ > +#define EVENTS_PER_LO_PRIO_QUEUE 1 /**< Alloc LO_PRIO_QUEUES x > LO_PRIO_EVENTS > + events */ > +ODP_STATIC_ASSERT(HI_PRIO_QUEUES <= MAX_QUEUES, "Too many HI priority > queues"); > +ODP_STATIC_ASSERT(LO_PRIO_QUEUES <= MAX_QUEUES, "Too many LO priority > queues"); > + > +#define CACHE_ALIGN_ROUNDUP(x)\ > + ((ODP_CACHE_LINE_SIZE) * \ > + (((x) + ODP_CACHE_LINE_SIZE - 1) / (ODP_CACHE_LINE_SIZE))) > + > +/* Test priorities */ > +#define NUM_PRIOS 2 /**< Number of tested priorities */ > +#define HI_PRIO 0 > +#define LO_PRIO 1 > + > +/** Test event types */ > +typedef enum { > + WARM_UP, /**< Warm up event */ > + TRAFFIC, /**< Event used only as traffic load */ > + SAMPLE /**< Event used to measure latency */ > +} event_type_t; > + > +/** Test event */ > +typedef struct { > + uint64_t ts; /**< Send timestamp */ > + event_type_t type; /**< Message type */ > + int src_idx[NUM_PRIOS]; /**< Source ODP queue */ > + int prio; /**< Source queue priority */ > +} test_event_t; > + > +/** Test arguments */ > +typedef struct { > + int cpu_count; /**< CPU count */ > + odp_schedule_sync_t sync_type; /**< Scheduler sync type */ > + struct { > + int queues; /**< Number of scheduling queues */ > + int events; /**< Number of events */ > + odp_bool_t events_per_queue; /**< Allocate 'queues' x 'events' > + test events */ > + } prio[NUM_PRIOS]; > + odp_bool_t sample_per_prio; /**< Allocate a separate sample event for > + each priority */ > +} test_args_t; > + > +/** Latency measurements statistics */ > +typedef struct { > + uint64_t events; /**< Total number of received events */ > + uint64_t sample_events; /**< Number of received sample events */ > + uint64_t tot; /**< Total event latency. Sum of all events. */ > + uint64_t min; /**< Minimum event latency */ > + uint64_t max; /**< Maximum event latency */ > +} test_stat_t; > + > +/** Performance test statistics (per core) */ > +typedef union { > + test_stat_t prio[NUM_PRIOS]; /**< Test statistics per priority */ > + > + uint8_t pad[CACHE_ALIGN_ROUNDUP(NUM_PRIOS * sizeof(test_stat_t))]; > +} core_stat_t ODP_ALIGNED_CACHE; > + > +/** Test global variables */ > +typedef struct { > + core_stat_t core_stat[MAX_WORKERS]; /**< Core specific stats */ > + odp_barrier_t barrier; /**< Barrier for thread synchronization */ > + odp_pool_t pool; /**< Pool for allocating test events */ > + test_args_t args; /**< Parsed command line arguments */ > + odp_queue_t queue[NUM_PRIOS][MAX_QUEUES]; /**< Scheduled queues */ > +} test_globals_t; > + > +/** > + * Clear all scheduled queues. > + * > + * Retry to be sure that all buffers have been scheduled. > + */ > +static void clear_sched_queues(void) > +{ > + odp_event_t ev; > + > + while (1) { > + ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT); > + > + if (ev == ODP_EVENT_INVALID) > + break; > + > + odp_event_free(ev); > + } > +} > + > +/** > + * Enqueue events into queues > + * > + * @param prio Queue priority (HI_PRIO/LO_PRIO) > + * @param num_queues Number of queues > + * @param num_events Number of 'TRAFFIC' events > + * @param num_samples Number of 'SAMPLE' events > + * @param div_events If true, divide 'num_events' between 'num_queues'. if > + * false, enqueue 'num_events' to each queue. > + * @param globals Test shared data > + * > + * @retval 0 on success > + * @retval -1 on failure > + */ > +static int enqueue_events(int prio, int num_queues, int num_events, > + int num_samples, odp_bool_t div_events, > + test_globals_t *globals) > +{ > + odp_buffer_t buf[num_events + num_samples]; > + odp_event_t ev[num_events + num_samples]; > + odp_queue_t queue; > + test_event_t *event; > + int i, j, ret; > + int enq_events; > + int events_per_queue; > + int tot_events; > + int rdy_events = 0; > + > + tot_events = num_events + num_samples; > + > + if (!num_queues || !tot_events) > + return 0; > + > + events_per_queue = tot_events; > + if (div_events) > + events_per_queue = (tot_events + num_queues - 1) / num_queues; > + > + for (i = 0; i < num_queues; i++) { > + queue = globals->queue[prio][i]; > + > + ret = odp_buffer_alloc_multi(globals->pool, buf, > + events_per_queue); > + if (ret != events_per_queue) { > + LOG_ERR("Buffer alloc failed. Try increasing > EVENT_POOL_SIZE.\n"); > + ret = ret < 0 ? 0 : ret; > + odp_buffer_free_multi(buf, ret); > + return -1; > + } > + for (j = 0; j < events_per_queue; j++) { > + if (!odp_buffer_is_valid(buf[j])) { > + LOG_ERR("Buffer alloc failed\n"); > + odp_buffer_free_multi(buf, events_per_queue); > + return -1; > + } > + > + event = odp_buffer_addr(buf[j]); > + memset(event, 0, sizeof(test_event_t)); > + > + /* Latency isn't measured from the first processing > + * round. */ > + if (num_samples > 0) { > + event->type = WARM_UP; > + num_samples--; > + } else { > + event->type = TRAFFIC; > + } > + event->src_idx[prio] = i; > + event->prio = prio; > + ev[j] = odp_buffer_to_event(buf[j]); > + } > + > + enq_events = 0; > + do { > + ret = odp_queue_enq_multi(queue, &ev[enq_events], > + events_per_queue - > + enq_events); > + if (ret < 0) { > + LOG_ERR("Queue enqueue failed.\n"); > + return -1; > + } > + enq_events += ret; > + } while (enq_events < events_per_queue); > + > + rdy_events += events_per_queue; > + if (div_events && rdy_events >= tot_events) > + return 0; > + } > + return 0; > +} > + > +/** > + * Print latency measurement results > + * > + * @param globals Test shared data > + */ > +static void print_results(test_globals_t *globals) > +{ > + test_stat_t *lat; > + odp_schedule_sync_t stype; > + test_stat_t total; > + test_args_t *args; > + uint64_t avg; > + int i, j; > + > + args = &globals->args; > + stype = globals->args.sync_type; > + > + printf("\n%s queue scheduling latency\n", > + (stype == ODP_SCHED_SYNC_ATOMIC) ? "ATOMIC" : > + ((stype == ODP_SCHED_SYNC_ORDERED) ? "ORDERED" : "PARALLEL")); > + > + printf(" LO_PRIO queues: %i\n", args->prio[LO_PRIO].queues); > + if (args->prio[LO_PRIO].events_per_queue) > + printf(" LO_PRIO event per queue: %i\n", > + args->prio[LO_PRIO].events); > + else > + printf(" LO_PRIO events: %i\n", args->prio[LO_PRIO].events); > + > + printf(" HI_PRIO queues: %i\n", args->prio[HI_PRIO].queues); > + if (args->prio[HI_PRIO].events_per_queue) > + printf(" HI_PRIO event per queue: %i\n\n", > + args->prio[HI_PRIO].events); > + else > + printf(" HI_PRIO events: %i\n\n", args->prio[HI_PRIO].events); > + > + for (i = 0; i < NUM_PRIOS; i++) { > + memset(&total, 0, sizeof(test_stat_t)); > + total.min = UINT64_MAX; > + > + printf("%s priority\n" > + "Thread Avg[ns] Min[ns] Max[ns] Samples > Total\n" > + > "---------------------------------------------------------------\n", > + i == HI_PRIO ? "HIGH" : "LOW"); > + for (j = 1; j <= args->cpu_count; j++) { > + lat = &globals->core_stat[j].prio[i]; > + > + if (lat->sample_events == 0) { > + printf("%-8d N/A\n", j); > + continue; > + } > + > + if (lat->max > total.max) > + total.max = lat->max; > + if (lat->min < total.min) > + total.min = lat->min; > + total.tot += lat->tot; > + total.sample_events += lat->sample_events; > + total.events += lat->events; > + > + avg = lat->events ? lat->tot / lat->sample_events : 0; > + printf("%-8d %-10" PRIu64 " %-10" PRIu64 " " > + "%-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 "\n", > + j, avg, lat->min, lat->max, lat->sample_events, > + lat->events); > + } > + > printf("---------------------------------------------------------------\n"); > + if (total.sample_events == 0) { > + printf("Total N/A\n\n"); > + continue; > + } > + avg = total.events ? total.tot / total.sample_events : 0; > + printf("Total %-10" PRIu64 " %-10" PRIu64 " %-10" PRIu64 " " > + "%-10" PRIu64 " %-10" PRIu64 "\n\n", avg, total.min, > + total.max, total.sample_events, total.events); > + } > +} > + > +/** > + * Measure latency of scheduled ODP events > + * > + * Schedule and enqueue events until 'TEST_ROUNDS' events have been > processed. > + * Scheduling latency is measured only from type 'SAMPLE' events. Other > events > + * are simply enqueued back to the scheduling queues. > + * > + * For 'TRAFFIC' type events the destination queue is selected from the same > + * priority class as source queue. 'SAMPLE' type event may change priority > + * depending on the command line arguments. > + * > + * @param thr Thread ID > + * @param globals Test shared data > + * > + * @retval 0 on success > + * @retval -1 on failure > + */ > +static int test_schedule(int thr, test_globals_t *globals) > +{ > + odp_event_t ev; > + odp_buffer_t buf; > + odp_queue_t src_queue; > + odp_queue_t dst_queue; > + uint64_t latency; > + uint32_t i; > + test_event_t *event; > + test_stat_t *stats; > + int dst_idx; > + > + memset(&globals->core_stat[thr], 0, sizeof(core_stat_t)); > + globals->core_stat[thr].prio[HI_PRIO].min = UINT64_MAX; > + globals->core_stat[thr].prio[LO_PRIO].min = UINT64_MAX; > + > + for (i = 0; i < TEST_ROUNDS; i++) { > + ev = odp_schedule(&src_queue, ODP_SCHED_WAIT); > + > + buf = odp_buffer_from_event(ev); > + event = odp_buffer_addr(buf); > + > + stats = &globals->core_stat[thr].prio[event->prio]; > + > + if (event->type == SAMPLE) { > + latency = odp_time_to_ns(odp_time_global()) - event->ts; > + > + if (latency > stats->max) > + stats->max = latency; > + if (latency < stats->min) > + stats->min = latency; > + stats->tot += latency; > + stats->sample_events++; > + > + /* Move sample event to a different priority */ > + if (!globals->args.sample_per_prio && > + globals->args.prio[!event->prio].queues) > + event->prio = !event->prio; > + } > + > + if (odp_unlikely(event->type == WARM_UP)) > + event->type = SAMPLE; > + else > + stats->events++; > + > + /* Move event to next queue */ > + dst_idx = event->src_idx[event->prio] + 1; > + if (dst_idx >= globals->args.prio[event->prio].queues) > + dst_idx = 0; > + event->src_idx[event->prio] = dst_idx; > + dst_queue = globals->queue[event->prio][dst_idx]; > + > + if (event->type == SAMPLE) > + event->ts = odp_time_to_ns(odp_time_global()); > + > + if (odp_queue_enq(dst_queue, ev)) { > + LOG_ERR("[%i] Queue enqueue failed.\n", thr); > + odp_event_free(ev); > + return -1; > + } > + } > + > + /* Clear possible locally stored buffers */ > + odp_schedule_pause(); > + > + while (1) { > + ev = odp_schedule(&src_queue, ODP_SCHED_NO_WAIT); > + > + if (ev == ODP_EVENT_INVALID) > + break; > + > + if (odp_queue_enq(src_queue, ev)) { > + LOG_ERR("[%i] Queue enqueue failed.\n", thr); > + odp_event_free(ev); > + return -1; > + } > + } > + > + odp_schedule_resume(); > + > + odp_barrier_wait(&globals->barrier); > + > + clear_sched_queues(); > + > + if (thr == MAIN_THREAD) > + print_results(globals); > + > + return 0; > +} > + > +/** > + * Worker thread > + * > + * @param arg Arguments > + * > + * @retval 0 on success > + * @retval -1 on failure > + */ > +static int run_thread(void *arg ODP_UNUSED) > +{ > + odp_shm_t shm; > + test_globals_t *globals; > + test_args_t *args; > + int thr; > + int sample_events = 0; > + > + thr = odp_thread_id(); > + > + shm = odp_shm_lookup("test_globals"); > + globals = odp_shm_addr(shm); > + > + if (globals == NULL) { > + LOG_ERR("Shared mem lookup failed\n"); > + return -1; > + } > + > + if (thr == MAIN_THREAD) { > + args = &globals->args; > + > + if (enqueue_events(HI_PRIO, args->prio[HI_PRIO].queues, > + args->prio[HI_PRIO].events, 1, > + !args->prio[HI_PRIO].events_per_queue, > + globals)) > + return -1; > + > + if (!args->prio[HI_PRIO].queues || args->sample_per_prio) > + sample_events = 1; > + > + if (enqueue_events(LO_PRIO, args->prio[LO_PRIO].queues, > + args->prio[LO_PRIO].events, sample_events, > + !args->prio[LO_PRIO].events_per_queue, > + globals)) > + return -1; > + } > + > + odp_barrier_wait(&globals->barrier); > + > + if (test_schedule(thr, globals)) > + return -1; > + > + return 0; > +} > + > +/** > + * Print usage information > + */ > +static void usage(void) > +{ > + printf("\n" > + "OpenDataPlane scheduler latency benchmark application.\n" > + "\n" > + "Usage: ./odp_sched_latency [options]\n" > + "Optional OPTIONS:\n" > + " -c, --count <number> CPU count\n" > + " -l, --lo-prio-queues <number> Number of low priority > scheduled queues\n" > + " -t, --hi-prio-queues <number> Number of high priority > scheduled queues\n" > + " -m, --lo-prio-events-per-queue <number> Number of events per > low priority queue\n" > + " -n, --hi-prio-events-per-queue <number> Number of events per > high priority queues\n" > + " -o, --lo-prio-events <number> Total number of low priority > events (overrides the\n" > + " number of events per queue)\n" > + " -p, --hi-prio-events <number> Total number of high priority > events (overrides the\n" > + " number of events per queue)\n" > + " -r --sample-per-prio Allocate a separate sample event for > each priority. By default\n" > + " a single sample event is used and its > priority is changed after\n" > + " each processing round.\n" > + " -s, --sync Scheduled queues' sync type\n" > + " 0: ODP_SCHED_SYNC_PARALLEL (default)\n" > + " 1: ODP_SCHED_SYNC_ATOMIC\n" > + " 2: ODP_SCHED_SYNC_ORDERED\n" > + " -h, --help Display help and exit.\n\n" > + ); > +} > + > +/** > + * Parse arguments > + * > + * @param argc Argument count > + * @param argv Argument vector > + * @param args Test arguments > + */ > +static void parse_args(int argc, char *argv[], test_args_t *args) > +{ > + int opt; > + int long_index; > + int i; > + > + static const struct option longopts[] = { > + {"count", required_argument, NULL, 'c'}, > + {"lo-prio-queues", required_argument, NULL, 'l'}, > + {"hi-prio-queues", required_argument, NULL, 't'}, > + {"lo-prio-events-per-queue", required_argument, NULL, 'm'}, > + {"hi-prio-events-per-queue", required_argument, NULL, 'n'}, > + {"lo-prio-events", required_argument, NULL, 'o'}, > + {"hi-prio-events", required_argument, NULL, 'p'}, > + {"sample-per-prio", no_argument, NULL, 'r'}, > + {"sync", required_argument, NULL, 's'}, > + {"help", no_argument, NULL, 'h'}, > + {NULL, 0, NULL, 0} > + }; > + > + static const char *shortopts = "+c:s:l:t:m:n:o:p:rh"; > + > + /* Let helper collect its own arguments (e.g. --odph_proc) */ > + odph_parse_options(argc, argv, shortopts, longopts); > + > + args->sync_type = ODP_SCHED_SYNC_PARALLEL; > + args->sample_per_prio = SAMPLE_EVENT_PER_PRIO; > + args->prio[LO_PRIO].queues = LO_PRIO_QUEUES; > + args->prio[HI_PRIO].queues = HI_PRIO_QUEUES; > + args->prio[LO_PRIO].events = LO_PRIO_EVENTS; > + args->prio[HI_PRIO].events = HI_PRIO_EVENTS; > + args->prio[LO_PRIO].events_per_queue = EVENTS_PER_LO_PRIO_QUEUE; > + args->prio[HI_PRIO].events_per_queue = EVENTS_PER_HI_PRIO_QUEUE; > + > + opterr = 0; /* Do not issue errors on helper options */ > + while (1) { > + opt = getopt_long(argc, argv, shortopts, longopts, &long_index); > + > + if (opt == -1) > + break; /* No more options */ > + > + switch (opt) { > + case 'c': > + args->cpu_count = atoi(optarg); > + break; > + case 'l': > + args->prio[LO_PRIO].queues = atoi(optarg); > + break; > + case 't': > + args->prio[HI_PRIO].queues = atoi(optarg); > + break; > + case 'm': > + args->prio[LO_PRIO].events = atoi(optarg); > + args->prio[LO_PRIO].events_per_queue = 1; > + break; > + case 'n': > + args->prio[HI_PRIO].events = atoi(optarg); > + args->prio[HI_PRIO].events_per_queue = 1; > + break; > + case 'o': > + args->prio[LO_PRIO].events = atoi(optarg); > + args->prio[LO_PRIO].events_per_queue = 0; > + break; > + case 'p': > + args->prio[HI_PRIO].events = atoi(optarg); > + args->prio[HI_PRIO].events_per_queue = 0; > + break; > + case 's': > + i = atoi(optarg); > + if (i == 1) > + args->sync_type = ODP_SCHED_SYNC_ATOMIC; > + else if (i == 2) > + args->sync_type = ODP_SCHED_SYNC_ORDERED; > + else > + args->sync_type = ODP_SCHED_SYNC_PARALLEL; > + break; > + case 'r': > + args->sample_per_prio = 1; > + break; > + case 'h': > + usage(); > + exit(EXIT_SUCCESS); > + break; > + > + default: > + break; > + } > + } > + > + /* Make sure arguments are valid */ > + if (args->cpu_count > MAX_WORKERS) > + args->cpu_count = MAX_WORKERS; > + if (args->prio[LO_PRIO].queues > MAX_QUEUES) > + args->prio[LO_PRIO].queues = MAX_QUEUES; > + if (args->prio[HI_PRIO].queues > MAX_QUEUES) > + args->prio[HI_PRIO].queues = MAX_QUEUES; > + if (!args->prio[HI_PRIO].queues && !args->prio[LO_PRIO].queues) { > + printf("No queues configured\n"); > + usage(); > + exit(EXIT_FAILURE); > + } > +} > + > +/** > + * Test main function > + */ > +int main(int argc, char *argv[]) > +{ > + odp_instance_t instance; > + odph_odpthread_t *thread_tbl; > + odph_odpthread_params_t thr_params; > + odp_cpumask_t cpumask; > + odp_pool_t pool; > + odp_pool_param_t params; > + odp_shm_t shm; > + test_globals_t *globals; > + test_args_t args; > + char cpumaskstr[ODP_CPUMASK_STR_SIZE]; > + int i, j; > + int ret = 0; > + int num_workers = 0; > + > + printf("\nODP scheduling latency benchmark starts\n\n"); > + > + memset(&args, 0, sizeof(args)); > + parse_args(argc, argv, &args); > + > + /* ODP global init */ > + if (odp_init_global(&instance, NULL, NULL)) { > + LOG_ERR("ODP global init failed.\n"); > + return -1; > + } > + > + /* > + * Init this thread. It makes also ODP calls when > + * setting up resources for worker threads. > + */ > + if (odp_init_local(instance, ODP_THREAD_CONTROL)) { > + LOG_ERR("ODP global init failed.\n"); > + return -1; > + } > + > + printf("\n"); > + printf("ODP system info\n"); > + printf("---------------\n"); > + printf("ODP API version: %s\n", odp_version_api_str()); > + printf("ODP impl name: %s\n", odp_version_impl_name()); > + printf("ODP impl details: %s\n", odp_version_impl_str()); > + printf("CPU model: %s\n", odp_cpu_model_str()); > + printf("CPU freq (hz): %" PRIu64 "\n", odp_cpu_hz_max()); > + printf("Cache line size: %i\n", odp_sys_cache_line_size()); > + printf("Max CPU count: %i\n", odp_cpu_count()); > + > + /* Get default worker cpumask */ > + if (args.cpu_count) > + num_workers = args.cpu_count; > + > + num_workers = odp_cpumask_default_worker(&cpumask, num_workers); > + args.cpu_count = num_workers; > + > + (void)odp_cpumask_to_str(&cpumask, cpumaskstr, sizeof(cpumaskstr)); > + > + printf("Worker threads: %i\n", num_workers); > + printf("First CPU: %i\n", odp_cpumask_first(&cpumask)); > + printf("CPU mask: %s\n\n", cpumaskstr); > + > + thread_tbl = calloc(sizeof(odph_odpthread_t), num_workers); > + if (!thread_tbl) { > + LOG_ERR("no memory for thread_tbl\n"); > + return -1; > + } > + > + shm = odp_shm_reserve("test_globals", > + sizeof(test_globals_t), ODP_CACHE_LINE_SIZE, 0); > + if (shm == ODP_SHM_INVALID) { > + LOG_ERR("Shared memory reserve failed.\n"); > + return -1; > + } > + > + globals = odp_shm_addr(shm); > + memset(globals, 0, sizeof(test_globals_t)); > + memcpy(&globals->args, &args, sizeof(test_args_t)); > + > + /* > + * Create event pool > + */ > + odp_pool_param_init(¶ms); > + params.buf.size = sizeof(test_event_t); > + params.buf.align = 0; > + params.buf.num = EVENT_POOL_SIZE; > + params.type = ODP_POOL_BUFFER; > + > + pool = odp_pool_create("event_pool", ¶ms); > + > + if (pool == ODP_POOL_INVALID) { > + LOG_ERR("Pool create failed.\n"); > + return -1; > + } > + globals->pool = pool; > + > + /* > + * Create queues for schedule test > + */ > + for (i = 0; i < NUM_PRIOS; i++) { > + char name[] = "sched_XX_YY"; > + odp_queue_t queue; > + odp_queue_param_t param; > + int prio; > + > + if (i == HI_PRIO) > + prio = ODP_SCHED_PRIO_HIGHEST; > + else > + prio = ODP_SCHED_PRIO_LOWEST; > + > + name[6] = '0' + (prio / 10); > + name[7] = '0' + prio - (10 * (prio / 10)); > + > + odp_queue_param_init(¶m); > + param.type = ODP_QUEUE_TYPE_SCHED; > + param.sched.prio = prio; > + param.sched.sync = args.sync_type; > + param.sched.group = ODP_SCHED_GROUP_ALL; > + > + for (j = 0; j < args.prio[i].queues; j++) { > + name[9] = '0' + j / 10; > + name[10] = '0' + j - 10 * (j / 10); > + > + queue = odp_queue_create(name, ¶m); > + > + if (queue == ODP_QUEUE_INVALID) { > + LOG_ERR("Scheduled queue create failed.\n"); > + return -1; > + } > + > + globals->queue[i][j] = queue; > + } > + } > + > + odp_barrier_init(&globals->barrier, num_workers); > + > + /* Create and launch worker threads */ > + memset(&thr_params, 0, sizeof(thr_params)); > + thr_params.thr_type = ODP_THREAD_WORKER; > + thr_params.instance = instance; > + thr_params.start = run_thread; > + thr_params.arg = NULL; > + odph_odpthreads_create(thread_tbl, &cpumask, &thr_params); > + > + /* Wait for worker threads to terminate */ > + odph_odpthreads_join(thread_tbl); > + free(thread_tbl); > + > + printf("ODP scheduling latency test complete\n\n"); > + > + for (i = 0; i < NUM_PRIOS; i++) { > + odp_queue_t queue; > + int num_queues; > + > + num_queues = args.prio[i].queues; > + > + for (j = 0; j < num_queues; j++) { > + queue = globals->queue[i][j]; > + ret += odp_queue_destroy(queue); > + } > + } > + > + ret += odp_shm_free(shm); > + ret += odp_pool_destroy(pool); > + ret += odp_term_local(); > + ret += odp_term_global(instance); > + > + return ret; > +} > -- > 2.7.4 >