[RFC PATCH 1/3] app/test-mempool-perf: skeleton of new test app

Bruce Richardson <[email protected]>
Newsgroups org.dpdk.dev
Message-ID <[email protected]>
Start a new app for running some performance tests of mempool drivers
with different parameters. If run with no parameters, prompt for the
options to use.

Signed-off-by: Bruce Richardson <[email protected]>
---
 app/meson.build                   |   1 +
 app/test-mempool-perf/main.c      | 330 ++++++++++++++++++++++++++++++
 app/test-mempool-perf/meson.build |   8 +
 doc/guides/tools/index.rst        |   1 +
 doc/guides/tools/mempoolperf.rst  | 139 +++++++++++++
 5 files changed, 479 insertions(+)
 create mode 100644 app/test-mempool-perf/main.c
 create mode 100644 app/test-mempool-perf/meson.build
 create mode 100644 doc/guides/tools/mempoolperf.rst

diff --git a/app/meson.build b/app/meson.build
index 1798db3ae4..81f36f497b 100644
--- a/app/meson.build
+++ b/app/meson.build
@@ -31,6 +31,7 @@ apps = [
         'test-pipeline',
         'test-pmd',
         'test-regex',
+        'test-mempool-perf',
         'test-sad',
         'test-security-perf',
 ]
diff --git a/app/test-mempool-perf/main.c b/app/test-mempool-perf/main.c
new file mode 100644
index 0000000000..877abc2170
--- /dev/null
+++ b/app/test-mempool-perf/main.c
@@ -0,0 +1,330 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2024 Intel Corporation
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <inttypes.h>
+
+#include <rte_argparse.h>
+#include <rte_eal.h>
+#include <rte_lcore.h>
+#include <rte_mempool.h>
+#include <rte_string_fns.h>
+
+#define DEFAULT_CACHE_SIZE          512
+#define DEFAULT_RAND_FACTOR         8
+#define DEFAULT_BURST_SIZE          32
+#define DEFAULT_NB_BUFS_PER_LCORE   1024
+
+struct test_config {
+	char     mempool_type[RTE_MEMPOOL_NAMESIZE];
+	uint32_t nb_bufs;
+	uint32_t cache_size;
+	uint32_t nb_threads;
+	uint32_t rand_factor;
+	uint32_t burst_size;
+	bool     access_on_alloc;
+};
+
+static struct test_config cfg = {
+	.mempool_type   = "",
+	.nb_bufs        = 0,  /* 0 means: compute from lcore count */
+	.cache_size     = DEFAULT_CACHE_SIZE,
+	.nb_threads     = 0,  /* 0 means: use all worker lcores */
+	.rand_factor    = DEFAULT_RAND_FACTOR,
+	.burst_size     = DEFAULT_BURST_SIZE,
+	.access_on_alloc = true,
+};
+
+static void
+apply_defaults(void)
+{
+	unsigned int nb_workers;
+
+	nb_workers = rte_lcore_count() > 1 ? rte_lcore_count() - 1 : 1;
+
+	if (cfg.nb_bufs == 0)
+		cfg.nb_bufs = DEFAULT_NB_BUFS_PER_LCORE * rte_lcore_count();
+	if (cfg.nb_threads == 0)
+		cfg.nb_threads = nb_workers;
+}
+
+static bool
+is_valid_mempool_type(const char *name)
+{
+	uint32_t i;
+
+	for (i = 0; i < rte_mempool_ops_table.num_ops; i++)
+		if (strcmp(name, rte_mempool_ops_table.ops[i].name) == 0)
+			return true;
+	return false;
+}
+
+static void
+list_mempool_types(void)
+{
+	uint32_t i;
+
+	printf("Available mempool types:\n");
+	for (i = 0; i < rte_mempool_ops_table.num_ops; i++)
+		printf("  [%u] %s\n", i, rte_mempool_ops_table.ops[i].name);
+}
+
+static void
+print_config(void)
+{
+	printf("\n=== test-mempool-perf configuration ===\n");
+	printf("  Mempool type     : %s\n", cfg.mempool_type);
+	printf("  Num buffers      : %" PRIu32 "\n", cfg.nb_bufs);
+	printf("  Cache size       : %" PRIu32 "\n", cfg.cache_size);
+	printf("  Thread count     : %" PRIu32 "\n", cfg.nb_threads);
+	printf("  Randomness factor: %" PRIu32 "\n", cfg.rand_factor);
+	printf("  Burst size       : %" PRIu32 "\n", cfg.burst_size);
+	printf("  Access on alloc  : %s\n", cfg.access_on_alloc ? "yes" : "no");
+	printf("========================================\n\n");
+}
+
+static void
+print_reproduce_cmd(void)
+{
+	printf("Reproduce using parameters:"
+		" -M %s -n %" PRIu32 " -c %" PRIu32 " -t %" PRIu32 " -r %" PRIu32 " -b %" PRIu32 " %s\n\n",
+			cfg.mempool_type, cfg.nb_bufs, cfg.cache_size, cfg.nb_threads, cfg.rand_factor,
+			cfg.burst_size, cfg.access_on_alloc ? "-A" : "-N");
+}
+
+static void
+trim_newline(char *s)
+{
+	size_t len = strlen(s);
+
+	if (len > 0 && s[len - 1] == '\n')
+		s[len - 1] = '\0';
+}
+
+static int
+prompt_uint32(const char *prompt, uint32_t *val)
+{
+	char buf[64];
+	char *end;
+	unsigned long v;
+
+	fputs(prompt, stdout);
+	fflush(stdout);
+	if (fgets(buf, sizeof(buf), stdin) == NULL)
+		return -1;
+	trim_newline(buf);
+	if (buf[0] == '\0')
+		return 0;  /* keep default */
+
+	v = strtoul(buf, &end, 0);
+	if (*end != '\0') {
+		fprintf(stderr, "Invalid number: %s\n", buf);
+		return -1;
+	}
+	*val = (uint32_t)v;
+	return 1;
+}
+
+static int
+run_interactive_mode(void)
+{
+	char prompt[128];
+	char buf[RTE_MEMPOOL_NAMESIZE];
+
+	printf("\n=== test-mempool-perf interactive setup ===\n");
+	printf("Press Enter to accept the default value.\n\n");
+
+	list_mempool_types();
+	printf("\n");
+
+	/* Mempool type is required; loop until a valid name is entered */
+	for (;;) {
+		printf("Mempool type (required): ");
+		fflush(stdout);
+		if (fgets(buf, sizeof(buf), stdin) == NULL)
+			return -1;
+		trim_newline(buf);
+		if (buf[0] == '\0') {
+			printf("  A mempool type is required; please enter one of the names above.\n");
+			continue;
+		}
+		if (is_valid_mempool_type(buf))
+			break;
+		printf("  Unknown mempool type '%s'; please enter one of the names above.\n", buf);
+	}
+	rte_strscpy(cfg.mempool_type, buf, sizeof(cfg.mempool_type));
+
+	/* Compute numeric defaults now that EAL is initialised */
+	apply_defaults();
+
+	/* Number of buffers */
+	snprintf(prompt, sizeof(prompt),
+		 "Number of buffers [%" PRIu32 "]: ", cfg.nb_bufs);
+	if (prompt_uint32(prompt, &cfg.nb_bufs) < 0)
+		return -1;
+
+	/* Cache size */
+	snprintf(prompt, sizeof(prompt),
+		 "Cache size [%" PRIu32 "]: ", cfg.cache_size);
+	if (prompt_uint32(prompt, &cfg.cache_size) < 0)
+		return -1;
+
+	/* Thread count */
+	snprintf(prompt, sizeof(prompt),
+		 "Thread count [%" PRIu32 "]: ", cfg.nb_threads);
+	if (prompt_uint32(prompt, &cfg.nb_threads) < 0)
+		return -1;
+
+	/* Randomness factor */
+	snprintf(prompt, sizeof(prompt),
+		 "Randomness factor [%" PRIu32 "]: ", cfg.rand_factor);
+	if (prompt_uint32(prompt, &cfg.rand_factor) < 0)
+		return -1;
+
+	/* Burst size */
+	snprintf(prompt, sizeof(prompt),
+		 "Burst size [%" PRIu32 "]: ", cfg.burst_size);
+	if (prompt_uint32(prompt, &cfg.burst_size) < 0)
+		return -1;
+	/* Access on allocation */
+	printf("Access buffers on allocation [%s]: ",
+	       cfg.access_on_alloc ? "yes" : "no");
+	fflush(stdout);
+	{
+		char yn[16];
+
+		if (fgets(yn, sizeof(yn), stdin) == NULL)
+			return -1;
+		trim_newline(yn);
+		if (yn[0] == 'y' || yn[0] == 'Y')
+			cfg.access_on_alloc = true;
+		else if (yn[0] == 'n' || yn[0] == 'N')
+			cfg.access_on_alloc = false;
+		/* else keep default */
+	}
+
+	return 0;
+}
+
+static bool summary_only;
+
+/* Used only in non-interactive mode to receive the --mempool-type string */
+static const char *mempool_type_arg;
+
+static int
+parse_args(int argc, char **argv)
+{
+	static struct rte_argparse obj = {
+		.prog_name = "test-mempool-perf",
+		.usage = "[EAL options] -- [options]",
+		.descriptor = "Mempool performance tester",
+		.exit_on_error = true,
+		.args = {
+			{ "--mempool-type", "-M",
+			  "Mempool driver to test (required in non-interactive mode)",
+			  (void *)&mempool_type_arg, NULL,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_STR,
+			},
+			{ "--nb-bufs", "-n",
+			  "Number of buffers in the pool (default: 1024 * lcore count)",
+			  (void *)&cfg.nb_bufs, NULL,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
+			},
+			{ "--cache-size", "-c",
+			  "Per-lcore object cache size (default: 512)",
+			  (void *)&cfg.cache_size, NULL,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
+			},
+			{ "--nb-threads", "-t",
+			  "Number of worker threads to use (default: all worker lcores)",
+			  (void *)&cfg.nb_threads, NULL,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
+			},
+			{ "--rand-factor", "-r",
+			  "Randomness factor for alloc/free burst sizes (default: 8)",
+			  (void *)&cfg.rand_factor, NULL,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
+			},
+			{ "--burst-size", "-b",
+			  "Number of objects per alloc/free burst (default: 32)",
+			  (void *)&cfg.burst_size, NULL,
+			  RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_U32,
+			},
+			{ "--access-on-alloc", "-A",
+			  "Enable touching buffer memory on allocation (default: enabled)",
+			  (void *)&cfg.access_on_alloc, (void *)true,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+			},
+			{ "--no-access-on-alloc", "-N",
+			  "Disable touching buffer memory on allocation",
+			  (void *)&cfg.access_on_alloc, (void *)false,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+			},
+			{ "--summary", "-s",
+			  "Print only the aggregate total, not per-lcore results",
+			  (void *)&summary_only, (void *)true,
+			  RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+			},
+			ARGPARSE_ARG_END(),
+		},
+	};
+	int ret;
+
+	ret = rte_argparse_parse(&obj, argc, argv);
+	if (ret < 0)
+		return ret;
+
+	if (mempool_type_arg != NULL)
+		rte_strscpy(cfg.mempool_type, mempool_type_arg,
+			    sizeof(cfg.mempool_type));
+
+	return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+	int ret;
+
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
+	argc -= ret;
+	argv += ret;
+
+	if (argc == 1) {
+		/* No app-specific arguments: enter interactive configuration */
+		ret = run_interactive_mode();
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "Interactive configuration failed\n");
+		print_reproduce_cmd();
+	} else {
+		ret = parse_args(argc, argv);
+		if (ret < 0)
+			rte_exit(EXIT_FAILURE, "Invalid application arguments\n");
+
+		if (cfg.mempool_type[0] == '\0') {
+			fprintf(stderr,
+				"Error: --mempool-type is required in non-interactive mode\n");
+			list_mempool_types();
+			rte_exit(EXIT_FAILURE, "Mempool type not specified\n");
+		}
+		if (!is_valid_mempool_type(cfg.mempool_type)) {
+			fprintf(stderr, "Error: unknown mempool type '%s'\n",
+				cfg.mempool_type);
+			list_mempool_types();
+			rte_exit(EXIT_FAILURE, "Invalid mempool type\n");
+		}
+
+		apply_defaults();
+	}
+
+	print_config();
+
+	rte_eal_cleanup();
+	return 0;
+}
diff --git a/app/test-mempool-perf/meson.build b/app/test-mempool-perf/meson.build
new file mode 100644
index 0000000000..2d61ac62e9
--- /dev/null
+++ b/app/test-mempool-perf/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 Intel Corporation
+
+deps += ['mempool', 'argparse']
+
+sources = files(
+        'main.c',
+)
diff --git a/doc/guides/tools/index.rst b/doc/guides/tools/index.rst
index 13f75a5bc6..cb71ba7b55 100644
--- a/doc/guides/tools/index.rst
+++ b/doc/guides/tools/index.rst
@@ -17,6 +17,7 @@ DPDK Tools User Guides
     telemetrywatcher
     dmaperf
     flow-perf
+    mempoolperf
     securityperf
     testbbdev
     cryptoperf
diff --git a/doc/guides/tools/mempoolperf.rst b/doc/guides/tools/mempoolperf.rst
new file mode 100644
index 0000000000..e0ad859a29
--- /dev/null
+++ b/doc/guides/tools/mempoolperf.rst
@@ -0,0 +1,139 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2024 Intel Corporation
+
+dpdk-test-mempool-perf Application
+====================================
+
+The ``dpdk-test-mempool-perf`` tool measures the alloc/free throughput of DPDK mempool implementations.
+Worker threads repeatedly allocate and free objects in configurable burst sizes following a randomised pattern,
+exercising the pool under varying levels of occupancy.
+Any mempool driver registered with the DPDK mempool ops table can be tested.
+
+
+Running the Application
+-----------------------
+
+.. code-block:: console
+
+   dpdk-test-mempool-perf [EAL options] -- [application options]
+
+See the *DPDK Getting Started Guide* for a description of EAL options.
+
+The application operates in two modes depending on whether application-specific options are supplied after ``--``:
+
+interactive
+   Invoked with no options or only EAL options (nothing after ``--``, or ``--`` omitted).
+   The tool prompts for each parameter in turn; pressing Enter accepts the displayed default.
+   After configuration, a command line is printed that reproduces the same settings non-interactively.
+
+non-interactive
+   All configuration is supplied on the command line.
+   ``--mempool-type`` is required; all other parameters are optional.
+
+
+Application Options
+~~~~~~~~~~~~~~~~~~~
+
+``--mempool-type <name>`` / ``-M <name>``
+   Name of the mempool driver to test.
+   Required in non-interactive mode.
+   To list the drivers available on the current system,
+   run the application in interactive mode; the available names are printed at startup.
+   Common names include ``ring_mp_mc`` and ``stack``.
+
+``--nb-bufs <n>`` / ``-n <n>``
+   Total number of objects in the pool.
+   Default: 1024 multiplied by the total lcore count.
+   The pool must be large enough that it is not exhausted when all workers hold their maximum simultaneous in-flight objects,
+   which is ``(rand-factor / 2) * burst-size`` objects per worker.
+
+``--cache-size <n>`` / ``-c <n>``
+   Per-lcore object cache size, in objects.
+   Default: 512.
+   A larger cache reduces contention on the central pool at the cost of higher per-core memory usage.
+   Set to 0 to disable the per-lcore cache and measure underlying data structure throughput.
+
+``--nb-threads <n>`` / ``-t <n>``
+   Number of worker lcores to launch.
+   Default: all available worker lcores (total lcores minus the main lcore).
+
+``--rand-factor <n>`` / ``-r <n>``
+   Controls the width of the randomised allocation pattern.
+   Default: 8.
+   The value is rounded down to the nearest even number (minimum 2).
+   Half of the resulting slots perform bulk allocations and half perform bulk frees;
+   the order is reshuffled randomly at regular intervals.
+   A larger value means workers hold more in-flight objects on average
+   and vary their occupancy over a wider range,
+   exercising the pool under a more realistic mix of pressure levels.
+
+``--burst-size <n>`` / ``-b <n>``
+   Number of objects per alloc or free call.
+   Default: 32.
+   Higher burst sizes amortise per-call overhead
+   and can reveal differences between pool implementations that batch internal operations.
+
+``--access-on-alloc`` / ``-A``
+   Touch every cache line of each allocated object immediately after allocation (default behaviour).
+   This models workloads that initialise or write packet data after allocation,
+   ensuring that the measured throughput reflects both pool overhead and memory bandwidth pressure.
+
+``--no-access-on-alloc`` / ``-N``
+   Skip the memory-access step after allocation.
+   Use this to isolate pure pool ring or lock overhead from memory bandwidth effects.
+
+``--summary`` / ``-s``
+   Print only the aggregate total in the results, suppressing the per-worker-lcore breakdown.
+   Useful when scripting comparisons across pool types or configurations.
+
+
+Interactive Mode
+----------------
+
+Running the tool with only EAL options enters interactive mode::
+
+   dpdk-test-mempool-perf [EAL options]
+
+The application lists all available mempool drivers then prompts for each parameter.
+Pressing Enter at any prompt keeps the displayed default value.
+``--mempool-type`` is the only mandatory entry.
+
+After configuration the tool prints an equivalent non-interactive command::
+
+   Reproduce using parameters: -M ring_mp_mc -n 4096 -c 512 -t 3 -r 8 -b 32 -A
+
+Append this output after the EAL options on subsequent runs to reproduce the exact same configuration without prompting.
+
+
+Examples
+--------
+
+Run interactively, letting the tool prompt for all settings:
+
+.. code-block:: console
+
+   dpdk-test-mempool-perf -l 0-3
+
+Run non-interactively with four worker threads:
+
+.. code-block:: console
+
+   dpdk-test-mempool-perf -l 0-4 -- -M ring_mp_mc -t 4 -n 20480
+
+Disable the per-lcore cache to measure raw ring throughput:
+
+.. code-block:: console
+
+   dpdk-test-mempool-perf -l 0-1 -- -M ring_mp_mc -c 0
+
+Measure without memory access to isolate pool overhead from bandwidth:
+
+.. code-block:: console
+
+   dpdk-test-mempool-perf -l 0-4 -- -M ring_mp_mc -N
+
+Print only the aggregate total, suitable for scripted comparisons:
+
+.. code-block:: console
+
+   dpdk-test-mempool-perf -l 0-4 -- -M ring_mp_mc -s
-- 
2.53.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.