/* 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; } } #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; \ __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_WORKER(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 */ signal_workers_to_finish(data); RTE_LCORE_FOREACH_WORKER(id) rte_eal_wait_lcore(id); measure_perf(str, data); } 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);