* [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; 3+ 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] 3+ 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; 3+ 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] 3+ 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; 3+ 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] 3+ messages in thread
end of thread, other threads:[~2026-11-10 3:39 UTC | newest]
Thread overview: 3+ 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
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®