From: Matteo Croce <technoboy85@gmail.com>
To: Brendan Higgins <brendan.higgins@linux.dev>,
David Gow <davidgow@google.com>,
Andrew Morton <akpm@linux-foundation.org>,
David Laight <david.laight.linux@gmail.com>
Cc: linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v7] kunit: memcpy: Add benchmark
Date: Tue, 10 Nov 2026 03:16:22 +0100 [thread overview]
Message-ID: <20261110021622.122490-1-technoboy85@gmail.com> (raw)
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
next reply other threads:[~2026-11-10 2:16 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 2:16 Matteo Croce [this message]
2026-11-10 2:20 ` [PATCH v7] kunit: memcpy: Add benchmark Matteo Croce
2026-11-10 3:39 ` Andrew Morton
2026-11-10 10:01 ` David Laight
2026-11-11 17:19 ` Matteo Croce
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261110021622.122490-1-technoboy85@gmail.com \
--to=technoboy85@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=brendan.higgins@linux.dev \
--cc=david.laight.linux@gmail.com \
--cc=davidgow@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.