* [PATCH 0/7] app/test: make perf tests usable on wider range of systems
@ 2026-05-29 17:10 Stephen Hemminger
2026-05-29 17:10 ` [PATCH 1/7] app/test/reciprocal_division: make it a fast test Stephen Hemminger
` (7 more replies)
0 siblings, 8 replies; 21+ messages in thread
From: Stephen Hemminger @ 2026-05-29 17:10 UTC (permalink / raw)
To: dev; +Cc: Stephen Hemminger
Today' side quest is fixing the perftests to run on my machines.
Several of the perf/autotests assume server-class machines: they run for
billions of iterations, or size hugepage allocations by total lcore count,
which makes them time out or fail to allocate on smaller systems and on
machines with high core counts but modest memory.
This series trims runtime and memory use without losing meaningful test
coverage, and reports resource shortfalls as skips rather than failures.
Stephen Hemminger (7):
app/test/reciprocal_division: make it a fast test
app/test/reciprocal_division_perf: reduce test time
app/test/mempool_perf: size mempool by tested cores
app/test/mempool_perf: drop constant-values replay
app/test/mempool_perf: scale down for high core counts
app/test/test_rcu_qsbr_perf: call quiescent more often
app/test/test_pmd_perf: skip if no device available
app/test/test_mempool_perf.c | 84 +++----
app/test/test_pmd_perf.c | 2 +-
app/test/test_rcu_qsbr_perf.c | 3 +-
app/test/test_reciprocal_division.c | 279 +++++++++++++----------
app/test/test_reciprocal_division_perf.c | 4 +-
5 files changed, 196 insertions(+), 176 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 21+ messages in thread* [PATCH 1/7] app/test/reciprocal_division: make it a fast test 2026-05-29 17:10 [PATCH 0/7] app/test: make perf tests usable on wider range of systems Stephen Hemminger @ 2026-05-29 17:10 ` Stephen Hemminger 2026-05-29 17:10 ` [PATCH 2/7] app/test/reciprocal_division_perf: reduce test time Stephen Hemminger ` (6 subsequent siblings) 7 siblings, 0 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-05-29 17:10 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger This test is useful to have in CI, but the iteration values were too large to make it usable as a fast test. Redo the test with checks around boundary values and use unit test framework for sub tests. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_reciprocal_division.c | 279 ++++++++++++++++------------ 1 file changed, 157 insertions(+), 122 deletions(-) diff --git a/app/test/test_reciprocal_division.c b/app/test/test_reciprocal_division.c index 3d0736d8dd..80f8561523 100644 --- a/app/test/test_reciprocal_division.c +++ b/app/test/test_reciprocal_division.c @@ -5,162 +5,197 @@ #include "test.h" #include <stdio.h> -#include <unistd.h> #include <inttypes.h> #include <rte_common.h> -#include <rte_cycles.h> #include <rte_random.h> #include <rte_reciprocal.h> -#define MAX_ITERATIONS (1ULL << 32) -#define DIVIDE_ITER (100) +#define MAX_ITERATIONS (1ULL << 20) +#define DIVISORS_RANDOM 64 static int -test_reciprocal(void) +test_u32_divide(uint32_t a, uint32_t d, struct rte_reciprocal r) { - int result = 0; - uint32_t divisor_u32 = 0; - uint32_t dividend_u32; - uint32_t nresult_u32; - uint32_t rresult_u32; - uint64_t i, j; - uint64_t divisor_u64 = 0; - uint64_t dividend_u64; - uint64_t nresult_u64; - uint64_t rresult_u64; - struct rte_reciprocal reci_u32 = {0}; - struct rte_reciprocal_u64 reci_u64 = {0}; - - printf("Validating unsigned 32bit division.\n"); - for (i = 0; i < MAX_ITERATIONS; i++) { - /* Change divisor every DIVIDE_ITER iterations. */ - if (i % DIVIDE_ITER == 0) { - divisor_u32 = rte_rand(); - reci_u32 = rte_reciprocal_value(divisor_u32); - } - - dividend_u32 = rte_rand(); - nresult_u32 = dividend_u32 / divisor_u32; - rresult_u32 = rte_reciprocal_divide(dividend_u32, - reci_u32); - if (nresult_u32 != rresult_u32) { - printf("Division failed, %"PRIu32"/%"PRIu32" = " - "expected %"PRIu32" result %"PRIu32"\n", - dividend_u32, divisor_u32, - nresult_u32, rresult_u32); - result = 1; - break; - } - } + uint32_t expected = a / d; + uint32_t result = rte_reciprocal_divide(a, r); - printf("Validating unsigned 64bit division.\n"); - for (i = 0; i < MAX_ITERATIONS; i++) { - /* Change divisor every DIVIDE_ITER iterations. */ - if (i % DIVIDE_ITER == 0) { - divisor_u64 = rte_rand(); - reci_u64 = rte_reciprocal_value_u64(divisor_u64); - } + TEST_ASSERT_EQUAL(expected, result, + "%"PRIu32"/%"PRIu32" expected %"PRIu32" got %"PRIu32, + a, d, expected, result); + return 0; +} - dividend_u64 = rte_rand(); - nresult_u64 = dividend_u64 / divisor_u64; - rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, - &reci_u64); - if (nresult_u64 != rresult_u64) { - printf("Division failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - break; +static int +test_reciprocal_u32(void) +{ + const uint32_t edge_div_u32[] = { + 1, 2, 3, 7, 0x7fffffff, 0x80000000, 0xfffffffe, UINT32_MAX, + }; + unsigned int n_div = RTE_DIM(edge_div_u32) + DIVISORS_RANDOM; + + for (unsigned int di = 0; di < n_div; di++) { + uint32_t d; + if (di < RTE_DIM(edge_div_u32)) + d = edge_div_u32[di]; /* Make sure and test the edge cases */ + else + d = rte_rand_max(UINT32_MAX - 1) + 1; + + struct rte_reciprocal r = rte_reciprocal_value(d); + uint32_t qmax = UINT32_MAX / d; /* largest q with q*d <= UINT32_MAX */ + if (d != 1) + qmax++; + + for (unsigned int k = 0; k < MAX_ITERATIONS; k++) { + uint32_t q = rte_rand_max(qmax); + uint32_t val = q * d; /* fits in u32 */ + + /* Check around the value. + * Under and overflow of 32 bit value are fine here. + */ + if (test_u32_divide(val - 1, d, r) < 0 || + test_u32_divide(val, d, r) < 0 || + test_u32_divide(val + 1, d, r) < 0) + return -1; } } + return TEST_SUCCESS; +} - printf("Validating unsigned 64bit division with 32bit divisor.\n"); - for (i = 0; i < MAX_ITERATIONS; i++) { - /* Change divisor every DIVIDE_ITER iterations. */ - if (i % DIVIDE_ITER == 0) { - divisor_u64 = rte_rand() >> 32; - reci_u64 = rte_reciprocal_value_u64(divisor_u64); - } +static int +test_u64_divide(uint64_t a, uint64_t d, const struct rte_reciprocal_u64 *r) +{ + uint64_t expected = a / d; + uint64_t result = rte_reciprocal_divide_u64(a, r); + + TEST_ASSERT_EQUAL(expected, result, + "%"PRIu64"/%"PRIu64" expected %"PRIu64" got %"PRIu64, + a, d, expected, result); + return 0; +} - dividend_u64 = rte_rand(); - nresult_u64 = dividend_u64 / divisor_u64; - rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, - &reci_u64); +static int +test_reciprocal_u64(void) +{ + const uint64_t edge_div_u64[] = { + 1, 2, 3, 7, 0x7fffffff, 0x80000000, 0xfffffffe, UINT64_MAX, + }; + unsigned int n_div = RTE_DIM(edge_div_u64) + DIVISORS_RANDOM; + + for (unsigned int di = 0; di < n_div; di++) { + uint64_t d; + if (di < RTE_DIM(edge_div_u64)) + d = edge_div_u64[di]; + else + d = rte_rand_max(UINT64_MAX - 1) + 1; + + struct rte_reciprocal_u64 r = rte_reciprocal_value_u64(d); + uint64_t qmax = UINT64_MAX / d; /* largest q with q*d <= UINT64_MAX */ + if (d != 1) + ++qmax; + + for (unsigned int k = 0; k < MAX_ITERATIONS; k++) { + uint64_t q = rte_rand_max(qmax); + uint64_t val = q * d; + + if (test_u64_divide(val - 1, d, &r) < 0 || + test_u64_divide(val, d, &r) < 0 || + test_u64_divide(val + 1, d, &r) < 0) + return -1; - if (nresult_u64 != rresult_u64) { - printf("Division failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - break; } } + return TEST_SUCCESS; +} - printf("Validating division by power of 2.\n"); - for (i = 0; i < 32; i++) { - divisor_u64 = 1ull << i; - reci_u64 = rte_reciprocal_value_u64(divisor_u64); - reci_u32 = rte_reciprocal_value((uint32_t)divisor_u64); +static int +test_reciprocal_u64_small(void) +{ + /* 64-bit division with a 32-bit-range divisor */ + uint64_t divisor_u64 = (rte_rand() >> 32) | 1; + struct rte_reciprocal_u64 reci_u64 = rte_reciprocal_value_u64(divisor_u64); + + for (unsigned int i = 0; i < MAX_ITERATIONS; i++) { + uint64_t dividend_u64 = rte_rand(); + uint64_t nresult_u64 = dividend_u64 / divisor_u64; + uint64_t rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, &reci_u64); + + TEST_ASSERT_EQUAL(nresult_u64, rresult_u64, + "%"PRIu64"/%"PRIu64" = expected %"PRIu64" got %"PRIu64, + dividend_u64, divisor_u64, nresult_u64, rresult_u64); + } - for (j = 0; j < MAX_ITERATIONS >> 4; j++) { - dividend_u64 = rte_rand(); + return TEST_SUCCESS; +} - nresult_u64 = dividend_u64 / divisor_u64; - rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, +static int +test_reciprocal_pow2(void) +{ + for (unsigned int i = 0; i < 32; i++) { + uint64_t divisor_u64 = 1ULL << i; + struct rte_reciprocal_u64 reci_u64 = rte_reciprocal_value_u64(divisor_u64); + struct rte_reciprocal reci_u32 = rte_reciprocal_value((uint32_t)divisor_u64); + + for (unsigned int j = 0; j < MAX_ITERATIONS >> 4; j++) { + uint64_t dividend_u64 = rte_rand(); + uint64_t nresult_u64 = dividend_u64 / divisor_u64; + uint64_t rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, &reci_u64); - if (nresult_u64 != rresult_u64) { - printf( - "Division 64 failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - } - - nresult_u32 = (dividend_u64 >> 32) / divisor_u64; - rresult_u32 = rte_reciprocal_divide( + TEST_ASSERT_EQUAL(nresult_u64, rresult_u64, + "u64 %"PRIu64"/%"PRIu64" = expected %"PRIu64" got %"PRIu64, + dividend_u64, divisor_u64, + nresult_u64, rresult_u64); + + uint32_t nresult_u32 = (dividend_u64 >> 32) / divisor_u64; + uint32_t rresult_u32 = rte_reciprocal_divide( (dividend_u64 >> 32), reci_u32); - if (nresult_u32 != rresult_u32) { - printf( - "Division 32 failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64 >> 32, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - break; - } + TEST_ASSERT_EQUAL(nresult_u32, rresult_u32, + "u32 %"PRIu64"/%"PRIu64" = expected %"PRIu32" got %"PRIu32, + dividend_u64 >> 32, divisor_u64, + nresult_u32, rresult_u32); } } - for (; i < 64; i++) { - divisor_u64 = 1ull << i; - reci_u64 = rte_reciprocal_value_u64(divisor_u64); + for (unsigned int i = 32; i < 64; i++) { + uint64_t divisor_u64 = 1ULL << i; + struct rte_reciprocal_u64 reci_u64 = rte_reciprocal_value_u64(divisor_u64); - for (j = 0; j < MAX_ITERATIONS >> 4; j++) { - dividend_u64 = rte_rand(); - - nresult_u64 = dividend_u64 / divisor_u64; - rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, + for (unsigned int j = 0; j < MAX_ITERATIONS >> 4; j++) { + uint64_t dividend_u64 = rte_rand(); + uint64_t nresult_u64 = dividend_u64 / divisor_u64; + uint64_t rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, &reci_u64); - if (nresult_u64 != rresult_u64) { - printf("Division failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - break; - } + TEST_ASSERT_EQUAL(nresult_u64, rresult_u64, + "u64 %"PRIu64"/%"PRIu64" = expected %"PRIu64" got %"PRIu64, + dividend_u64, divisor_u64, + nresult_u64, rresult_u64); } } - return result; + return TEST_SUCCESS; +} + +static struct unit_test_suite reciprocal_tests = { + .suite_name = "reciprocal division autotest", + .setup = NULL, + .teardown = NULL, + .unit_test_cases = { + TEST_CASE(test_reciprocal_u32), + TEST_CASE(test_reciprocal_u64), + TEST_CASE(test_reciprocal_u64_small), + TEST_CASE(test_reciprocal_pow2), + TEST_CASES_END() + } +}; + +static int +test_reciprocal(void) +{ + return unit_test_suite_runner(&reciprocal_tests); } -REGISTER_PERF_TEST(reciprocal_division, test_reciprocal); +REGISTER_FAST_TEST(reciprocal_division_autotest, NOHUGE_OK, ASAN_OK, test_reciprocal); -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 2/7] app/test/reciprocal_division_perf: reduce test time 2026-05-29 17:10 [PATCH 0/7] app/test: make perf tests usable on wider range of systems Stephen Hemminger 2026-05-29 17:10 ` [PATCH 1/7] app/test/reciprocal_division: make it a fast test Stephen Hemminger @ 2026-05-29 17:10 ` Stephen Hemminger 2026-05-29 17:10 ` [PATCH 3/7] app/test/mempool_perf: size mempool by tested cores Stephen Hemminger ` (5 subsequent siblings) 7 siblings, 0 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-05-29 17:10 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger The test time was excessively long it does not need to go for 2^32 iterations. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_reciprocal_division_perf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/test/test_reciprocal_division_perf.c b/app/test/test_reciprocal_division_perf.c index 6c8afc6e36..046a77f5f2 100644 --- a/app/test/test_reciprocal_division_perf.c +++ b/app/test/test_reciprocal_division_perf.c @@ -13,8 +13,8 @@ #include <rte_random.h> #include <rte_reciprocal.h> -#define MAX_ITERATIONS (1ULL << 32) -#define DIVIDE_ITER (1ULL << 28) +#define MAX_ITERATIONS (1ULL << 24) +#define DIVIDE_ITER (1ULL << 10) static int test_reciprocal_division_perf(void) -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 3/7] app/test/mempool_perf: size mempool by tested cores 2026-05-29 17:10 [PATCH 0/7] app/test: make perf tests usable on wider range of systems Stephen Hemminger 2026-05-29 17:10 ` [PATCH 1/7] app/test/reciprocal_division: make it a fast test Stephen Hemminger 2026-05-29 17:10 ` [PATCH 2/7] app/test/reciprocal_division_perf: reduce test time Stephen Hemminger @ 2026-05-29 17:10 ` Stephen Hemminger 2026-06-01 8:12 ` Andrew Rybchenko 2026-05-29 17:10 ` [PATCH 4/7] app/test/mempool_perf: drop constant-values replay Stephen Hemminger ` (4 subsequent siblings) 7 siblings, 1 reply; 21+ messages in thread From: Stephen Hemminger @ 2026-05-29 17:10 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger, Andrew Rybchenko, Morten Brørup The mempool size is computed from rte_lcore_count() so on systems with many lcores the test requires multiple GB of hugepages even for the single-core and dual-core variants. On a 20 lcore system with 2 GB of hugepages the test fails with: cannot populate ring_mp_mc mempool Test Failed Size the four mempools by the number of cores actually exercised. Return TEST_SKIPPED rather than -1 when allocation or populate of a mempool fails, so insufficient memory is reported as a skip and not as a test failure. Propagate the skip through the combined mempool_perf_autotest wrapper. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_mempool_perf.c | 44 +++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/app/test/test_mempool_perf.c b/app/test/test_mempool_perf.c index e164eca788..19591ad0c9 100644 --- a/app/test/test_mempool_perf.c +++ b/app/test/test_mempool_perf.c @@ -84,7 +84,6 @@ #define MEMPOOL_ELT_SIZE 2048 #define MAX_KEEP 32768 #define N (128 * MAX_KEEP) -#define MEMPOOL_SIZE ((rte_lcore_count()*(MAX_KEEP+RTE_MEMPOOL_CACHE_MAX_SIZE*2))-1) /* Number of pointers fitting into one cache line. */ #define CACHE_LINE_BURST (RTE_CACHE_LINE_SIZE / sizeof(uintptr_t)) @@ -330,7 +329,7 @@ launch_cores(struct rte_mempool *mp, unsigned int cores) n_get_bulk, n_put_bulk, use_constant_values); - if (rte_mempool_avail_count(mp) != MEMPOOL_SIZE) { + if (rte_mempool_avail_count(mp) != mp->size) { printf("mempool is not full\n"); return -1; } @@ -449,22 +448,25 @@ do_all_mempool_perf_tests(unsigned int cores) const char *mp_cache_ops; const char *mp_nocache_ops; const char *default_pool_ops; + unsigned int mempool_size = cores * + (MAX_KEEP + RTE_MEMPOOL_CACHE_MAX_SIZE * 2) - 1; int ret = -1; /* create a mempool (without cache) */ - mp_nocache = rte_mempool_create("perf_test_nocache", MEMPOOL_SIZE, + mp_nocache = rte_mempool_create("perf_test_nocache", mempool_size, MEMPOOL_ELT_SIZE, 0, 0, NULL, NULL, my_obj_init, NULL, SOCKET_ID_ANY, 0); if (mp_nocache == NULL) { printf("cannot allocate mempool (without cache)\n"); + ret = TEST_SKIPPED; goto err; } mp_nocache_ops = rte_mempool_get_ops(mp_nocache->ops_index)->name; /* create a mempool (with cache) */ - mp_cache = rte_mempool_create("perf_test_cache", MEMPOOL_SIZE, + mp_cache = rte_mempool_create("perf_test_cache", mempool_size, MEMPOOL_ELT_SIZE, RTE_MEMPOOL_CACHE_MAX_SIZE, 0, NULL, NULL, @@ -472,6 +474,7 @@ do_all_mempool_perf_tests(unsigned int cores) SOCKET_ID_ANY, 0); if (mp_cache == NULL) { printf("cannot allocate mempool (with cache)\n"); + ret = TEST_SKIPPED; goto err; } mp_cache_ops = rte_mempool_get_ops(mp_cache->ops_index)->name; @@ -480,12 +483,13 @@ do_all_mempool_perf_tests(unsigned int cores) /* Create a mempool (without cache) based on Default handler */ default_pool_nocache = rte_mempool_create_empty("default_pool_nocache", - MEMPOOL_SIZE, + mempool_size, MEMPOOL_ELT_SIZE, 0, 0, SOCKET_ID_ANY, 0); if (default_pool_nocache == NULL) { printf("cannot allocate %s mempool (without cache)\n", default_pool_ops); + ret = TEST_SKIPPED; goto err; } if (rte_mempool_set_ops_byname(default_pool_nocache, default_pool_ops, NULL) < 0) { @@ -494,18 +498,20 @@ do_all_mempool_perf_tests(unsigned int cores) } if (rte_mempool_populate_default(default_pool_nocache) < 0) { printf("cannot populate %s mempool\n", default_pool_ops); + ret = TEST_SKIPPED; goto err; } rte_mempool_obj_iter(default_pool_nocache, my_obj_init, NULL); /* Create a mempool (with cache) based on Default handler */ default_pool_cache = rte_mempool_create_empty("default_pool_cache", - MEMPOOL_SIZE, + mempool_size, MEMPOOL_ELT_SIZE, RTE_MEMPOOL_CACHE_MAX_SIZE, 0, SOCKET_ID_ANY, 0); if (default_pool_cache == NULL) { printf("cannot allocate %s mempool (with cache)\n", default_pool_ops); + ret = TEST_SKIPPED; goto err; } if (rte_mempool_set_ops_byname(default_pool_cache, default_pool_ops, NULL) < 0) { @@ -514,6 +520,7 @@ do_all_mempool_perf_tests(unsigned int cores) } if (rte_mempool_populate_default(default_pool_cache) < 0) { printf("cannot populate %s mempool\n", default_pool_ops); + ret = TEST_SKIPPED; goto err; } rte_mempool_obj_iter(default_pool_cache, my_obj_init, NULL); @@ -584,27 +591,22 @@ test_mempool_perf_allcores(void) static int test_mempool_perf(void) { - int ret = -1; + int ret; /* performance test with 1, 2 and max cores */ - if (do_all_mempool_perf_tests(1) < 0) - goto err; + ret = do_all_mempool_perf_tests(1); + if (ret != 0) + return ret; if (rte_lcore_count() == 1) - goto done; + return 0; - if (do_all_mempool_perf_tests(2) < 0) - goto err; + ret = do_all_mempool_perf_tests(2); + if (ret != 0) + return ret; if (rte_lcore_count() == 2) - goto done; - - if (do_all_mempool_perf_tests(rte_lcore_count()) < 0) - goto err; + return 0; -done: - ret = 0; - -err: - return ret; + return do_all_mempool_perf_tests(rte_lcore_count()); } REGISTER_PERF_TEST(mempool_perf_autotest, test_mempool_perf); -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 3/7] app/test/mempool_perf: size mempool by tested cores 2026-05-29 17:10 ` [PATCH 3/7] app/test/mempool_perf: size mempool by tested cores Stephen Hemminger @ 2026-06-01 8:12 ` Andrew Rybchenko 2026-06-01 12:22 ` Morten Brørup 0 siblings, 1 reply; 21+ messages in thread From: Andrew Rybchenko @ 2026-06-01 8:12 UTC (permalink / raw) To: Stephen Hemminger, dev; +Cc: Morten Brørup On 5/29/26 8:10 PM, Stephen Hemminger wrote: > The mempool size is computed from rte_lcore_count() so on systems > with many lcores the test requires multiple GB of hugepages even > for the single-core and dual-core variants. On a 20 lcore system > with 2 GB of hugepages the test fails with: > > cannot populate ring_mp_mc mempool > Test Failed > > Size the four mempools by the number of cores actually exercised. > > Return TEST_SKIPPED rather than -1 when allocation or populate of > a mempool fails, so insufficient memory is reported as a skip and > not as a test failure. Propagate the skip through the combined > mempool_perf_autotest wrapper. > > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> Acked-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru> ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH 3/7] app/test/mempool_perf: size mempool by tested cores 2026-06-01 8:12 ` Andrew Rybchenko @ 2026-06-01 12:22 ` Morten Brørup 0 siblings, 0 replies; 21+ messages in thread From: Morten Brørup @ 2026-06-01 12:22 UTC (permalink / raw) To: Andrew Rybchenko, Stephen Hemminger, dev > From: Andrew Rybchenko [mailto:andrew.rybchenko@oktetlabs.ru] > Sent: Monday, 1 June 2026 10.13 > > On 5/29/26 8:10 PM, Stephen Hemminger wrote: > > The mempool size is computed from rte_lcore_count() so on systems > > with many lcores the test requires multiple GB of hugepages even > > for the single-core and dual-core variants. On a 20 lcore system > > with 2 GB of hugepages the test fails with: > > > > cannot populate ring_mp_mc mempool > > Test Failed > > > > Size the four mempools by the number of cores actually exercised. > > > > Return TEST_SKIPPED rather than -1 when allocation or populate of > > a mempool fails, so insufficient memory is reported as a skip and > > not as a test failure. Propagate the skip through the combined > > mempool_perf_autotest wrapper. > > > > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> > > Acked-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru> LGTM too. Acked-by: Morten Brørup <mb@smartsharesystems.com> ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 4/7] app/test/mempool_perf: drop constant-values replay 2026-05-29 17:10 [PATCH 0/7] app/test: make perf tests usable on wider range of systems Stephen Hemminger ` (2 preceding siblings ...) 2026-05-29 17:10 ` [PATCH 3/7] app/test/mempool_perf: size mempool by tested cores Stephen Hemminger @ 2026-05-29 17:10 ` Stephen Hemminger 2026-06-01 8:34 ` Andrew Rybchenko 2026-05-29 17:10 ` [PATCH 5/7] app/test/mempool_perf: scale down for high core counts Stephen Hemminger ` (3 subsequent siblings) 7 siblings, 1 reply; 21+ messages in thread From: Stephen Hemminger @ 2026-05-29 17:10 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger, Andrew Rybchenko, Morten Brørup The second nested matrix replays each (n_get_bulk == n_put_bulk) point with use_constant_values=1 to exercise the compile-time constant bulk-size paths in test_loop(). This roughly doubles the work for the get/put diagonal at every n_keep without adding new signal: the cycles/op result for a constant bulk is interesting in isolated inlining studies, not in routine regression sweeps. Drop the replay. The use_constant_values switch and its branches in test_loop() are retained for now since they are exercised by hand in any local benchmarking. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_mempool_perf.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/test/test_mempool_perf.c b/app/test/test_mempool_perf.c index 19591ad0c9..dd2f0bbaca 100644 --- a/app/test/test_mempool_perf.c +++ b/app/test/test_mempool_perf.c @@ -423,14 +423,6 @@ do_one_mempool_test(struct rte_mempool *mp, unsigned int cores, int external_cac ret = launch_cores(mp, cores); if (ret < 0) return -1; - - /* replay test with constant values */ - if (n_get_bulk == n_put_bulk) { - use_constant_values = 1; - ret = launch_cores(mp, cores); - if (ret < 0) - return -1; - } } } } -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 4/7] app/test/mempool_perf: drop constant-values replay 2026-05-29 17:10 ` [PATCH 4/7] app/test/mempool_perf: drop constant-values replay Stephen Hemminger @ 2026-06-01 8:34 ` Andrew Rybchenko 2026-06-01 13:22 ` Morten Brørup 0 siblings, 1 reply; 21+ messages in thread From: Andrew Rybchenko @ 2026-06-01 8:34 UTC (permalink / raw) To: Stephen Hemminger, dev; +Cc: Morten Brørup On 5/29/26 8:10 PM, Stephen Hemminger wrote: > The second nested matrix replays each (n_get_bulk == n_put_bulk) > point with use_constant_values=1 to exercise the compile-time > constant bulk-size paths in test_loop(). This roughly doubles the > work for the get/put diagonal at every n_keep without adding new > signal: the cycles/op result for a constant bulk is interesting in > isolated inlining studies, not in routine regression sweeps. > > Drop the replay. The use_constant_values switch and its branches > in test_loop() are retained for now since they are exercised by > hand in any local benchmarking. > > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> As far as I can see you delete the only place where use_constant_values is set to 1. It looks suspicious and basically preserves dead code. Since Morten added the code, the patch should wait for his approval. > --- > app/test/test_mempool_perf.c | 8 -------- > 1 file changed, 8 deletions(-) > > diff --git a/app/test/test_mempool_perf.c b/app/test/test_mempool_perf.c > index 19591ad0c9..dd2f0bbaca 100644 > --- a/app/test/test_mempool_perf.c > +++ b/app/test/test_mempool_perf.c > @@ -423,14 +423,6 @@ do_one_mempool_test(struct rte_mempool *mp, unsigned int cores, int external_cac > ret = launch_cores(mp, cores); > if (ret < 0) > return -1; > - > - /* replay test with constant values */ > - if (n_get_bulk == n_put_bulk) { > - use_constant_values = 1; > - ret = launch_cores(mp, cores); > - if (ret < 0) > - return -1; > - } > } > } > } ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH 4/7] app/test/mempool_perf: drop constant-values replay 2026-06-01 8:34 ` Andrew Rybchenko @ 2026-06-01 13:22 ` Morten Brørup 0 siblings, 0 replies; 21+ messages in thread From: Morten Brørup @ 2026-06-01 13:22 UTC (permalink / raw) To: Andrew Rybchenko, Stephen Hemminger, dev > From: Andrew Rybchenko [mailto:andrew.rybchenko@oktetlabs.ru] > Sent: Monday, 1 June 2026 10.35 > > On 5/29/26 8:10 PM, Stephen Hemminger wrote: > > The second nested matrix replays each (n_get_bulk == n_put_bulk) > > point with use_constant_values=1 to exercise the compile-time > > constant bulk-size paths in test_loop(). This roughly doubles the > > work for the get/put diagonal at every n_keep without adding new > > signal: the cycles/op result for a constant bulk is interesting in > > isolated inlining studies, not in routine regression sweeps. > > > > Drop the replay. The use_constant_values switch and its branches > > in test_loop() are retained for now since they are exercised by > > hand in any local benchmarking. > > > > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> > > As far as I can see you delete the only place where use_constant_values > is set to 1. It looks suspicious and basically preserves dead code. > Since Morten added the code, the patch should wait for his approval. Having used the mempool perf test extensively myself, I agree that it is painfully slow. Mempools are very often used with constant request sizes, so testing their performance remains relevant for regression sweeps too. NAK to this change. Maybe the testing of constant values could be reduced by using a subset of the non-constant mix of values. > > > --- > > app/test/test_mempool_perf.c | 8 -------- > > 1 file changed, 8 deletions(-) > > > > diff --git a/app/test/test_mempool_perf.c > b/app/test/test_mempool_perf.c > > index 19591ad0c9..dd2f0bbaca 100644 > > --- a/app/test/test_mempool_perf.c > > +++ b/app/test/test_mempool_perf.c > > @@ -423,14 +423,6 @@ do_one_mempool_test(struct rte_mempool *mp, > unsigned int cores, int external_cac > > ret = launch_cores(mp, cores); > > if (ret < 0) > > return -1; > > - > > - /* replay test with constant values */ > > - if (n_get_bulk == n_put_bulk) { > > - use_constant_values = 1; > > - ret = launch_cores(mp, cores); > > - if (ret < 0) > > - return -1; > > - } > > } > > } > > } ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 5/7] app/test/mempool_perf: scale down for high core counts 2026-05-29 17:10 [PATCH 0/7] app/test: make perf tests usable on wider range of systems Stephen Hemminger ` (3 preceding siblings ...) 2026-05-29 17:10 ` [PATCH 4/7] app/test/mempool_perf: drop constant-values replay Stephen Hemminger @ 2026-05-29 17:10 ` Stephen Hemminger 2026-06-01 8:42 ` Andrew Rybchenko 2026-06-01 12:58 ` Morten Brørup 2026-05-29 17:10 ` [PATCH 6/7] app/test/test_rcu_qsbr_perf: call quiescent more often Stephen Hemminger ` (2 subsequent siblings) 7 siblings, 2 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-05-29 17:10 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger, Andrew Rybchenko, Morten Brørup On a 32-core system the test matrix runs the cartesian product of 4 mempools, 3 core-count configurations and ~340 (n_keep, bulk) points at TIME_S=1 second each: about 67 minutes total, well past the 10 minute perf-test timeout. Two reductions, no loss of meaningful signal: 1. Per-point duration: 1 second -> 200 ms. Each point currently collects 10^5-10^6 mempool ops; 200 ms still yields >10^4 samples, well above the noise floor for a cycles-per-op average. 2. Matrix trim: drop adjacent bulk and n_keep points that don't produce regime changes. Retained set covers the boundaries that matter: 1, 4, cache-line burst (8), typical packet burst (32) and cache size (RTE_MEMPOOL_CACHE_MAX_SIZE = 512) for bulk; 32 (fits in cache), 512 (= cache size) and 32768 (far exceeds cache) for n_keep. Combined effect: ~10x runtime reduction. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_mempool_perf.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/app/test/test_mempool_perf.c b/app/test/test_mempool_perf.c index dd2f0bbaca..6801812a8d 100644 --- a/app/test/test_mempool_perf.c +++ b/app/test/test_mempool_perf.c @@ -61,26 +61,21 @@ * * - Pseudorandom max bulk size (*n_max_bulk*) * - * - Max bulk from CACHE_LINE_BURST to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE, - * where CACHE_LINE_BURST is the number of pointers fitting into one CPU cache line. + * - Max bulk: CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE, + * where CACHE_LINE_BURST is the number of pointers fitting into + * one CPU cache line. * * - Fixed bulk size (*n_get_bulk*, *n_put_bulk*) * - * - Bulk get from 1 to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE - * - Bulk put from 1 to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE - * - Bulk get and put from 1 to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE, compile time constant + * - Bulk get: 1, 4, CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE + * - Bulk put: 1, 4, CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE * * - Number of kept objects (*n_keep*) * - * - 32 - * - 128 - * - 512 - * - 2048 - * - 8192 - * - 32768 + * - 32, 512, 32768 */ -#define TIME_S 1 +#define TIME_MS 200 #define MEMPOOL_ELT_SIZE 2048 #define MAX_KEEP 32768 #define N (128 * MAX_KEEP) @@ -257,7 +252,7 @@ per_lcore_mempool_test(void *arg) start_cycles = rte_get_timer_cycles(); - while (time_diff/hz < TIME_S) { + while (time_diff < hz * TIME_MS / 1000) { if (n_max_bulk != 0) ret = test_loop_random(mp, cache, n_keep, n_max_bulk); else if (!use_constant_values) @@ -376,13 +371,10 @@ launch_cores(struct rte_mempool *mp, unsigned int cores) static int do_one_mempool_test(struct rte_mempool *mp, unsigned int cores, int external_cache) { - unsigned int bulk_tab_max[] = { CACHE_LINE_BURST, 32, 64, 128, 256, - RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; - unsigned int bulk_tab_get[] = { 1, 4, CACHE_LINE_BURST, 32, 64, 128, 256, - RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; - unsigned int bulk_tab_put[] = { 1, 4, CACHE_LINE_BURST, 32, 64, 128, 256, - RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; - unsigned int keep_tab[] = { 32, 128, 512, 2048, 8192, 32768, 0 }; + unsigned int bulk_tab_max[] = { CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; + unsigned int bulk_tab_get[] = { 1, 4, CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; + unsigned int bulk_tab_put[] = { 1, 4, CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; + unsigned int keep_tab[] = { 32, 512, 32768, 0 }; unsigned int *max_bulk_ptr; unsigned int *get_bulk_ptr; unsigned int *put_bulk_ptr; -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 5/7] app/test/mempool_perf: scale down for high core counts 2026-05-29 17:10 ` [PATCH 5/7] app/test/mempool_perf: scale down for high core counts Stephen Hemminger @ 2026-06-01 8:42 ` Andrew Rybchenko 2026-06-01 12:58 ` Morten Brørup 1 sibling, 0 replies; 21+ messages in thread From: Andrew Rybchenko @ 2026-06-01 8:42 UTC (permalink / raw) To: Stephen Hemminger, dev; +Cc: Morten Brørup On 5/29/26 8:10 PM, Stephen Hemminger wrote: > On a 32-core system the test matrix runs the cartesian product of > 4 mempools, 3 core-count configurations and ~340 (n_keep, bulk) > points at TIME_S=1 second each: about 67 minutes total, well past > the 10 minute perf-test timeout. > > Two reductions, no loss of meaningful signal: > > 1. Per-point duration: 1 second -> 200 ms. Each point currently > collects 10^5-10^6 mempool ops; 200 ms still yields >10^4 > samples, well above the noise floor for a cycles-per-op average. > > 2. Matrix trim: drop adjacent bulk and n_keep points that don't > produce regime changes. Retained set covers the boundaries > that matter: 1, 4, cache-line burst (8), typical packet burst > (32) and cache size (RTE_MEMPOOL_CACHE_MAX_SIZE = 512) for bulk; > 32 (fits in cache), 512 (= cache size) and 32768 (far exceeds > cache) for n_keep. > > Combined effect: ~10x runtime reduction. > > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> I have no strong opinion on the patch. In general it LGTM and makes sense. Acked-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru> > --- > app/test/test_mempool_perf.c | 32 ++++++++++++-------------------- > 1 file changed, 12 insertions(+), 20 deletions(-) > > diff --git a/app/test/test_mempool_perf.c b/app/test/test_mempool_perf.c > index dd2f0bbaca..6801812a8d 100644 > --- a/app/test/test_mempool_perf.c > +++ b/app/test/test_mempool_perf.c > @@ -61,26 +61,21 @@ > * > * - Pseudorandom max bulk size (*n_max_bulk*) > * > - * - Max bulk from CACHE_LINE_BURST to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE, > - * where CACHE_LINE_BURST is the number of pointers fitting into one CPU cache line. > + * - Max bulk: CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE, > + * where CACHE_LINE_BURST is the number of pointers fitting into > + * one CPU cache line. > * > * - Fixed bulk size (*n_get_bulk*, *n_put_bulk*) > * > - * - Bulk get from 1 to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE > - * - Bulk put from 1 to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE > - * - Bulk get and put from 1 to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE, compile time constant > + * - Bulk get: 1, 4, CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE > + * - Bulk put: 1, 4, CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE > * > * - Number of kept objects (*n_keep*) > * > - * - 32 > - * - 128 > - * - 512 > - * - 2048 > - * - 8192 > - * - 32768 > + * - 32, 512, 32768 > */ > > -#define TIME_S 1 > +#define TIME_MS 200 > #define MEMPOOL_ELT_SIZE 2048 > #define MAX_KEEP 32768 > #define N (128 * MAX_KEEP) > @@ -257,7 +252,7 @@ per_lcore_mempool_test(void *arg) > > start_cycles = rte_get_timer_cycles(); > > - while (time_diff/hz < TIME_S) { > + while (time_diff < hz * TIME_MS / 1000) { > if (n_max_bulk != 0) > ret = test_loop_random(mp, cache, n_keep, n_max_bulk); > else if (!use_constant_values) > @@ -376,13 +371,10 @@ launch_cores(struct rte_mempool *mp, unsigned int cores) > static int > do_one_mempool_test(struct rte_mempool *mp, unsigned int cores, int external_cache) > { > - unsigned int bulk_tab_max[] = { CACHE_LINE_BURST, 32, 64, 128, 256, > - RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; > - unsigned int bulk_tab_get[] = { 1, 4, CACHE_LINE_BURST, 32, 64, 128, 256, > - RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; > - unsigned int bulk_tab_put[] = { 1, 4, CACHE_LINE_BURST, 32, 64, 128, 256, > - RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; > - unsigned int keep_tab[] = { 32, 128, 512, 2048, 8192, 32768, 0 }; > + unsigned int bulk_tab_max[] = { CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; > + unsigned int bulk_tab_get[] = { 1, 4, CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; > + unsigned int bulk_tab_put[] = { 1, 4, CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; > + unsigned int keep_tab[] = { 32, 512, 32768, 0 }; > unsigned int *max_bulk_ptr; > unsigned int *get_bulk_ptr; > unsigned int *put_bulk_ptr; ^ permalink raw reply [flat|nested] 21+ messages in thread
* RE: [PATCH 5/7] app/test/mempool_perf: scale down for high core counts 2026-05-29 17:10 ` [PATCH 5/7] app/test/mempool_perf: scale down for high core counts Stephen Hemminger 2026-06-01 8:42 ` Andrew Rybchenko @ 2026-06-01 12:58 ` Morten Brørup 1 sibling, 0 replies; 21+ messages in thread From: Morten Brørup @ 2026-06-01 12:58 UTC (permalink / raw) To: Stephen Hemminger, Andrew Rybchenko, dev > From: Stephen Hemminger [mailto:stephen@networkplumber.org] > Sent: Friday, 29 May 2026 19.11 > > On a 32-core system the test matrix runs the cartesian product of > 4 mempools, 3 core-count configurations and ~340 (n_keep, bulk) > points at TIME_S=1 second each: about 67 minutes total, well past > the 10 minute perf-test timeout. > > Two reductions, no loss of meaningful signal: > > 1. Per-point duration: 1 second -> 200 ms. Each point currently > collects 10^5-10^6 mempool ops; 200 ms still yields >10^4 > samples, well above the noise floor for a cycles-per-op average. Ack to this. > > 2. Matrix trim: drop adjacent bulk and n_keep points that don't > produce regime changes. Retained set covers the boundaries > that matter: 1, 4, cache-line burst (8), typical packet burst > (32) and cache size (RTE_MEMPOOL_CACHE_MAX_SIZE = 512) for bulk; > 32 (fits in cache), 512 (= cache size) and 32768 (far exceeds > cache) for n_keep. My mempool optimization patch [1] introduces a bounce buffer limit, so huge requests are not needlessly copied twice to bounce through the cache, but are moved directly between application memory and the mempool backend driver. The bounce buffer limit is half the cache size. So, please keep 256. Maybe change it to RTE_MEMPOOL_CACHE_MAX_SIZE / 2. [1]: https://patchwork.dpdk.org/project/dpdk/patch/20260526140000.175092-1-mb@smartsharesystems.com/ Also consider keeping 64; it seems to be a popular default burst size for some CPUs. On the other hand, if the patch introducing default mbuf burst sizes [2] gets accepted, we could replace 32 with RTE_MBUF_BURST_SIZE_THROUGHPUT and 4 with RTE_MBUF_BURST_SIZE_LATENCY. No strong opinion on 64; I'll leave that up to you. [2]: https://patchwork.dpdk.org/project/dpdk/list/?series=37914 > > Combined effect: ~10x runtime reduction. > > Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> With suggested changes, Acked-by: Morten Brørup <mb@smartsharesystems.com> ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 6/7] app/test/test_rcu_qsbr_perf: call quiescent more often 2026-05-29 17:10 [PATCH 0/7] app/test: make perf tests usable on wider range of systems Stephen Hemminger ` (4 preceding siblings ...) 2026-05-29 17:10 ` [PATCH 5/7] app/test/mempool_perf: scale down for high core counts Stephen Hemminger @ 2026-05-29 17:10 ` Stephen Hemminger 2026-05-29 17:11 ` [PATCH 7/7] app/test/test_pmd_perf: skip if no device available Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 0/5] app/test: make perf tests usable on wider range of systems Stephen Hemminger 7 siblings, 0 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-05-29 17:10 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger, Honnappa Nagarahalli The performance test would generate large backlog of quiescent actions which caused test to take excessively long time. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_rcu_qsbr_perf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/test/test_rcu_qsbr_perf.c b/app/test/test_rcu_qsbr_perf.c index bdffd64e60..8768a9ce1a 100644 --- a/app/test/test_rcu_qsbr_perf.c +++ b/app/test/test_rcu_qsbr_perf.c @@ -313,9 +313,8 @@ test_rcu_qsbr_hash_reader(void *arg) pdata[thread_id]++; } rte_rcu_qsbr_unlock(temp, thread_id); + rte_rcu_qsbr_quiescent(temp, thread_id); } - /* Update quiescent state counter */ - rte_rcu_qsbr_quiescent(temp, thread_id); rte_rcu_qsbr_thread_offline(temp, thread_id); loop_cnt++; } while (!writer_done); -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 7/7] app/test/test_pmd_perf: skip if no device available 2026-05-29 17:10 [PATCH 0/7] app/test: make perf tests usable on wider range of systems Stephen Hemminger ` (5 preceding siblings ...) 2026-05-29 17:10 ` [PATCH 6/7] app/test/test_rcu_qsbr_perf: call quiescent more often Stephen Hemminger @ 2026-05-29 17:11 ` Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 0/5] app/test: make perf tests usable on wider range of systems Stephen Hemminger 7 siblings, 0 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-05-29 17:11 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger Rather than exiting with FAILURE the test should exit with SKIPPED status if it can't find a device to attach to. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_pmd_perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/test/test_pmd_perf.c b/app/test/test_pmd_perf.c index 995b0a6f20..8eff0a1223 100644 --- a/app/test/test_pmd_perf.c +++ b/app/test/test_pmd_perf.c @@ -691,7 +691,7 @@ test_pmd_perf(void) if (nb_ports < NB_ETHPORTS_USED) { printf("At least %u port(s) used for perf. test\n", NB_ETHPORTS_USED); - return -1; + return TEST_SKIPPED; } nb_lcores = rte_lcore_count(); -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 0/5] app/test: make perf tests usable on wider range of systems 2026-05-29 17:10 [PATCH 0/7] app/test: make perf tests usable on wider range of systems Stephen Hemminger ` (6 preceding siblings ...) 2026-05-29 17:11 ` [PATCH 7/7] app/test/test_pmd_perf: skip if no device available Stephen Hemminger @ 2026-08-26 1:56 ` Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 1/5] app/test/reciprocal_division: make it a fast test Stephen Hemminger ` (4 more replies) 7 siblings, 5 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-08-26 1:56 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger Several of the perf/autotests assume server-class machines: they run for billions of iterations, or size hugepage allocations by total lcore count, which makes them time out or fail to allocate on smaller systems and on machines with high core counts but modest memory. This series trims runtime and memory use without losing meaningful test coverage, and reports resource shortfalls as skips rather than failures. The goal is a perf suite that can be run regularly to catch large regressions, so finishing reliably matters more than fine resolution. Measured with "meson test --suite=perf-tests" on a 32 lcore system with 10 GB of hugepages, same machine and same session before and after: main this series Ok 41 44 Fail 1 0 Timeout 4 0 mempool_perf_autotest timeout 414s mempool_perf_autotest_1core timeout 83s mempool_perf_autotest_2cores timeout 84s mempool_perf_autotest_allcores timeout 200s rcu_qsbr_perf_autotest 495s 32s reciprocal_division_perf 252s 1s reciprocal_division 104s moved to fast-tests pmd_perf_autotest FAIL skip total suite 65.5 min 24.8 min The 65.5 minutes understates it: four of those tests were killed at the 600 second timeout rather than finishing, so the real figure is unknown and larger. Note that with enough memory for the largest mempool, all four mempool variants time out on main, including the single core one. Memory is not the only problem; the test also has no working bound on its own runtime. Two distinct problems were behind that, and both needed fixing: - test_loop() did a fixed number of objects per call, and the elapsed time is only checked between calls. A small bulk size needs many more mempool operations for the same objects, and each operation is much more expensive when many cores contend, so with bulk size 1 on 32 cores a single call took over 50 seconds and TIME_MS could not bound anything. The work per call is now limited by mempool operations. - Each test point launches and joins every lcore. That costs about 2 ms on one core but over a second on 32, so the runtime is set by the number of points rather than by the time spent measuring. Above two cores a reduced set of get/put bulk sizes is used. On a machine with less memory the mempool sizing matters too: sizing the pools by the cores actually exercised lets the one and two core variants run where they previously could not allocate at all, and a shortfall is now reported as a skip rather than a failure. v2 changes: - Patch 5 no longer drops the constant-values replay. Morten pointed out that mempools are commonly used with compile time constant request sizes, so those paths are real regression signal, and NAKed the removal. The replay is kept but restricted to a subset of the bulk sizes rather than the whole get/put diagonal. - The bulk size tables keep 64 and RTE_MEMPOOL_CACHE_MAX_SIZE / 2. The latter is the bounce buffer limit in Morten's mempool optimization work, above which requests bypass the cache, so it marks a real change in behaviour. - The mempool patches are squashed into one per test file. The result contains considerably more than was reviewed in v1, so the acks given on the v1 mempool patches have been dropped rather than carried forward. - Added the test_loop() bound and the reduced bulk table described above, plus per-point elapsed time and a flush before measuring, so a run killed by a timeout shows where it got to. That is how the 50 second points were found. Stephen Hemminger (5): app/test/reciprocal_division: make it a fast test app/test/reciprocal_division_perf: reduce test time app/test/test_rcu_qsbr_perf: call quiescent more often app/test/test_pmd_perf: skip if no device available app/test/mempool_perf: adjust test for large core counts app/test/test_mempool_perf.c | 179 ++++++++++----- app/test/test_pmd_perf.c | 2 +- app/test/test_rcu_qsbr_perf.c | 3 +- app/test/test_reciprocal_division.c | 279 +++++++++++++---------- app/test/test_reciprocal_division_perf.c | 4 +- 5 files changed, 281 insertions(+), 186 deletions(-) -- 2.53.0 ^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v2 1/5] app/test/reciprocal_division: make it a fast test 2026-08-26 1:56 ` [PATCH v2 0/5] app/test: make perf tests usable on wider range of systems Stephen Hemminger @ 2026-08-26 1:56 ` Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 2/5] app/test/reciprocal_division_perf: reduce test time Stephen Hemminger ` (3 subsequent siblings) 4 siblings, 0 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-08-26 1:56 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger This test is useful to have in CI, but the iteration values were too large to make it usable as a fast test. Redo the test with checks around boundary values and use unit test framework for sub tests. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_reciprocal_division.c | 279 ++++++++++++++++------------ 1 file changed, 157 insertions(+), 122 deletions(-) diff --git a/app/test/test_reciprocal_division.c b/app/test/test_reciprocal_division.c index 3d0736d8dd..80f8561523 100644 --- a/app/test/test_reciprocal_division.c +++ b/app/test/test_reciprocal_division.c @@ -5,162 +5,197 @@ #include "test.h" #include <stdio.h> -#include <unistd.h> #include <inttypes.h> #include <rte_common.h> -#include <rte_cycles.h> #include <rte_random.h> #include <rte_reciprocal.h> -#define MAX_ITERATIONS (1ULL << 32) -#define DIVIDE_ITER (100) +#define MAX_ITERATIONS (1ULL << 20) +#define DIVISORS_RANDOM 64 static int -test_reciprocal(void) +test_u32_divide(uint32_t a, uint32_t d, struct rte_reciprocal r) { - int result = 0; - uint32_t divisor_u32 = 0; - uint32_t dividend_u32; - uint32_t nresult_u32; - uint32_t rresult_u32; - uint64_t i, j; - uint64_t divisor_u64 = 0; - uint64_t dividend_u64; - uint64_t nresult_u64; - uint64_t rresult_u64; - struct rte_reciprocal reci_u32 = {0}; - struct rte_reciprocal_u64 reci_u64 = {0}; - - printf("Validating unsigned 32bit division.\n"); - for (i = 0; i < MAX_ITERATIONS; i++) { - /* Change divisor every DIVIDE_ITER iterations. */ - if (i % DIVIDE_ITER == 0) { - divisor_u32 = rte_rand(); - reci_u32 = rte_reciprocal_value(divisor_u32); - } - - dividend_u32 = rte_rand(); - nresult_u32 = dividend_u32 / divisor_u32; - rresult_u32 = rte_reciprocal_divide(dividend_u32, - reci_u32); - if (nresult_u32 != rresult_u32) { - printf("Division failed, %"PRIu32"/%"PRIu32" = " - "expected %"PRIu32" result %"PRIu32"\n", - dividend_u32, divisor_u32, - nresult_u32, rresult_u32); - result = 1; - break; - } - } + uint32_t expected = a / d; + uint32_t result = rte_reciprocal_divide(a, r); - printf("Validating unsigned 64bit division.\n"); - for (i = 0; i < MAX_ITERATIONS; i++) { - /* Change divisor every DIVIDE_ITER iterations. */ - if (i % DIVIDE_ITER == 0) { - divisor_u64 = rte_rand(); - reci_u64 = rte_reciprocal_value_u64(divisor_u64); - } + TEST_ASSERT_EQUAL(expected, result, + "%"PRIu32"/%"PRIu32" expected %"PRIu32" got %"PRIu32, + a, d, expected, result); + return 0; +} - dividend_u64 = rte_rand(); - nresult_u64 = dividend_u64 / divisor_u64; - rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, - &reci_u64); - if (nresult_u64 != rresult_u64) { - printf("Division failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - break; +static int +test_reciprocal_u32(void) +{ + const uint32_t edge_div_u32[] = { + 1, 2, 3, 7, 0x7fffffff, 0x80000000, 0xfffffffe, UINT32_MAX, + }; + unsigned int n_div = RTE_DIM(edge_div_u32) + DIVISORS_RANDOM; + + for (unsigned int di = 0; di < n_div; di++) { + uint32_t d; + if (di < RTE_DIM(edge_div_u32)) + d = edge_div_u32[di]; /* Make sure and test the edge cases */ + else + d = rte_rand_max(UINT32_MAX - 1) + 1; + + struct rte_reciprocal r = rte_reciprocal_value(d); + uint32_t qmax = UINT32_MAX / d; /* largest q with q*d <= UINT32_MAX */ + if (d != 1) + qmax++; + + for (unsigned int k = 0; k < MAX_ITERATIONS; k++) { + uint32_t q = rte_rand_max(qmax); + uint32_t val = q * d; /* fits in u32 */ + + /* Check around the value. + * Under and overflow of 32 bit value are fine here. + */ + if (test_u32_divide(val - 1, d, r) < 0 || + test_u32_divide(val, d, r) < 0 || + test_u32_divide(val + 1, d, r) < 0) + return -1; } } + return TEST_SUCCESS; +} - printf("Validating unsigned 64bit division with 32bit divisor.\n"); - for (i = 0; i < MAX_ITERATIONS; i++) { - /* Change divisor every DIVIDE_ITER iterations. */ - if (i % DIVIDE_ITER == 0) { - divisor_u64 = rte_rand() >> 32; - reci_u64 = rte_reciprocal_value_u64(divisor_u64); - } +static int +test_u64_divide(uint64_t a, uint64_t d, const struct rte_reciprocal_u64 *r) +{ + uint64_t expected = a / d; + uint64_t result = rte_reciprocal_divide_u64(a, r); + + TEST_ASSERT_EQUAL(expected, result, + "%"PRIu64"/%"PRIu64" expected %"PRIu64" got %"PRIu64, + a, d, expected, result); + return 0; +} - dividend_u64 = rte_rand(); - nresult_u64 = dividend_u64 / divisor_u64; - rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, - &reci_u64); +static int +test_reciprocal_u64(void) +{ + const uint64_t edge_div_u64[] = { + 1, 2, 3, 7, 0x7fffffff, 0x80000000, 0xfffffffe, UINT64_MAX, + }; + unsigned int n_div = RTE_DIM(edge_div_u64) + DIVISORS_RANDOM; + + for (unsigned int di = 0; di < n_div; di++) { + uint64_t d; + if (di < RTE_DIM(edge_div_u64)) + d = edge_div_u64[di]; + else + d = rte_rand_max(UINT64_MAX - 1) + 1; + + struct rte_reciprocal_u64 r = rte_reciprocal_value_u64(d); + uint64_t qmax = UINT64_MAX / d; /* largest q with q*d <= UINT64_MAX */ + if (d != 1) + ++qmax; + + for (unsigned int k = 0; k < MAX_ITERATIONS; k++) { + uint64_t q = rte_rand_max(qmax); + uint64_t val = q * d; + + if (test_u64_divide(val - 1, d, &r) < 0 || + test_u64_divide(val, d, &r) < 0 || + test_u64_divide(val + 1, d, &r) < 0) + return -1; - if (nresult_u64 != rresult_u64) { - printf("Division failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - break; } } + return TEST_SUCCESS; +} - printf("Validating division by power of 2.\n"); - for (i = 0; i < 32; i++) { - divisor_u64 = 1ull << i; - reci_u64 = rte_reciprocal_value_u64(divisor_u64); - reci_u32 = rte_reciprocal_value((uint32_t)divisor_u64); +static int +test_reciprocal_u64_small(void) +{ + /* 64-bit division with a 32-bit-range divisor */ + uint64_t divisor_u64 = (rte_rand() >> 32) | 1; + struct rte_reciprocal_u64 reci_u64 = rte_reciprocal_value_u64(divisor_u64); + + for (unsigned int i = 0; i < MAX_ITERATIONS; i++) { + uint64_t dividend_u64 = rte_rand(); + uint64_t nresult_u64 = dividend_u64 / divisor_u64; + uint64_t rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, &reci_u64); + + TEST_ASSERT_EQUAL(nresult_u64, rresult_u64, + "%"PRIu64"/%"PRIu64" = expected %"PRIu64" got %"PRIu64, + dividend_u64, divisor_u64, nresult_u64, rresult_u64); + } - for (j = 0; j < MAX_ITERATIONS >> 4; j++) { - dividend_u64 = rte_rand(); + return TEST_SUCCESS; +} - nresult_u64 = dividend_u64 / divisor_u64; - rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, +static int +test_reciprocal_pow2(void) +{ + for (unsigned int i = 0; i < 32; i++) { + uint64_t divisor_u64 = 1ULL << i; + struct rte_reciprocal_u64 reci_u64 = rte_reciprocal_value_u64(divisor_u64); + struct rte_reciprocal reci_u32 = rte_reciprocal_value((uint32_t)divisor_u64); + + for (unsigned int j = 0; j < MAX_ITERATIONS >> 4; j++) { + uint64_t dividend_u64 = rte_rand(); + uint64_t nresult_u64 = dividend_u64 / divisor_u64; + uint64_t rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, &reci_u64); - if (nresult_u64 != rresult_u64) { - printf( - "Division 64 failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - } - - nresult_u32 = (dividend_u64 >> 32) / divisor_u64; - rresult_u32 = rte_reciprocal_divide( + TEST_ASSERT_EQUAL(nresult_u64, rresult_u64, + "u64 %"PRIu64"/%"PRIu64" = expected %"PRIu64" got %"PRIu64, + dividend_u64, divisor_u64, + nresult_u64, rresult_u64); + + uint32_t nresult_u32 = (dividend_u64 >> 32) / divisor_u64; + uint32_t rresult_u32 = rte_reciprocal_divide( (dividend_u64 >> 32), reci_u32); - if (nresult_u32 != rresult_u32) { - printf( - "Division 32 failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64 >> 32, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - break; - } + TEST_ASSERT_EQUAL(nresult_u32, rresult_u32, + "u32 %"PRIu64"/%"PRIu64" = expected %"PRIu32" got %"PRIu32, + dividend_u64 >> 32, divisor_u64, + nresult_u32, rresult_u32); } } - for (; i < 64; i++) { - divisor_u64 = 1ull << i; - reci_u64 = rte_reciprocal_value_u64(divisor_u64); + for (unsigned int i = 32; i < 64; i++) { + uint64_t divisor_u64 = 1ULL << i; + struct rte_reciprocal_u64 reci_u64 = rte_reciprocal_value_u64(divisor_u64); - for (j = 0; j < MAX_ITERATIONS >> 4; j++) { - dividend_u64 = rte_rand(); - - nresult_u64 = dividend_u64 / divisor_u64; - rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, + for (unsigned int j = 0; j < MAX_ITERATIONS >> 4; j++) { + uint64_t dividend_u64 = rte_rand(); + uint64_t nresult_u64 = dividend_u64 / divisor_u64; + uint64_t rresult_u64 = rte_reciprocal_divide_u64(dividend_u64, &reci_u64); - if (nresult_u64 != rresult_u64) { - printf("Division failed, %"PRIu64"/%"PRIu64" = " - "expected %"PRIu64" result %"PRIu64"\n", - dividend_u64, divisor_u64, - nresult_u64, rresult_u64); - result = 1; - break; - } + TEST_ASSERT_EQUAL(nresult_u64, rresult_u64, + "u64 %"PRIu64"/%"PRIu64" = expected %"PRIu64" got %"PRIu64, + dividend_u64, divisor_u64, + nresult_u64, rresult_u64); } } - return result; + return TEST_SUCCESS; +} + +static struct unit_test_suite reciprocal_tests = { + .suite_name = "reciprocal division autotest", + .setup = NULL, + .teardown = NULL, + .unit_test_cases = { + TEST_CASE(test_reciprocal_u32), + TEST_CASE(test_reciprocal_u64), + TEST_CASE(test_reciprocal_u64_small), + TEST_CASE(test_reciprocal_pow2), + TEST_CASES_END() + } +}; + +static int +test_reciprocal(void) +{ + return unit_test_suite_runner(&reciprocal_tests); } -REGISTER_PERF_TEST(reciprocal_division, test_reciprocal); +REGISTER_FAST_TEST(reciprocal_division_autotest, NOHUGE_OK, ASAN_OK, test_reciprocal); -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 2/5] app/test/reciprocal_division_perf: reduce test time 2026-08-26 1:56 ` [PATCH v2 0/5] app/test: make perf tests usable on wider range of systems Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 1/5] app/test/reciprocal_division: make it a fast test Stephen Hemminger @ 2026-08-26 1:56 ` Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 3/5] app/test/test_rcu_qsbr_perf: call quiescent more often Stephen Hemminger ` (2 subsequent siblings) 4 siblings, 0 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-08-26 1:56 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger The test time was excessively long it does not need to go for 2^32 iterations. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_reciprocal_division_perf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/test/test_reciprocal_division_perf.c b/app/test/test_reciprocal_division_perf.c index 6c8afc6e36..046a77f5f2 100644 --- a/app/test/test_reciprocal_division_perf.c +++ b/app/test/test_reciprocal_division_perf.c @@ -13,8 +13,8 @@ #include <rte_random.h> #include <rte_reciprocal.h> -#define MAX_ITERATIONS (1ULL << 32) -#define DIVIDE_ITER (1ULL << 28) +#define MAX_ITERATIONS (1ULL << 24) +#define DIVIDE_ITER (1ULL << 10) static int test_reciprocal_division_perf(void) -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 3/5] app/test/test_rcu_qsbr_perf: call quiescent more often 2026-08-26 1:56 ` [PATCH v2 0/5] app/test: make perf tests usable on wider range of systems Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 1/5] app/test/reciprocal_division: make it a fast test Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 2/5] app/test/reciprocal_division_perf: reduce test time Stephen Hemminger @ 2026-08-26 1:56 ` Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 4/5] app/test/test_pmd_perf: skip if no device available Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 5/5] app/test/mempool_perf: adjust test to work with many cores Stephen Hemminger 4 siblings, 0 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-08-26 1:56 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger The performance test would generate large backlog of quiescent actions which caused test to take excessively long time. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_rcu_qsbr_perf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/test/test_rcu_qsbr_perf.c b/app/test/test_rcu_qsbr_perf.c index bdffd64e60..8768a9ce1a 100644 --- a/app/test/test_rcu_qsbr_perf.c +++ b/app/test/test_rcu_qsbr_perf.c @@ -313,9 +313,8 @@ test_rcu_qsbr_hash_reader(void *arg) pdata[thread_id]++; } rte_rcu_qsbr_unlock(temp, thread_id); + rte_rcu_qsbr_quiescent(temp, thread_id); } - /* Update quiescent state counter */ - rte_rcu_qsbr_quiescent(temp, thread_id); rte_rcu_qsbr_thread_offline(temp, thread_id); loop_cnt++; } while (!writer_done); -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 4/5] app/test/test_pmd_perf: skip if no device available 2026-08-26 1:56 ` [PATCH v2 0/5] app/test: make perf tests usable on wider range of systems Stephen Hemminger ` (2 preceding siblings ...) 2026-08-26 1:56 ` [PATCH v2 3/5] app/test/test_rcu_qsbr_perf: call quiescent more often Stephen Hemminger @ 2026-08-26 1:56 ` Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 5/5] app/test/mempool_perf: adjust test to work with many cores Stephen Hemminger 4 siblings, 0 replies; 21+ messages in thread From: Stephen Hemminger @ 2026-08-26 1:56 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger Rather than exiting with FAILURE the test should exit with SKIPPED status if it can't find a device to attach to. Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_pmd_perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/test/test_pmd_perf.c b/app/test/test_pmd_perf.c index 995b0a6f20..8eff0a1223 100644 --- a/app/test/test_pmd_perf.c +++ b/app/test/test_pmd_perf.c @@ -691,7 +691,7 @@ test_pmd_perf(void) if (nb_ports < NB_ETHPORTS_USED) { printf("At least %u port(s) used for perf. test\n", NB_ETHPORTS_USED); - return -1; + return TEST_SKIPPED; } nb_lcores = rte_lcore_count(); -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v2 5/5] app/test/mempool_perf: adjust test to work with many cores 2026-08-26 1:56 ` [PATCH v2 0/5] app/test: make perf tests usable on wider range of systems Stephen Hemminger ` (3 preceding siblings ...) 2026-08-26 1:56 ` [PATCH v2 4/5] app/test/test_pmd_perf: skip if no device available Stephen Hemminger @ 2026-08-26 1:56 ` Stephen Hemminger 2026-08-26 3:58 ` Morten Brørup 4 siblings, 1 reply; 21+ messages in thread From: Stephen Hemminger @ 2026-08-26 1:56 UTC (permalink / raw) To: dev; +Cc: Stephen Hemminger On a system with many lcores this test both fails to allocate on a machine with modest memory and runs far past the 10 minute perf-test timeout. With enough hugepages for the largest mempool, all four variants time out, including the single core one. Size the four mempools by the number of cores actually exercised rather than by rte_lcore_count(), and report a shortfall as TEST_SKIPPED rather than a failure. Run the largest core count first in the combined test, so a machine which cannot fit it skips immediately instead of spending minutes on the smaller runs. Reduce the time per test point from 1 second to 200 ms and drop the n_keep values which do not mark a change in behaviour, keeping 32, 512 and 32768. For bulk sizes keep 1, 4, CACHE_LINE_BURST, 32, 64, RTE_MEMPOOL_CACHE_MAX_SIZE / 2 and RTE_MEMPOOL_CACHE_MAX_SIZE; the second to last is the bounce buffer limit above which requests bypass the cache. Replay a subset of those with compile time constant values rather than the whole get/put diagonal, since mempools are commonly used with constant request sizes and those paths still need covering. The elapsed time is only checked between calls to test_loop(), and one call did a fixed number of objects. A small bulk size needs many more mempool operations for those objects, and each operation is much more expensive when many cores contend for the ring, so with bulk size 1 on 32 cores a single call took over 50 seconds and TIME_MS could not bound anything: n_keep=32768 n_get_bulk=1 n_put_bulk=1 elapsed_ms=53431 Limit the mempool operations per call instead of the objects, and return the number of objects handled so the reported rate stays correct. Each test point also launches and joins every lcore, which costs about 2 ms on one core but over a second on 32, so use a reduced set of get/put bulk sizes above two cores. Print the elapsed time of each point and flush before measuring, so a run killed by a timeout shows where it got to. On a 32 lcore system with 10 GB of hugepages: mempool_perf_autotest timeout -> 414s mempool_perf_autotest_1core timeout -> 83s mempool_perf_autotest_2cores timeout -> 84s mempool_perf_autotest_allcores timeout -> 200s Signed-off-by: Stephen Hemminger <stephen@networkplumber.org> --- app/test/test_mempool_perf.c | 179 +++++++++++++++++++++++------------ 1 file changed, 120 insertions(+), 59 deletions(-) diff --git a/app/test/test_mempool_perf.c b/app/test/test_mempool_perf.c index e164eca788..8313bdc1fc 100644 --- a/app/test/test_mempool_perf.c +++ b/app/test/test_mempool_perf.c @@ -43,7 +43,7 @@ * Each core get *n_keep* objects per bulk of *n_get_bulk*. Then, * objects are put back in the pool per bulk of *n_put_bulk*. * - * This sequence is done during TIME_S seconds. + * This sequence is done during TIME_MS milliseconds. * * This test is done on the following configurations: * @@ -61,30 +61,32 @@ * * - Pseudorandom max bulk size (*n_max_bulk*) * - * - Max bulk from CACHE_LINE_BURST to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE, - * where CACHE_LINE_BURST is the number of pointers fitting into one CPU cache line. + * - Max bulk: CACHE_LINE_BURST, 32, 64, RTE_MEMPOOL_CACHE_MAX_SIZE / 2 + * and RTE_MEMPOOL_CACHE_MAX_SIZE, where CACHE_LINE_BURST is the + * number of pointers fitting into one CPU cache line. * * - Fixed bulk size (*n_get_bulk*, *n_put_bulk*) * - * - Bulk get from 1 to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE - * - Bulk put from 1 to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE - * - Bulk get and put from 1 to 256, and RTE_MEMPOOL_CACHE_MAX_SIZE, compile time constant + * - Bulk get: 1, 4, CACHE_LINE_BURST, 32, 64, + * RTE_MEMPOOL_CACHE_MAX_SIZE / 2, RTE_MEMPOOL_CACHE_MAX_SIZE + * - Bulk put: 1, 4, CACHE_LINE_BURST, 32, 64, + * RTE_MEMPOOL_CACHE_MAX_SIZE / 2, RTE_MEMPOOL_CACHE_MAX_SIZE + * - Bulk get and put of 1, CACHE_LINE_BURST, 32 and + * RTE_MEMPOOL_CACHE_MAX_SIZE, compile time constant * * - Number of kept objects (*n_keep*) * - * - 32 - * - 128 - * - 512 - * - 2048 - * - 8192 - * - 32768 + * - 32, 512, 32768 */ -#define TIME_S 1 +#define TIME_MS 200 +/* above this many cores, use the reduced bulk size tables */ +#define MANYCORE_THRESHOLD 2 #define MEMPOOL_ELT_SIZE 2048 #define MAX_KEEP 32768 #define N (128 * MAX_KEEP) -#define MEMPOOL_SIZE ((rte_lcore_count()*(MAX_KEEP+RTE_MEMPOOL_CACHE_MAX_SIZE*2))-1) +/* max mempool operations per call to test_loop() */ +#define MAX_OPS (256 * 1024) /* Number of pointers fitting into one cache line. */ #define CACHE_LINE_BURST (RTE_CACHE_LINE_SIZE / sizeof(uintptr_t)) @@ -147,9 +149,21 @@ test_loop(struct rte_mempool *mp, struct rte_mempool_cache *cache, alignas(RTE_CACHE_LINE_SIZE) void *obj_table[MAX_KEEP]; unsigned int idx; unsigned int i; + unsigned int iter; int ret; - for (i = 0; likely(i < (N / x_keep)); i++) { + /* + * The elapsed time is only checked between calls to this function, + * so bound the work done by one call. A small bulk size means many + * more mempool operations for the same number of objects, and each + * operation is far more expensive when many cores contend, so limit + * the operations rather than the objects. + */ + iter = MAX_OPS / (x_keep / x_get_bulk + x_keep / x_put_bulk); + if (iter == 0) + iter = 1; + + for (i = 0; likely(i < iter); i++) { /* get x_keep objects by bulk of x_get_bulk */ for (idx = 0; idx < x_keep; idx += x_get_bulk) { ret = rte_mempool_generic_get(mp, @@ -171,7 +185,7 @@ test_loop(struct rte_mempool *mp, struct rte_mempool_cache *cache, } } - return 0; + return iter * x_keep; } static __rte_always_inline int @@ -215,7 +229,7 @@ test_loop_random(struct rte_mempool *mp, struct rte_mempool_cache *cache, } } - return 0; + return N; } static int @@ -258,7 +272,7 @@ per_lcore_mempool_test(void *arg) start_cycles = rte_get_timer_cycles(); - while (time_diff/hz < TIME_S) { + while (time_diff < hz * TIME_MS / 1000) { if (n_max_bulk != 0) ret = test_loop_random(mp, cache, n_keep, n_max_bulk); else if (!use_constant_values) @@ -289,7 +303,7 @@ per_lcore_mempool_test(void *arg) end_cycles = rte_get_timer_cycles(); time_diff = end_cycles - start_cycles; - stats[lcore_id].enq_count += N; + stats[lcore_id].enq_count += ret; } stats[lcore_id].duration_cycles = time_diff; @@ -312,6 +326,7 @@ launch_cores(struct rte_mempool *mp, unsigned int cores) int ret; unsigned int cores_save = cores; double hz = rte_get_timer_hz(); + uint64_t start = rte_get_timer_cycles(); rte_atomic_store_explicit(&synchro, 0, rte_memory_order_relaxed); @@ -330,7 +345,10 @@ launch_cores(struct rte_mempool *mp, unsigned int cores) n_get_bulk, n_put_bulk, use_constant_values); - if (rte_mempool_avail_count(mp) != MEMPOOL_SIZE) { + /* show progress if test is killed by timeout */ + fflush(stdout); + + if (rte_mempool_avail_count(mp) != mp->size) { printf("mempool is not full\n"); return -1; } @@ -368,7 +386,8 @@ launch_cores(struct rte_mempool *mp, unsigned int cores) rate += (double)stats[lcore_id].enq_count * hz / (double)stats[lcore_id].duration_cycles; - printf("rate_persec=%10" PRIu64 "\n", rate); + printf("rate_persec=%10" PRIu64 " elapsed_ms=%4.0f\n", rate, + (rte_get_timer_cycles() - start) * 1000.0 / hz); return 0; } @@ -377,19 +396,34 @@ launch_cores(struct rte_mempool *mp, unsigned int cores) static int do_one_mempool_test(struct rte_mempool *mp, unsigned int cores, int external_cache) { - unsigned int bulk_tab_max[] = { CACHE_LINE_BURST, 32, 64, 128, 256, - RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; - unsigned int bulk_tab_get[] = { 1, 4, CACHE_LINE_BURST, 32, 64, 128, 256, + unsigned int bulk_tab_max[] = { CACHE_LINE_BURST, 32, 64, + RTE_MEMPOOL_CACHE_MAX_SIZE / 2, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; + unsigned int bulk_tab_get[] = { 1, 4, CACHE_LINE_BURST, 32, 64, + RTE_MEMPOOL_CACHE_MAX_SIZE / 2, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; + unsigned int bulk_tab_put[] = { 1, 4, CACHE_LINE_BURST, 32, 64, + RTE_MEMPOOL_CACHE_MAX_SIZE / 2, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; + /* launch overhead dominates with many cores, so test fewer sizes */ + unsigned int bulk_tab_get_manycore[] = { 1, 32, + RTE_MEMPOOL_CACHE_MAX_SIZE / 2, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; + unsigned int bulk_tab_put_manycore[] = { 1, 32, + RTE_MEMPOOL_CACHE_MAX_SIZE / 2, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; + /* subset of the get/put bulk sizes, replayed with constant values */ + unsigned int bulk_tab_const[] = { 1, CACHE_LINE_BURST, 32, RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; - unsigned int bulk_tab_put[] = { 1, 4, CACHE_LINE_BURST, 32, 64, 128, 256, - RTE_MEMPOOL_CACHE_MAX_SIZE, 0 }; - unsigned int keep_tab[] = { 32, 128, 512, 2048, 8192, 32768, 0 }; + unsigned int keep_tab[] = { 32, 512, 32768, 0 }; unsigned int *max_bulk_ptr; unsigned int *get_bulk_ptr; unsigned int *put_bulk_ptr; unsigned int *keep_ptr; + unsigned int *get_tab = bulk_tab_get; + unsigned int *put_tab = bulk_tab_put; int ret; + if (cores > MANYCORE_THRESHOLD) { + get_tab = bulk_tab_get_manycore; + put_tab = bulk_tab_put_manycore; + } + for (keep_ptr = keep_tab; *keep_ptr; keep_ptr++) { for (max_bulk_ptr = bulk_tab_max; *max_bulk_ptr; max_bulk_ptr++) { @@ -409,8 +443,8 @@ do_one_mempool_test(struct rte_mempool *mp, unsigned int cores, int external_cac } for (keep_ptr = keep_tab; *keep_ptr; keep_ptr++) { - for (get_bulk_ptr = bulk_tab_get; *get_bulk_ptr; get_bulk_ptr++) { - for (put_bulk_ptr = bulk_tab_put; *put_bulk_ptr; put_bulk_ptr++) { + for (get_bulk_ptr = get_tab; *get_bulk_ptr; get_bulk_ptr++) { + for (put_bulk_ptr = put_tab; *put_bulk_ptr; put_bulk_ptr++) { if (*keep_ptr < *get_bulk_ptr || *keep_ptr < *put_bulk_ptr) continue; @@ -424,18 +458,34 @@ do_one_mempool_test(struct rte_mempool *mp, unsigned int cores, int external_cac ret = launch_cores(mp, cores); if (ret < 0) return -1; - - /* replay test with constant values */ - if (n_get_bulk == n_put_bulk) { - use_constant_values = 1; - ret = launch_cores(mp, cores); - if (ret < 0) - return -1; - } } } } + /* + * Replay a subset of the bulk sizes with compile time constant + * values, to cover the inlined constant bulk paths. Only the + * n_get_bulk == n_put_bulk diagonal is valid here, and a subset + * of it is enough to catch a regression in those paths. + */ + for (keep_ptr = keep_tab; *keep_ptr; keep_ptr++) { + for (get_bulk_ptr = bulk_tab_const; *get_bulk_ptr; get_bulk_ptr++) { + + if (*keep_ptr < *get_bulk_ptr) + continue; + + use_external_cache = external_cache; + use_constant_values = 1; + n_max_bulk = 0; + n_get_bulk = *get_bulk_ptr; + n_put_bulk = *get_bulk_ptr; + n_keep = *keep_ptr; + ret = launch_cores(mp, cores); + if (ret < 0) + return -1; + } + } + return 0; } @@ -449,22 +499,25 @@ do_all_mempool_perf_tests(unsigned int cores) const char *mp_cache_ops; const char *mp_nocache_ops; const char *default_pool_ops; + unsigned int mempool_size = cores * + (MAX_KEEP + RTE_MEMPOOL_CACHE_MAX_SIZE * 2) - 1; int ret = -1; /* create a mempool (without cache) */ - mp_nocache = rte_mempool_create("perf_test_nocache", MEMPOOL_SIZE, + mp_nocache = rte_mempool_create("perf_test_nocache", mempool_size, MEMPOOL_ELT_SIZE, 0, 0, NULL, NULL, my_obj_init, NULL, SOCKET_ID_ANY, 0); if (mp_nocache == NULL) { printf("cannot allocate mempool (without cache)\n"); + ret = TEST_SKIPPED; goto err; } mp_nocache_ops = rte_mempool_get_ops(mp_nocache->ops_index)->name; /* create a mempool (with cache) */ - mp_cache = rte_mempool_create("perf_test_cache", MEMPOOL_SIZE, + mp_cache = rte_mempool_create("perf_test_cache", mempool_size, MEMPOOL_ELT_SIZE, RTE_MEMPOOL_CACHE_MAX_SIZE, 0, NULL, NULL, @@ -472,6 +525,7 @@ do_all_mempool_perf_tests(unsigned int cores) SOCKET_ID_ANY, 0); if (mp_cache == NULL) { printf("cannot allocate mempool (with cache)\n"); + ret = TEST_SKIPPED; goto err; } mp_cache_ops = rte_mempool_get_ops(mp_cache->ops_index)->name; @@ -480,12 +534,13 @@ do_all_mempool_perf_tests(unsigned int cores) /* Create a mempool (without cache) based on Default handler */ default_pool_nocache = rte_mempool_create_empty("default_pool_nocache", - MEMPOOL_SIZE, + mempool_size, MEMPOOL_ELT_SIZE, 0, 0, SOCKET_ID_ANY, 0); if (default_pool_nocache == NULL) { printf("cannot allocate %s mempool (without cache)\n", default_pool_ops); + ret = TEST_SKIPPED; goto err; } if (rte_mempool_set_ops_byname(default_pool_nocache, default_pool_ops, NULL) < 0) { @@ -494,18 +549,20 @@ do_all_mempool_perf_tests(unsigned int cores) } if (rte_mempool_populate_default(default_pool_nocache) < 0) { printf("cannot populate %s mempool\n", default_pool_ops); + ret = TEST_SKIPPED; goto err; } rte_mempool_obj_iter(default_pool_nocache, my_obj_init, NULL); /* Create a mempool (with cache) based on Default handler */ default_pool_cache = rte_mempool_create_empty("default_pool_cache", - MEMPOOL_SIZE, + mempool_size, MEMPOOL_ELT_SIZE, RTE_MEMPOOL_CACHE_MAX_SIZE, 0, SOCKET_ID_ANY, 0); if (default_pool_cache == NULL) { printf("cannot allocate %s mempool (with cache)\n", default_pool_ops); + ret = TEST_SKIPPED; goto err; } if (rte_mempool_set_ops_byname(default_pool_cache, default_pool_ops, NULL) < 0) { @@ -514,6 +571,7 @@ do_all_mempool_perf_tests(unsigned int cores) } if (rte_mempool_populate_default(default_pool_cache) < 0) { printf("cannot populate %s mempool\n", default_pool_ops); + ret = TEST_SKIPPED; goto err; } rte_mempool_obj_iter(default_pool_cache, my_obj_init, NULL); @@ -584,27 +642,30 @@ test_mempool_perf_allcores(void) static int test_mempool_perf(void) { - int ret = -1; - - /* performance test with 1, 2 and max cores */ - if (do_all_mempool_perf_tests(1) < 0) - goto err; - if (rte_lcore_count() == 1) - goto done; - - if (do_all_mempool_perf_tests(2) < 0) - goto err; - if (rte_lcore_count() == 2) - goto done; + unsigned int cores = rte_lcore_count(); + int ret; - if (do_all_mempool_perf_tests(rte_lcore_count()) < 0) - goto err; + /* + * Performance test with max, 1 and 2 cores. + * + * The largest configuration needs the most memory, so run it + * first: on a system which cannot fit it, the mempool allocation + * fails right away and the test is skipped without first spending + * minutes on the smaller core counts. + */ + if (cores > 2) { + ret = do_all_mempool_perf_tests(cores); + if (ret != 0) + return ret; + } -done: - ret = 0; + ret = do_all_mempool_perf_tests(1); + if (ret != 0) + return ret; + if (cores == 1) + return 0; -err: - return ret; + return do_all_mempool_perf_tests(2); } REGISTER_PERF_TEST(mempool_perf_autotest, test_mempool_perf); -- 2.53.0 ^ permalink raw reply related [flat|nested] 21+ messages in thread
* RE: [PATCH v2 5/5] app/test/mempool_perf: adjust test to work with many cores 2026-08-26 1:56 ` [PATCH v2 5/5] app/test/mempool_perf: adjust test to work with many cores Stephen Hemminger @ 2026-08-26 3:58 ` Morten Brørup 0 siblings, 0 replies; 21+ messages in thread From: Morten Brørup @ 2026-08-26 3:58 UTC (permalink / raw) To: Stephen Hemminger, dev Acked-by: Morten Brørup <mb@smartsharesystems.com> ^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-08-26 3:58 UTC | newest] Thread overview: 21+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-29 17:10 [PATCH 0/7] app/test: make perf tests usable on wider range of systems Stephen Hemminger 2026-05-29 17:10 ` [PATCH 1/7] app/test/reciprocal_division: make it a fast test Stephen Hemminger 2026-05-29 17:10 ` [PATCH 2/7] app/test/reciprocal_division_perf: reduce test time Stephen Hemminger 2026-05-29 17:10 ` [PATCH 3/7] app/test/mempool_perf: size mempool by tested cores Stephen Hemminger 2026-06-01 8:12 ` Andrew Rybchenko 2026-06-01 12:22 ` Morten Brørup 2026-05-29 17:10 ` [PATCH 4/7] app/test/mempool_perf: drop constant-values replay Stephen Hemminger 2026-06-01 8:34 ` Andrew Rybchenko 2026-06-01 13:22 ` Morten Brørup 2026-05-29 17:10 ` [PATCH 5/7] app/test/mempool_perf: scale down for high core counts Stephen Hemminger 2026-06-01 8:42 ` Andrew Rybchenko 2026-06-01 12:58 ` Morten Brørup 2026-05-29 17:10 ` [PATCH 6/7] app/test/test_rcu_qsbr_perf: call quiescent more often Stephen Hemminger 2026-05-29 17:11 ` [PATCH 7/7] app/test/test_pmd_perf: skip if no device available Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 0/5] app/test: make perf tests usable on wider range of systems Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 1/5] app/test/reciprocal_division: make it a fast test Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 2/5] app/test/reciprocal_division_perf: reduce test time Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 3/5] app/test/test_rcu_qsbr_perf: call quiescent more often Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 4/5] app/test/test_pmd_perf: skip if no device available Stephen Hemminger 2026-08-26 1:56 ` [PATCH v2 5/5] app/test/mempool_perf: adjust test to work with many cores Stephen Hemminger 2026-08-26 3:58 ` Morten Brørup
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox