From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 170BF42CA9; Tue, 13 Jun 2023 14:55:10 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id E359B40A8A; Tue, 13 Jun 2023 14:55:09 +0200 (CEST) Received: from szxga01-in.huawei.com (szxga01-in.huawei.com [45.249.212.187]) by mails.dpdk.org (Postfix) with ESMTP id EAFF040698 for ; Tue, 13 Jun 2023 14:55:07 +0200 (CEST) Received: from dggpeml500011.china.huawei.com (unknown [172.30.72.57]) by szxga01-in.huawei.com (SkyGuard) with ESMTP id 4QgT3l6kfsztQVv; Tue, 13 Jun 2023 20:52:35 +0800 (CST) Received: from [10.67.101.191] (10.67.101.191) by dggpeml500011.china.huawei.com (7.185.36.84) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.23; Tue, 13 Jun 2023 20:55:04 +0800 Message-ID: <8b8949c1-739a-59f9-fd7d-a0e232c30252@huawei.com> Date: Tue, 13 Jun 2023 20:55:04 +0800 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Thunderbird/102.11.0 Subject: Re: [PATCH v6] app/dma-perf: introduce dma-perf application Content-Language: en-US To: Cheng Jiang , , , , , , References: <20230420072215.19069-1-cheng1.jiang@intel.com> <20230613043140.18101-1-cheng1.jiang@intel.com> From: huangdengdui CC: , , , , , In-Reply-To: <20230613043140.18101-1-cheng1.jiang@intel.com> Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit X-Originating-IP: [10.67.101.191] X-ClientProxiedBy: dggems706-chm.china.huawei.com (10.3.19.183) To dggpeml500011.china.huawei.com (7.185.36.84) X-CFilter-Loop: Reflected X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Hi Cheng, Few comments inline. Please check. Thanks, Dengdui On 2023/6/13 12:31, Cheng Jiang wrote: > There are many high-performance DMA devices supported in DPDK now, and > these DMA devices can also be integrated into other modules of DPDK as > accelerators, such as Vhost. Before integrating DMA into applications, > developers need to know the performance of these DMA devices in various > scenarios and the performance of CPUs in the same scenario, such as > different buffer lengths. Only in this way can we know the target > performance of the application accelerated by using them. This patch > introduces a high-performance testing tool, which supports comparing the > performance of CPU and DMA in different scenarios automatically with a > pre-set config file. Memory Copy performance test are supported for now. > > Signed-off-by: Cheng Jiang > Signed-off-by: Jiayu Hu > Signed-off-by: Yuan Wang > Acked-by: Morten Brørup > Acked-by: Chenbo Xia > --- > v6: > improved code based on Anoob's comments; > fixed some code structure issues; > v5: > fixed some LONG_LINE warnings; > v4: > fixed inaccuracy of the memory footprint display; > v3: > fixed some typos; > v2: > added lcore/dmadev designation; > added error case process; > removed worker_threads parameter from config.ini; > improved the logs; > improved config file; > > app/meson.build | 1 + > app/test-dma-perf/benchmark.c | 477 ++++++++++++++++++++++++++++ > app/test-dma-perf/config.ini | 59 ++++ > app/test-dma-perf/main.c | 569 ++++++++++++++++++++++++++++++++++ > app/test-dma-perf/main.h | 69 +++++ > app/test-dma-perf/meson.build | 17 + > 6 files changed, 1192 insertions(+) > create mode 100644 app/test-dma-perf/benchmark.c > create mode 100644 app/test-dma-perf/config.ini > create mode 100644 app/test-dma-perf/main.c > create mode 100644 app/test-dma-perf/main.h > create mode 100644 app/test-dma-perf/meson.build > > diff --git a/app/meson.build b/app/meson.build > index 74d2420f67..4fc1a83eba 100644 > --- a/app/meson.build > +++ b/app/meson.build > @@ -19,6 +19,7 @@ apps = [ > 'test-cmdline', > 'test-compress-perf', > 'test-crypto-perf', > + 'test-dma-perf', > 'test-eventdev', > 'test-fib', > 'test-flow-perf', > diff --git a/app/test-dma-perf/benchmark.c b/app/test-dma-perf/benchmark.c > new file mode 100644 > index 0000000000..bc1ca82297 > --- /dev/null > +++ b/app/test-dma-perf/benchmark.c > @@ -0,0 +1,477 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2023 Intel Corporation > + */ > + > +static inline int > +__rte_format_printf(3, 4) > +print_err(const char *func, int lineno, const char *format, ...) > +{ > + va_list ap; > + int ret; > + > + ret = fprintf(stderr, "In %s:%d - ", func, lineno); > + va_start(ap, format); > + ret += vfprintf(stderr, format, ap); > + va_end(ap); > + > + return ret; > +} > + > +static inline void > +calc_result(uint32_t buf_size, uint32_t nr_buf, uint16_t nb_workers, uint16_t test_secs, > + uint32_t total_cnt, float *memory, uint32_t *ave_cycle, > + float *bandwidth, float *mops) > +{ > + *memory = (float)(buf_size * (nr_buf / nb_workers) * 2) / (1024 * 1024); > + *ave_cycle = test_secs * rte_get_timer_hz() / total_cnt; > + *bandwidth = (buf_size * 8 * (rte_get_timer_hz() / (float)*ave_cycle)) / 1000000000; > + *mops = (float)rte_get_timer_hz() / *ave_cycle / 1000000; The value of ave_cycle may be 0. *mops = (float)(total_cnt / test_secs) / 1000000; ? > +} > + > +static void > +output_result(uint8_t scenario_id, uint32_t lcore_id, char *dma_name, uint64_t ave_cycle, > + uint32_t buf_size, uint32_t nr_buf, float memory, > + float bandwidth, float mops, bool is_dma) > +{ > + if (is_dma) > + printf("lcore %u, DMA %s:\n", lcore_id, dma_name); > + else > + printf("lcore %u\n", lcore_id); > + > + printf("average cycles/op: %" PRIu64 ", buffer size: %u, nr_buf: %u, memory: %.2lfMB, frequency: %" PRIu64 ".\n", > + ave_cycle, buf_size, nr_buf, memory, rte_get_timer_hz()); > + printf("Average bandwidth: %.3lfGbps, MOps: %.3lf\n", bandwidth, mops); > + > + if (is_dma) > + snprintf(output_str[lcore_id], MAX_OUTPUT_STR_LEN, CSV_LINE_DMA_FMT, > + scenario_id, lcore_id, dma_name, buf_size, > + nr_buf, memory, ave_cycle, bandwidth, mops); > + else > + snprintf(output_str[lcore_id], MAX_OUTPUT_STR_LEN, CSV_LINE_CPU_FMT, > + scenario_id, lcore_id, buf_size, > + nr_buf, memory, ave_cycle, bandwidth, mops); > +} > + > +static inline void > +cache_flush_buf(__maybe_unused struct rte_mbuf **array, > + __maybe_unused uint32_t buf_size, > + __maybe_unused uint32_t nr_buf) > +{ > +#ifdef RTE_ARCH_X86_64 > + char *data; > + struct rte_mbuf **srcs = array; > + uint32_t i, offset; > + > + for (i = 0; i < nr_buf; i++) { > + data = rte_pktmbuf_mtod(srcs[i], char *); > + for (offset = 0; offset < buf_size; offset += 64) > + __builtin_ia32_clflush(data + offset); > + } > +#endif > +} > + > + > +/* Parse the argument given in the command line of the application */ > +static int > +append_eal_args(int argc, char **argv, const char *eal_args, char **new_argv) > +{ > + int i; > + char *tokens[MAX_EAL_PARAM_NB]; > + char args[MAX_EAL_PARAM_LEN] = {0}; > + int token_nb, new_argc = 0; > + > + for (i = 0; i < argc; i++) { > + if ((strcmp(argv[i], CMDLINE_CONFIG_ARG) == 0) || > + (strcmp(argv[i], CMDLINE_RESULT_ARG) == 0)) { > + i++; > + continue; > + } > + strlcpy(new_argv[new_argc], argv[i], sizeof(new_argv[new_argc])); The type_of argv[new_argc] is *char. Cannot use sizeof(). strcpy(new_argv[new_argc], argv[i]); or strlcpy(new_argv[new_argc], argv[i], MAX_EAL_PARAM_LEN); ? > + new_argc++; > + } > + > + if (eal_args) { > + strlcpy(args, eal_args, sizeof(args)); > + token_nb = rte_strsplit(args, strlen(args), > + tokens, MAX_EAL_PARAM_NB, ' '); > + for (i = 0; i < token_nb; i++) > + strcpy(new_argv[new_argc++], tokens[i]); > + } > + > + return new_argc; > +} > + > +int > +main(int argc, char *argv[]) > +{ > + int ret; > + uint16_t case_nb; > + uint32_t i, nb_lcores; > + pid_t cpid, wpid; > + int wstatus; > + char args[MAX_EAL_PARAM_NB][MAX_EAL_PARAM_LEN]; > + char *pargs[MAX_EAL_PARAM_NB]; > + char *cfg_path_ptr = NULL; > + char *rst_path_ptr = NULL; > + char rst_path[PATH_MAX]; > + int new_argc; > + bool is_first_case = true; > + > + memset(args, 0, sizeof(args)); > + > + for (i = 0; i < RTE_DIM(pargs); i++) > + pargs[i] = args[i]; > + > + for (i = 0; i < (uint32_t)argc; i++) { > + if (strncmp(argv[i], CMDLINE_CONFIG_ARG, MAX_LONG_OPT_SZ) == 0) > + cfg_path_ptr = argv[i + 1]; > + if (strncmp(argv[i], CMDLINE_RESULT_ARG, MAX_LONG_OPT_SZ) == 0) > + rst_path_ptr = argv[i + 1]; > + } > + if (cfg_path_ptr == NULL) { > + printf("Config file not assigned.\n"); > + return -1; > + } > + if (rst_path_ptr == NULL) { > + strlcpy(rst_path, cfg_path_ptr, PATH_MAX); > + strcat(strtok(basename(rst_path), "."), "_result.csv"); > + rst_path_ptr = rst_path; > + } > + > + case_nb = load_configs(cfg_path_ptr); > + fd = fopen(rst_path_ptr, "w"); > + if (fd == NULL) { > + printf("Open output CSV file error.\n"); > + return -1; > + } > + fclose(fd); > + > + for (i = 0; i < case_nb; i++) { > + if (test_cases[i].test_type == TEST_TYPE_NONE) { > + printf("No test type in test case %d.\n\n", i + 1); > + continue; > + } > + if (!test_cases[i].is_valid) { > + printf("Invalid test case %d.\n\n", i + 1); > + continue; > + } > + > + cpid = fork(); > + if (cpid < 0) { > + printf("Fork case %d failed.\n", i + 1); > + exit(EXIT_FAILURE); > + } else if (cpid == 0) { > + printf("\nRunning case %u\n\n", i + 1); > + > + new_argc = append_eal_args(argc, argv, test_cases[i].eal_args, pargs); > + ret = rte_eal_init(new_argc, pargs); > + if (ret < 0) > + rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n"); > + > + /* Check lcores. */ > + nb_lcores = rte_lcore_count(); > + if (nb_lcores < 2) > + rte_exit(EXIT_FAILURE, > + "There should be at least 2 worker lcores.\n"); > + > + fd = fopen(rst_path_ptr, "a"); > + if (!fd) { > + printf("Open output CSV file error.\n"); > + return 0; > + } > + > + if (is_first_case) { > + output_env_info(); > + is_first_case = false; > + } > + run_test(i + 1, &test_cases[i]); > + > + /* clean up the EAL */ > + rte_eal_cleanup(); > + > + fclose(fd); > + > + printf("\nCase %u completed.\n\n", i + 1); > + > + exit(EXIT_SUCCESS); > + } else { > + wpid = waitpid(cpid, &wstatus, 0); > + if (wpid == -1) { > + printf("waitpid error.\n"); > + exit(EXIT_FAILURE); > + } > + > + if (WIFEXITED(wstatus)) > + printf("Case process exited. status %d\n\n", > + WEXITSTATUS(wstatus)); > + else if (WIFSIGNALED(wstatus)) > + printf("Case process killed by signal %d\n\n", > + WTERMSIG(wstatus)); > + else if (WIFSTOPPED(wstatus)) > + printf("Case process stopped by signal %d\n\n", > + WSTOPSIG(wstatus)); > + else if (WIFCONTINUED(wstatus)) > + printf("Case process continued.\n\n"); > + else > + printf("Case process unknown terminated.\n\n"); > + } > + } > + > + printf("Bye...\n"); > + return 0; > +}