/* Copyright (c) 2017-2018, Linaro Limited * Copyright (c) 2022-2023, Nokia * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include "bench_common.h" #include #include #include #include #include /** Default buffer size */ #define TEST_BUF_SIZE 1024 /** Default pool user area size in bytes */ #define TEST_UAREA_SIZE 8 /** Number of API function calls per test case */ #define TEST_REPEAT_COUNT 1000 /** Default number of rounds per test case */ #define TEST_ROUNDS 1000u /** Maximum burst size for *_multi operations */ #define TEST_MAX_BURST 64 /** Default burst size for *_multi operations */ #define TEST_DEF_BURST 8 /** Get rid of path in filename - only for unix-type paths using '/' */ #define NO_PATH(file_name) (strrchr((file_name), '/') ? \ strrchr((file_name), '/') + 1 : (file_name)) #define BENCH_INFO(run_fn, init_fn, term_fn, alt_name) \ {.name = #run_fn, .run = run_fn, .init = init_fn, .term = term_fn, .desc = alt_name} #define BENCH_INFO_COND(run_fn, init_fn, term_fn, alt_name, cond_fn) \ {.name = #run_fn, .run = run_fn, .init = init_fn, .term = term_fn, .desc = alt_name, \ .cond = cond_fn} /** * Parsed command line arguments */ typedef struct { int bench_idx; /** Benchmark index to run indefinitely */ int burst_size; /** Burst size for *_multi operations */ int cache_size; /** Pool cache size */ int time; /** Measure time vs. CPU cycles */ uint32_t rounds; /** Rounds per test case */ } appl_args_t; /** * Grouping of all global data */ typedef struct { /** Application (parsed) arguments */ appl_args_t appl; /** Common benchmark suite data */ bench_suite_t suite; /** Buffer pool */ odp_pool_t pool; /** Buffer size */ uint32_t buf_size; /** Buffer user area size */ uint32_t uarea_size; /** Max flow id */ uint32_t max_flow_id; /** Array for storing test buffers */ odp_buffer_t buf_tbl[TEST_REPEAT_COUNT * TEST_MAX_BURST]; /** Array for storing test event */ odp_event_t event_tbl[TEST_REPEAT_COUNT * TEST_MAX_BURST]; /** Array for storing test pointers */ void *ptr_tbl[TEST_REPEAT_COUNT]; /** Array for storing test pool handles */ odp_pool_t pool_tbl[TEST_REPEAT_COUNT]; /** Array for storing test event types */ odp_event_type_t event_type_tbl[TEST_REPEAT_COUNT]; /** Array for storing test event subtypes */ odp_event_subtype_t event_subtype_tbl[TEST_REPEAT_COUNT]; /** CPU mask as string */ char cpumask_str[ODP_CPUMASK_STR_SIZE]; } args_t; /** Global pointer to args */ static args_t *gbl_args; static void sig_handler(int signo ODP_UNUSED) { if (gbl_args == NULL) return; odp_atomic_store_u32(&gbl_args->suite.exit_worker, 1); } static void allocate_test_buffers(odp_buffer_t buf[], int num) { int num_buf = 0; while (num_buf < num) { int ret; ret = odp_buffer_alloc_multi(gbl_args->pool, &buf[num_buf], num - num_buf); if (ret < 0) ODPH_ABORT("Allocating test buffers failed\n"); num_buf += ret; } } static void alloc_buffers_multi(void) { allocate_test_buffers(gbl_args->buf_tbl, TEST_REPEAT_COUNT * gbl_args->appl.burst_size); } static void create_buffers(void) { allocate_test_buffers(gbl_args->buf_tbl, TEST_REPEAT_COUNT); } static void create_events(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; allocate_test_buffers(gbl_args->buf_tbl, TEST_REPEAT_COUNT); for (int i = 0; i < TEST_REPEAT_COUNT; i++) gbl_args->event_tbl[i] = odp_buffer_to_event(buf_tbl[i]); } static void create_events_multi(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; allocate_test_buffers(gbl_args->buf_tbl, TEST_REPEAT_COUNT * gbl_args->appl.burst_size); for (int i = 0; i < TEST_REPEAT_COUNT * gbl_args->appl.burst_size; i++) gbl_args->event_tbl[i] = odp_buffer_to_event(buf_tbl[i]); } static void free_buffers(void) { odp_buffer_free_multi(gbl_args->buf_tbl, TEST_REPEAT_COUNT); } static void free_buffers_multi(void) { odp_buffer_free_multi(gbl_args->buf_tbl, TEST_REPEAT_COUNT * gbl_args->appl.burst_size); } static int check_uarea(void) { return !!gbl_args->uarea_size; } static int check_flow_aware(void) { return !!gbl_args->max_flow_id; } static int buffer_from_event(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; odp_event_t *event_tbl = gbl_args->event_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) buf_tbl[i] = odp_buffer_from_event(event_tbl[i]); return i; } static int buffer_to_event(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; odp_event_t *event_tbl = gbl_args->event_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) event_tbl[i] = odp_buffer_to_event(buf_tbl[i]); return i; } static int buffer_addr(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; void **ptr_tbl = gbl_args->ptr_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) ptr_tbl[i] = odp_buffer_addr(buf_tbl[i]); return i; } static int buffer_size(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; uint32_t ret = 0; for (int i = 0; i < TEST_REPEAT_COUNT; i++) ret += odp_buffer_size(buf_tbl[i]); return ret; } static int buffer_user_area(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; void **ptr_tbl = gbl_args->ptr_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) ptr_tbl[i] = odp_buffer_user_area(buf_tbl[i]); return i; } static int buffer_pool(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; odp_pool_t *pool_tbl = gbl_args->pool_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) pool_tbl[i] = odp_buffer_pool(buf_tbl[i]); return i; } static int buffer_alloc(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; odp_pool_t pool = gbl_args->pool; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) buf_tbl[i] = odp_buffer_alloc(pool); return i; } static int buffer_alloc_multi(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; odp_pool_t pool = gbl_args->pool; int burst_size = gbl_args->appl.burst_size; int num = 0; for (int i = 0; i < TEST_REPEAT_COUNT; i++) num += odp_buffer_alloc_multi(pool, &buf_tbl[num], burst_size); return num; } static int buffer_free(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) odp_buffer_free(buf_tbl[i]); return i; } static int buffer_free_multi(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; int burst_size = gbl_args->appl.burst_size; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) odp_buffer_free_multi(&buf_tbl[i * burst_size], burst_size); return i; } static int buffer_alloc_free(void) { odp_pool_t pool = gbl_args->pool; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) { odp_buffer_t buf = odp_buffer_alloc(pool); if (odp_unlikely(buf == ODP_BUFFER_INVALID)) return 0; odp_buffer_free(buf); } return i; } static int buffer_alloc_free_multi(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; odp_pool_t pool = gbl_args->pool; int burst_size = gbl_args->appl.burst_size; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) { int num = odp_buffer_alloc_multi(pool, buf_tbl, burst_size); if (odp_unlikely(num < 1)) return 0; odp_buffer_free_multi(buf_tbl, num); } return i; } static int buffer_is_valid(void) { odp_buffer_t *buf_tbl = gbl_args->buf_tbl; uint32_t ret = 0; for (int i = 0; i < TEST_REPEAT_COUNT; i++) ret += odp_buffer_is_valid(buf_tbl[i]); return ret; } static int event_type(void) { odp_event_t *event_tbl = gbl_args->event_tbl; odp_event_type_t *event_type_tbl = gbl_args->event_type_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) event_type_tbl[i] = odp_event_type(event_tbl[i]); return i; } static int event_subtype(void) { odp_event_t *event_tbl = gbl_args->event_tbl; odp_event_subtype_t *event_subtype_tbl = gbl_args->event_subtype_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) event_subtype_tbl[i] = odp_event_subtype(event_tbl[i]); return i; } static int event_types(void) { odp_event_t *event_tbl = gbl_args->event_tbl; odp_event_type_t *event_type_tbl = gbl_args->event_type_tbl; odp_event_subtype_t *event_subtype_tbl = gbl_args->event_subtype_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) event_type_tbl[i] = odp_event_types(event_tbl[i], &event_subtype_tbl[i]); return i; } static int event_type_multi(void) { odp_event_t *event_tbl = gbl_args->event_tbl; odp_event_type_t *event_type_tbl = gbl_args->event_type_tbl; int burst_size = gbl_args->appl.burst_size; uint32_t ret = 0; for (int i = 0; i < TEST_REPEAT_COUNT; i++) ret += odp_event_type_multi(&event_tbl[i * burst_size], burst_size, &event_type_tbl[i]); return ret; } static int event_is_valid(void) { odp_event_t *event_tbl = gbl_args->event_tbl; uint32_t ret = 0; for (int i = 0; i < TEST_REPEAT_COUNT; i++) ret += odp_event_is_valid(event_tbl[i]); return ret; } static int event_free(void) { odp_event_t *event_tbl = gbl_args->event_tbl; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) odp_event_free(event_tbl[i]); return i; } static int event_free_multi(void) { odp_event_t *event_tbl = gbl_args->event_tbl; int burst_size = gbl_args->appl.burst_size; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) odp_event_free_multi(&event_tbl[i * burst_size], burst_size); return i; } static int event_free_sp(void) { odp_event_t *event_tbl = gbl_args->event_tbl; int burst_size = gbl_args->appl.burst_size; int i; for (i = 0; i < TEST_REPEAT_COUNT; i++) odp_event_free_sp(&event_tbl[i * burst_size], burst_size); return i; } static int event_flow_id(void) { odp_event_t *event_tbl = gbl_args->event_tbl; uint32_t ret = 0; for (int i = 0; i < TEST_REPEAT_COUNT; i++) ret += odp_event_flow_id(event_tbl[i]); return !ret; } static int event_flow_id_set(void) { odp_event_t *event_tbl = gbl_args->event_tbl; int i = 0; for (i = 0; i < TEST_REPEAT_COUNT; i++) odp_event_flow_id_set(event_tbl[i], 0); return i; } /** * Print usage information */ static void usage(char *progname) { printf("\n" "OpenDataPlane Buffer/Event API microbenchmarks.\n" "\n" "Usage: %s OPTIONS\n" " E.g. %s\n" "\n" "Optional OPTIONS:\n" " -b, --burst Test burst size.\n" " -c, --cache_size Pool cache size.\n" " -i, --index Benchmark index to run indefinitely.\n" " -r, --rounds Run each test case 'num' times (default %u).\n" " -t, --time Time measurement. 0: measure CPU cycles (default), 1: measure time\n" " -h, --help Display help and exit.\n\n" "\n", NO_PATH(progname), NO_PATH(progname), TEST_ROUNDS); } /** * Parse and store the command line arguments * * @param argc argument count * @param argv[] argument vector * @param appl_args Store application arguments here */ static void parse_args(int argc, char *argv[], appl_args_t *appl_args) { int opt; int long_index; static const struct option longopts[] = { {"burst", required_argument, NULL, 'b'}, {"cache_size", required_argument, NULL, 'c'}, {"index", required_argument, NULL, 'i'}, {"rounds", required_argument, NULL, 'r'}, {"time", required_argument, NULL, 't'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; static const char *shortopts = "c:b:i:r:t:h"; appl_args->bench_idx = 0; /* Run all benchmarks */ appl_args->burst_size = TEST_DEF_BURST; appl_args->cache_size = -1; appl_args->rounds = TEST_ROUNDS; appl_args->time = 0; while (1) { opt = getopt_long(argc, argv, shortopts, longopts, &long_index); if (opt == -1) break; /* No more options */ switch (opt) { case 'c': appl_args->cache_size = atoi(optarg); break; case 'b': appl_args->burst_size = atoi(optarg); break; case 'h': usage(argv[0]); exit(EXIT_SUCCESS); break; case 'i': appl_args->bench_idx = atoi(optarg); break; case 'r': appl_args->rounds = atoi(optarg); break; case 't': appl_args->time = atoi(optarg); break; default: break; } } if (appl_args->burst_size < 1 || appl_args->burst_size > TEST_MAX_BURST) { printf("Invalid burst size (max %d)\n", TEST_MAX_BURST); exit(EXIT_FAILURE); } if (appl_args->rounds < 1) { printf("Invalid number test rounds: %d\n", appl_args->rounds); exit(EXIT_FAILURE); } optind = 1; /* Reset 'extern optind' from the getopt lib */ } /** * Print system and application info */ static void print_info(void) { odp_sys_info_print(); printf("\n" "odp_bench_buffer options\n" "------------------------\n"); printf("Burst size: %d\n", gbl_args->appl.burst_size); printf("Buffer size: %d\n", gbl_args->buf_size); printf("CPU mask: %s\n", gbl_args->cpumask_str); if (gbl_args->appl.cache_size < 0) printf("Pool cache size: default\n"); else printf("Pool cache size: %d\n", gbl_args->appl.cache_size); printf("Measurement unit: %s\n", gbl_args->appl.time ? "nsec" : "CPU cycles"); printf("Test rounds: %u\n", gbl_args->appl.rounds); printf("\n"); } /** * Test functions */ bench_info_t test_suite[] = { BENCH_INFO(buffer_from_event, create_events, free_buffers, NULL), BENCH_INFO(buffer_to_event, create_buffers, free_buffers, NULL), BENCH_INFO(buffer_addr, create_buffers, free_buffers, NULL), BENCH_INFO(buffer_size, create_buffers, free_buffers, NULL), BENCH_INFO_COND(buffer_user_area, create_buffers, free_buffers, NULL, check_uarea), BENCH_INFO(buffer_pool, create_buffers, free_buffers, NULL), BENCH_INFO(buffer_alloc, NULL, free_buffers, NULL), BENCH_INFO(buffer_alloc_multi, NULL, free_buffers_multi, NULL), BENCH_INFO(buffer_free, create_buffers, NULL, NULL), BENCH_INFO(buffer_free_multi, alloc_buffers_multi, NULL, NULL), BENCH_INFO(buffer_alloc_free, NULL, NULL, NULL), BENCH_INFO(buffer_alloc_free_multi, NULL, NULL, NULL), BENCH_INFO(buffer_is_valid, create_buffers, free_buffers, NULL), BENCH_INFO(event_type, create_events, free_buffers, NULL), BENCH_INFO(event_subtype, create_buffers, free_buffers, NULL), BENCH_INFO(event_types, create_buffers, free_buffers, NULL), BENCH_INFO(event_type_multi, create_events_multi, free_buffers_multi, NULL), BENCH_INFO(event_is_valid, create_events, free_buffers, NULL), BENCH_INFO(event_free, create_events, NULL, NULL), BENCH_INFO(event_free_multi, create_events_multi, NULL, NULL), BENCH_INFO(event_free_sp, create_events_multi, NULL, NULL), BENCH_INFO_COND(event_flow_id, create_events, free_buffers, NULL, check_flow_aware), BENCH_INFO_COND(event_flow_id_set, create_events, free_buffers, NULL, check_flow_aware), }; /** * ODP buffer microbenchmark application */ int main(int argc, char *argv[]) { odph_helper_options_t helper_options; odph_thread_t worker_thread; odph_thread_common_param_t thr_common; odph_thread_param_t thr_param; int cpu; odp_shm_t shm; odp_cpumask_t cpumask, default_mask; odp_schedule_capability_t sched_capa; odp_pool_capability_t capa; odp_pool_param_t params; odp_instance_t instance; odp_init_t init_param; uint32_t buf_num; uint8_t ret; /* Let helper collect its own arguments (e.g. --odph_proc) */ argc = odph_parse_options(argc, argv); if (odph_options(&helper_options)) { ODPH_ERR("Error: reading ODP helper options failed\n"); exit(EXIT_FAILURE); } odp_init_param_init(&init_param); init_param.mem_model = helper_options.mem_model; /* Init ODP before calling anything else */ if (odp_init_global(&instance, &init_param, NULL)) { ODPH_ERR("Error: ODP global init failed\n"); exit(EXIT_FAILURE); } /* Init this thread */ if (odp_init_local(instance, ODP_THREAD_CONTROL)) { ODPH_ERR("Error: ODP local init failed\n"); exit(EXIT_FAILURE); } /* Reserve memory for args from shared mem */ shm = odp_shm_reserve("shm_args", sizeof(args_t), ODP_CACHE_LINE_SIZE, 0); if (shm == ODP_SHM_INVALID) { ODPH_ERR("Error: shared mem reserve failed\n"); exit(EXIT_FAILURE); } gbl_args = odp_shm_addr(shm); if (gbl_args == NULL) { ODPH_ERR("Error: shared mem alloc failed\n"); exit(EXIT_FAILURE); } memset(gbl_args, 0, sizeof(args_t)); /* Parse and store the application arguments */ parse_args(argc, argv, &gbl_args->appl); bench_suite_init(&gbl_args->suite); gbl_args->suite.bench = test_suite; gbl_args->suite.num_bench = ODPH_ARRAY_SIZE(test_suite); gbl_args->suite.indef_idx = gbl_args->appl.bench_idx; gbl_args->suite.rounds = gbl_args->appl.rounds; gbl_args->suite.repeat_count = TEST_REPEAT_COUNT; gbl_args->suite.measure_time = !!gbl_args->appl.time; /* Get default worker cpumask */ if (odp_cpumask_default_worker(&default_mask, 1) != 1) { ODPH_ERR("Error: unable to allocate worker thread\n"); exit(EXIT_FAILURE); } (void)odp_cpumask_to_str(&default_mask, gbl_args->cpumask_str, sizeof(gbl_args->cpumask_str)); if (odp_schedule_capability(&sched_capa)) { ODPH_ERR("Error: schedule capability failed\n"); exit(EXIT_FAILURE); } gbl_args->max_flow_id = 0; if (sched_capa.max_flow_id) { odp_schedule_config_t sched_config; odp_schedule_config_init(&sched_config); sched_config.max_flow_id = 1; if (odp_schedule_config(&sched_config)) { ODPH_ERR("Error: schedule config failed\n"); exit(EXIT_FAILURE); } gbl_args->max_flow_id = 1; } if (odp_pool_capability(&capa)) { ODPH_ERR("Error: unable to query pool capability\n"); exit(EXIT_FAILURE); } buf_num = gbl_args->appl.burst_size * TEST_REPEAT_COUNT; if (capa.buf.max_num && capa.buf.max_num < buf_num) { ODPH_ERR("Error: pool size not supported (max %" PRIu32 ")\n", capa.buf.max_num); exit(EXIT_FAILURE); } else if (gbl_args->appl.cache_size > (int)capa.buf.max_cache_size) { ODPH_ERR("Error: cache size not supported (max %" PRIu32 ")\n", capa.buf.max_cache_size); exit(EXIT_FAILURE); } gbl_args->buf_size = TEST_BUF_SIZE; if (capa.buf.max_size && capa.buf.max_size < TEST_BUF_SIZE) gbl_args->buf_size = capa.buf.max_size; gbl_args->uarea_size = TEST_UAREA_SIZE < capa.buf.max_uarea_size ? TEST_UAREA_SIZE : capa.buf.max_uarea_size; print_info(); /* Create buffer pool */ odp_pool_param_init(¶ms); params.buf.size = gbl_args->buf_size; params.buf.num = buf_num; params.buf.uarea_size = gbl_args->uarea_size; if (gbl_args->appl.cache_size >= 0) params.buf.cache_size = gbl_args->appl.cache_size; params.type = ODP_POOL_BUFFER; gbl_args->pool = odp_pool_create("microbench", ¶ms); if (gbl_args->pool == ODP_POOL_INVALID) { ODPH_ERR("Error: pool create failed\n"); exit(EXIT_FAILURE); } odp_pool_print(gbl_args->pool); memset(&worker_thread, 0, sizeof(odph_thread_t)); signal(SIGINT, sig_handler); /* Create worker thread */ cpu = odp_cpumask_first(&default_mask); odp_cpumask_zero(&cpumask); odp_cpumask_set(&cpumask, cpu); odph_thread_common_param_init(&thr_common); thr_common.instance = instance; thr_common.cpumask = &cpumask; thr_common.share_param = 1; odph_thread_param_init(&thr_param); thr_param.start = bench_run; thr_param.arg = &gbl_args->suite; thr_param.thr_type = ODP_THREAD_WORKER; odph_thread_create(&worker_thread, &thr_common, &thr_param, 1); odph_thread_join(&worker_thread, 1); ret = gbl_args->suite.retval; if (odp_pool_destroy(gbl_args->pool)) { ODPH_ERR("Error: pool destroy\n"); exit(EXIT_FAILURE); } if (odp_shm_free(shm)) { ODPH_ERR("Error: shm free\n"); exit(EXIT_FAILURE); } if (odp_term_local()) { ODPH_ERR("Error: term local\n"); exit(EXIT_FAILURE); } if (odp_term_global(instance)) { ODPH_ERR("Error: term global\n"); exit(EXIT_FAILURE); } return ret; }