From 16a277a24c9ffdda0daa6841e0cebc9cf0a7c4f1 Mon Sep 17 00:00:00 2001 From: Jerin Jacob Date: Thu, 23 Apr 2020 00:33:44 +0530 Subject: [PATCH] test/trace: add performance test cases This test case shall be used to measure the trace overhead. Example command to run the performance test case. echo "trace_perf_autotest" | ./build/app/test/dpdk-test -c 0x3 --trace=.* Signed-off-by: Jerin Jacob Acked-by: David Marchand --- app/test/Makefile | 1 + app/test/meson.build | 2 + app/test/test_trace_perf.c | 183 +++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 app/test/test_trace_perf.c diff --git a/app/test/Makefile b/app/test/Makefile index 0b5807d7e0..4582eca6cf 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -155,6 +155,7 @@ SRCS-y += test_version.c SRCS-y += test_func_reentrancy.c SRCS-y += test_trace.c SRCS-y += test_trace_register.c +SRCS-y += test_trace_perf.c SRCS-y += test_service_cores.c ifeq ($(CONFIG_RTE_LIBRTE_PMD_RING),y) diff --git a/app/test/meson.build b/app/test/meson.build index a0b2c1fa81..a9a8eabcd9 100644 --- a/app/test/meson.build +++ b/app/test/meson.build @@ -129,6 +129,7 @@ test_sources = files('commands.c', 'test_ticketlock.c', 'test_trace.c', 'test_trace_register.c', + 'test_trace_perf.c', 'test_version.c', 'virtual_pmd.c' ) @@ -283,6 +284,7 @@ perf_test_names = [ 'rand_perf_autotest', 'hash_readwrite_perf_autotest', 'hash_readwrite_lf_perf_autotest', + 'trace_perf_autotest', ] driver_test_names = [ diff --git a/app/test/test_trace_perf.c b/app/test/test_trace_perf.c new file mode 100644 index 0000000000..50c7381b77 --- /dev/null +++ b/app/test/test_trace_perf.c @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2020 Marvell International Ltd. + */ + +#include +#include +#include +#include +#include +#include + +#include "test.h" +#include "test_trace.h" + +struct test_data; + +struct lcore_data { + volatile bool done; + volatile bool started; + uint64_t total_cycles; + uint64_t total_calls; +} __rte_cache_aligned; + +struct test_data { + unsigned int nb_workers; + struct lcore_data ldata[]; +} __rte_cache_aligned; + +#define STEP 100 +#define CENT_OPS(OP) do { \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +OP; OP; OP; OP; OP; OP; OP; OP; OP; OP; \ +} while (0) + +static void +measure_perf(const char *str, struct test_data *data) +{ + uint64_t hz = rte_get_timer_hz(); + uint64_t total_cycles = 0; + uint64_t total_calls = 0; + double cycles, ns; + unsigned int workers; + + for (workers = 0; workers < data->nb_workers; workers++) { + total_cycles += data->ldata[workers].total_cycles; + total_calls += data->ldata[workers].total_calls; + } + + cycles = total_calls ? (double)total_cycles / (double)total_calls : 0; + cycles /= STEP; + cycles /= 100; /* CENT_OPS */ + + ns = (cycles / (double)hz) * 1E9; + printf("%16s: cycles=%f ns=%f\n", str, cycles, ns); +} + +static void +wait_till_workers_are_ready(struct test_data *data) +{ + unsigned int workers; + + for (workers = 0; workers < data->nb_workers; workers++) + while (!data->ldata[workers].started) + rte_pause(); +} + +static void +signal_workers_to_finish(struct test_data *data) +{ + unsigned int workers; + + for (workers = 0; workers < data->nb_workers; workers++) { + data->ldata[workers].done = 1; + rte_smp_wmb(); + } +} + +#define WORKER_DEFINE(func) \ +static void __rte_noinline \ +__worker_##func(struct lcore_data *ldata) \ +{ \ + uint64_t start; \ + int i; \ + while (!ldata->done) { \ + start = rte_get_timer_cycles(); \ + for (i = 0; i < STEP; i++) \ + CENT_OPS(func); \ + ldata->total_cycles += rte_get_timer_cycles() - start; \ + ldata->total_calls++; \ + } \ +} \ +static int \ +worker_fn_##func(void *arg) \ +{ \ + struct lcore_data *ldata = arg; \ + ldata->started = 1; \ + rte_smp_wmb(); \ + __worker_##func(ldata); \ + return 0; \ +} + + +/* Test to find trace overhead */ +#define GENERIC_VOID rte_eal_trace_generic_void() +#define GENERIC_U64 rte_eal_trace_generic_u64(0x120000) +#define GENERIC_INT rte_eal_trace_generic_int(-34) +#define GENERIC_FLOAT rte_eal_trace_generic_float(3.3f) +#define GENERIC_DOUBLE rte_eal_trace_generic_double(3.66666) +#define GENERIC_STR rte_eal_trace_generic_str("hello world") +#define VOID_FP app_dpdk_test_fp() + +WORKER_DEFINE(GENERIC_VOID) +WORKER_DEFINE(GENERIC_U64) +WORKER_DEFINE(GENERIC_INT) +WORKER_DEFINE(GENERIC_FLOAT) +WORKER_DEFINE(GENERIC_DOUBLE) +WORKER_DEFINE(GENERIC_STR) +WORKER_DEFINE(VOID_FP) + +static void +run_test(const char *str, lcore_function_t f, struct test_data *data, size_t sz) +{ + unsigned int id, worker = 0; + + memset(data, 0, sz); + data->nb_workers = rte_lcore_count() - 1; + RTE_LCORE_FOREACH_SLAVE(id) + rte_eal_remote_launch(f, &data->ldata[worker++], id); + + wait_till_workers_are_ready(data); + rte_delay_ms(100); /* Wait for some time to accumulate the stats */ + measure_perf(str, data); + signal_workers_to_finish(data); + + RTE_LCORE_FOREACH_SLAVE(id) + rte_eal_wait_lcore(id); +} + +static int +test_trace_perf(void) +{ + unsigned int nb_cores, nb_workers; + struct test_data *data; + size_t sz; + + nb_cores = rte_lcore_count(); + nb_workers = nb_cores - 1; + if (nb_cores < 2) { + printf("Need minimum two cores for testing\n"); + return TEST_SKIPPED; + } + + printf("Timer running at %5.2fMHz\n", rte_get_timer_hz()/1E6); + sz = sizeof(struct test_data); + sz += nb_workers * sizeof(struct lcore_data); + + data = rte_zmalloc(NULL, sz, RTE_CACHE_LINE_SIZE); + if (data == NULL) { + printf("Failed to allocate memory\n"); + return TEST_FAILED; + } + + run_test("void", worker_fn_GENERIC_VOID, data, sz); + run_test("u64", worker_fn_GENERIC_U64, data, sz); + run_test("int", worker_fn_GENERIC_INT, data, sz); + run_test("float", worker_fn_GENERIC_FLOAT, data, sz); + run_test("double", worker_fn_GENERIC_DOUBLE, data, sz); + run_test("string", worker_fn_GENERIC_STR, data, sz); + run_test("void_fp", worker_fn_VOID_FP, data, sz); + + rte_free(data); + return TEST_SUCCESS; +} + +REGISTER_TEST_COMMAND(trace_perf_autotest, test_trace_perf); -- 2.20.1