From: Bruce Richardson <bruce.richardson@intel.com>
To: dev@dpdk.org
Cc: techboard@dpdk.org, Bruce Richardson <bruce.richardson@intel.com>
Subject: [RFC PATCH 1/3] app/test-mempool-perf: skeleton of new test app
Date: Thu, 20 Aug 2026 15:24:37 +0100 [thread overview]
Message-ID: <20260820142439.3684311-2-bruce.richardson@intel.com> (raw)
In-Reply-To: <20260820142439.3684311-1-bruce.richardson@intel.com>
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 <bruce.richardson@intel.com>
---
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
next prev parent reply other threads:[~2026-08-20 14:25 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-20 14:24 [RFC PATCH 0/3] add standalone mempool perf testing app Bruce Richardson
2026-08-20 14:24 ` Bruce Richardson [this message]
2026-08-20 14:24 ` [RFC PATCH 2/3] app/test-mempool-perf: add perf test logic Bruce Richardson
2026-08-20 14:24 ` [RFC PATCH 3/3] app/test-mempool-perf: add testing in pipeline model Bruce Richardson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260820142439.3684311-2-bruce.richardson@intel.com \
--to=bruce.richardson@intel.com \
--cc=dev@dpdk.org \
--cc=techboard@dpdk.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox