* [PATCH v7] kunit: memcpy: Add benchmark
@ 2026-11-10 2:16 Matteo Croce
2026-11-10 2:20 ` Matteo Croce
2026-11-10 3:39 ` Andrew Morton
0 siblings, 2 replies; 4+ messages in thread
From: Matteo Croce @ 2026-11-10 2:16 UTC (permalink / raw)
To: Brendan Higgins, David Gow, Andrew Morton, David Laight
Cc: linux-kselftest, linux-kernel
Add optional benchmarks for memcpy() and memmove(), enabled with
CONFIG_MEMCPY_KUNIT_BENCHMARK.
Each function is run over several buffer sizes, up to 4 MiB, with both
aligned and unaligned buffers, to spot platforms where unaligned
accesses have a noticeable performance impact.
The copy length is hidden from the compiler, so the architecture's
out-of-line implementation is always measured rather than an inlined
constant-size copy.
Sample output on a RISC-V machine:
# modprobe memcpy_kunit
KTAP version 1
1..1
KTAP version 1
# Subtest: memcpy
# module: memcpy_kunit
1..8
[...]
# memcpy_bench_test: memcpy: aligned copy of len 2: 3 MB/s
# memcpy_bench_test: memcpy: unaligned copy of len 2: 4 MB/s
# memcpy_bench_test: memcpy: aligned copy of len 64: 127 MB/s
# memcpy_bench_test: memcpy: unaligned copy of len 64: 117 MB/s
# memcpy_bench_test: memcpy: aligned copy of len 256: 508 MB/s
# memcpy_bench_test: memcpy: unaligned copy of len 256: 542 MB/s
# memcpy_bench_test: memcpy: aligned copy of len 4194304: 848 MB/s
# memcpy_bench_test: memcpy: unaligned copy of len 4194304: 699 MB/s
# memcpy_bench_test.speed: slow
ok 7 memcpy_bench_test
# memmove_bench_test: memmove: aligned move of len 64: 121 MB/s
# memmove_bench_test: memmove: unaligned move of len 64: 104 MB/s
# memmove_bench_test: memmove: aligned move of len 256: 445 MB/s
# memmove_bench_test: memmove: unaligned move of len 256: 410 MB/s
# memmove_bench_test: memmove: aligned move of len 4194304: 1419 MB/s
# memmove_bench_test: memmove: unaligned move of len 4194304: 1360 MB/s
# memmove_bench_test.speed: slow
ok 8 memmove_bench_test
# memcpy: pass:8 fail:0 skip:0 total:8
# Totals: pass:8 fail:0 skip:0 total:8
ok 1 memcpy
Signed-off-by: Matteo Croce <technoboy85@gmail.com>
---
v6 -> v7:
- subject: "KUnit: memcpy: add benchmark" -> "kunit: memcpy: Add benchmark"
- memmove: make the aligned case actually aligned
- fix int overflow in throughput calculation, cap max size to 4 MiB
- hide copy length from the compiler (OPTIMIZER_HIDE_VAR)
- always register the bench cases, kunit_skip() when disabled
- Kconfig: move under MEMCPY_KUNIT_TEST, reword prompt and help
- update sample output
v6: https://lore.kernel.org/all/20260302023052.73943-1-teknoraver@meta.com/
lib/Kconfig.debug | 12 ++++
lib/tests/memcpy_kunit.c | 120 +++++++++++++++++++++++++++++++++++++++
2 files changed, 132 insertions(+)
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 6b24a2c89b84..7884a9a0f78f 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3006,6 +3006,18 @@ config MEMCPY_KUNIT_TEST
If unsure, say N.
+config MEMCPY_KUNIT_BENCHMARK
+ bool "Benchmark memcpy() and memmove() functions at runtime"
+ depends on MEMCPY_KUNIT_TEST
+ help
+ Adds benchmark cases to the memcpy KUnit test suite, measuring
+ the throughput of memcpy() and memmove() over several buffer
+ sizes, with both aligned and unaligned buffers. This helps to
+ spot architectures where unaligned accesses have a noticeable
+ performance impact. Results are printed in the KUnit log.
+
+ If unsure, say N.
+
config MIN_HEAP_KUNIT_TEST
tristate "Min heap test" if !KUNIT_ALL_TESTS
depends on KUNIT
diff --git a/lib/tests/memcpy_kunit.c b/lib/tests/memcpy_kunit.c
index d36933554e46..7b7103a72ada 100644
--- a/lib/tests/memcpy_kunit.c
+++ b/lib/tests/memcpy_kunit.c
@@ -11,6 +11,7 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/overflow.h>
+#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
@@ -493,6 +494,123 @@ static void memmove_overlap_test(struct kunit *test)
}
}
+#define COPIES_NUM 100
+#define BENCH_MAX_SIZE min_t(size_t, SZ_4M, KMALLOC_MAX_SIZE)
+
+static void memcpy_bench_size_align(struct kunit *test, int size, bool unalign)
+{
+ u64 start, end, total_ns = 0;
+ char *dst, *src;
+ int ret = 0;
+
+ /* Prevent constant-size inlining: always call the arch memcpy() */
+ OPTIMIZER_HIDE_VAR(size);
+
+ dst = kzalloc(size, GFP_KERNEL);
+ if (!dst) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ src = kzalloc(size, GFP_KERNEL);
+ if (!src) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ for (int i = 0; i < COPIES_NUM; i++) {
+ local_irq_disable();
+ start = ktime_get_ns();
+ memcpy(dst + unalign, src, size - unalign);
+ end = ktime_get_ns();
+ local_irq_enable();
+ total_ns += end - start;
+ }
+
+ /* Avoid division by zero */
+ if (!total_ns)
+ total_ns = 1;
+
+ kunit_info(test, "memcpy: %saligned copy of len %d: %llu MB/s",
+ unalign ? "un" : "", size,
+ div64_u64((u64)COPIES_NUM * size * NSEC_PER_SEC,
+ total_ns * 1024 * 1024));
+
+ kfree(src);
+
+out_free:
+ kfree(dst);
+
+out:
+ KUNIT_ASSERT_EQ(test, ret, 0);
+}
+
+static void memcpy_bench_size(struct kunit *test, int size)
+{
+ memcpy_bench_size_align(test, size, false);
+ memcpy_bench_size_align(test, size, true);
+}
+
+static void memcpy_bench_test(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_MEMCPY_KUNIT_BENCHMARK))
+ kunit_skip(test, "not enabled");
+
+ memcpy_bench_size(test, 2);
+ memcpy_bench_size(test, 64);
+ memcpy_bench_size(test, 256);
+ memcpy_bench_size(test, BENCH_MAX_SIZE);
+}
+
+static void memmove_bench_size_align(struct kunit *test, int size, bool unalign)
+{
+ u64 start, end, total_ns = 0;
+ char *buf;
+ /* Keep the destination word aligned in the aligned case */
+ const int shift = ALIGN(size / 10, sizeof(long));
+
+ /* Prevent constant-size inlining: always call the arch memmove() */
+ OPTIMIZER_HIDE_VAR(size);
+
+ buf = kzalloc(size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+
+ for (int i = 0; i < COPIES_NUM; i++) {
+ local_irq_disable();
+ start = ktime_get_ns();
+ memmove(buf + shift + unalign, buf, size - shift - unalign);
+ end = ktime_get_ns();
+ local_irq_enable();
+ total_ns += end - start;
+ }
+
+ if (!total_ns)
+ total_ns = 1;
+
+ kunit_info(test, "memmove: %saligned move of len %d: %llu MB/s",
+ unalign ? "un" : "", size,
+ div64_u64((u64)COPIES_NUM * (size - shift) * NSEC_PER_SEC,
+ total_ns * 1024 * 1024));
+
+ kfree(buf);
+}
+
+static void memmove_bench_size(struct kunit *test, int size)
+{
+ memmove_bench_size_align(test, size, false);
+ memmove_bench_size_align(test, size, true);
+}
+
+static void memmove_bench_test(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_MEMCPY_KUNIT_BENCHMARK))
+ kunit_skip(test, "not enabled");
+
+ memmove_bench_size(test, 64);
+ memmove_bench_size(test, 256);
+ memmove_bench_size(test, BENCH_MAX_SIZE);
+}
+
static struct kunit_case memcpy_test_cases[] = {
KUNIT_CASE(memset_test),
KUNIT_CASE(memcpy_test),
@@ -500,6 +618,8 @@ static struct kunit_case memcpy_test_cases[] = {
KUNIT_CASE_SLOW(memmove_test),
KUNIT_CASE_SLOW(memmove_large_test),
KUNIT_CASE_SLOW(memmove_overlap_test),
+ KUNIT_CASE_SLOW(memcpy_bench_test),
+ KUNIT_CASE_SLOW(memmove_bench_test),
{}
};
--
2.56.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v7] kunit: memcpy: Add benchmark
2026-11-10 2:16 [PATCH v7] kunit: memcpy: Add benchmark Matteo Croce
@ 2026-11-10 2:20 ` Matteo Croce
2026-11-10 3:39 ` Andrew Morton
1 sibling, 0 replies; 4+ messages in thread
From: Matteo Croce @ 2026-11-10 2:20 UTC (permalink / raw)
To: Brendan Higgins, David Gow, Andrew Morton, David Laight,
Kees Cook, linux-hardening
Cc: linux-kselftest, linux-kernel
+Cc Kees and linux-hardening, as per get_maintainer.pl
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v7] kunit: memcpy: Add benchmark
2026-11-10 2:16 [PATCH v7] kunit: memcpy: Add benchmark Matteo Croce
2026-11-10 2:20 ` Matteo Croce
@ 2026-11-10 3:39 ` Andrew Morton
2026-11-10 10:01 ` David Laight
1 sibling, 1 reply; 4+ messages in thread
From: Andrew Morton @ 2026-11-10 3:39 UTC (permalink / raw)
To: Matteo Croce
Cc: Brendan Higgins, David Gow, David Laight, linux-kselftest,
linux-kernel, Kees Cook, linux-hardening
On Tue, 10 Nov 2026 03:16:22 +0100 Matteo Croce <technoboy85@gmail.com> wrote:
> Add optional benchmarks for memcpy() and memmove(), enabled with
> CONFIG_MEMCPY_KUNIT_BENCHMARK.
hm, sorry, I must ask "why". Nowadays optimizing memcpy() is a
super-specialized thing undertaken by the bravest architecture
specialists who are unlikely to rely upon a generic kunit test for the
purpose!
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v7] kunit: memcpy: Add benchmark
2026-11-10 3:39 ` Andrew Morton
@ 2026-11-10 10:01 ` David Laight
0 siblings, 0 replies; 4+ messages in thread
From: David Laight @ 2026-11-10 10:01 UTC (permalink / raw)
To: Andrew Morton
Cc: Matteo Croce, Brendan Higgins, David Gow, linux-kselftest,
linux-kernel, Kees Cook, linux-hardening
On Mon, 9 Nov 2026 19:39:25 -0800
Andrew Morton <akpm@linux-foundation.org> wrote:
> On Tue, 10 Nov 2026 03:16:22 +0100 Matteo Croce <technoboy85@gmail.com> wrote:
>
> > Add optional benchmarks for memcpy() and memmove(), enabled with
> > CONFIG_MEMCPY_KUNIT_BENCHMARK.
>
> hm, sorry, I must ask "why". Nowadays optimizing memcpy() is a
> super-specialized thing undertaken by the bravest architecture
> specialists who are unlikely to rely upon a generic kunit test for the
> purpose!
You also want a comparison against the 'obvious loops' (which it can
take some effort to generate because the compiler tends to convert them
to memcpy()). Otherwise the numbers are pretty meaningless.
But kernel benchmarks are really the wrong place.
The 'bravest architecture specialists' are going to test the code
in userspace where multiple algorithms can be tested and quickly
tweaked/dismissed.
You also need a bank of systems with different cpu variants and run the
code on all on them - much easier if you only need to copy a program across.
I also (sometimes being one of the 'bravest architecture specialists')
try to work out how many bytes/clock the a copy (or ipcsum) is doing
and what the fixed costs are.
That actually tells you something useful.
(But you always need to check that the compiler generated something like
the expected copy - otherwise it is pretty pointless.)
David
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-10 10:01 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10 2:16 [PATCH v7] kunit: memcpy: Add benchmark Matteo Croce
2026-11-10 2:20 ` Matteo Croce
2026-11-10 3:39 ` Andrew Morton
2026-11-10 10:01 ` David Laight
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®