[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20260129234539.18513-1-teknoraver@meta.com>
Date: Fri, 30 Jan 2026 00:45:39 +0100
From: Matteo Croce <technoboy85@...il.com>
To: linux-kernel@...r.kernel.org,
Andrew Morton <akpm@...ux-foundation.org>
Subject: [PATCH v2] KUnit: memcpy: add benchmark
Add optional benchmarks for memcpy() and memmove() functions.
Each benchmark is run twice: first with buffers aligned and then with
buffers unaligned, to spot unaligned accesses on platforms where they
have a noticeable performance impact.
Sample output:
# 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 400 MBytes in 22 msecs (18027 MB/s)
# memcpy_bench_test: memcpy: unaligned copy of 400 MBytes in 23 msecs (17360 MB/s)
# memcpy_bench_test.speed: slow
ok 7 memcpy_bench_test
# memmove_bench_test: memmove: aligned move of 399 MBytes in 17 msecs (23012 MB/s)
# memmove_bench_test: memmove: unaligned move of 399 MBytes in 17 msecs (22381 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 <teknoraver@...a.com>
---
lib/Kconfig.debug | 9 ++++
lib/tests/memcpy_kunit.c | 106 +++++++++++++++++++++++++++++++++++++++
2 files changed, 115 insertions(+)
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index ba36939fda79..02868c4397cb 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2880,6 +2880,15 @@ config MEMCPY_KUNIT_TEST
If unsure, say N.
+config MEMCPY_KUNIT_BENCHMARK
+ bool "Benchmark string functions"
+ depends on MEMCPY_KUNIT_TEST
+ help
+ A benchmark for memcpy() and memmove() functions,
+ with both aligned and unaligned buffers.
+
+ If unsure, say N.
+
config IS_SIGNED_TYPE_KUNIT_TEST
tristate "Test is_signed_type() macro" if !KUNIT_ALL_TESTS
depends on KUNIT
diff --git a/lib/tests/memcpy_kunit.c b/lib/tests/memcpy_kunit.c
index d36933554e46..100cda8d4f34 100644
--- a/lib/tests/memcpy_kunit.c
+++ b/lib/tests/memcpy_kunit.c
@@ -493,6 +493,108 @@ static void memmove_overlap_test(struct kunit *test)
}
}
+#ifdef CONFIG_MEMCPY_KUNIT_BENCHMARK
+
+#define COPY_SIZE (4 * 1024 * 1024)
+#define COPIES_NUM 100
+
+static int memcpy_bench_align(struct kunit *test, bool unalign)
+{
+ u64 start, end, total_ns = 0;
+ char *buf1;
+ char *buf2;
+ int ret = 0;
+
+ buf1 = kzalloc(COPY_SIZE, GFP_KERNEL);
+ if (!buf1)
+ return -ENOMEM;
+
+ buf2 = kzalloc(COPY_SIZE, GFP_KERNEL);
+ if (!buf2) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ for (int i = 0; i < COPIES_NUM; i++) {
+ preempt_disable();
+ start = ktime_get_ns();
+ memcpy(buf1 + unalign, buf2, COPY_SIZE - unalign);
+ end = ktime_get_ns();
+ preempt_enable();
+ total_ns += end - start;
+ }
+
+ /* Avoid division by zero */
+ if (!total_ns)
+ total_ns = 1;
+
+ kunit_info(test, "memcpy: %saligned copy of %lu MBytes in %lld msecs (%lld MB/s)\n",
+ unalign ? "un" : "",
+ (unsigned long)(COPIES_NUM * COPY_SIZE) / (1024 * 1024),
+ total_ns / 1000000,
+ (COPIES_NUM * COPY_SIZE * 1000000000ULL / total_ns) / (1024 * 1024));
+
+ kfree(buf2);
+
+out_free:
+ kfree(buf1);
+
+ return ret;
+}
+
+static void memcpy_bench_test(struct kunit *test)
+{
+ KUNIT_ASSERT_EQ_MSG(test, memcpy_bench_align(test, false), 0,
+ "aligned memcpy benchmark failed");
+ KUNIT_ASSERT_EQ_MSG(test, memcpy_bench_align(test, true), 0,
+ "unaligned memcpy benchmark failed");
+}
+
+#define POS_SHIFT (2 * PAGE_SIZE)
+
+static int memmove_bench_align(struct kunit *test, bool unalign)
+{
+ u64 start, end, total_ns = 0;
+ char *buf;
+ int ret = 0;
+
+ buf = kzalloc(COPY_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (int i = 0; i < COPIES_NUM; i++) {
+ preempt_disable();
+ start = ktime_get_ns();
+ memmove(buf + POS_SHIFT + unalign, buf, COPY_SIZE - POS_SHIFT - unalign);
+ end = ktime_get_ns();
+ preempt_enable();
+ total_ns += end - start;
+ }
+
+ if (!total_ns)
+ total_ns = 1;
+
+ kunit_info(test, "memmove: %saligned move of %lu MBytes in %lld msecs (%lld MB/s)\n",
+ unalign ? "un" : "",
+ (unsigned long)(COPIES_NUM * (COPY_SIZE - POS_SHIFT)) / (1024 * 1024),
+ total_ns / 1000000,
+ (COPIES_NUM * (COPY_SIZE - POS_SHIFT) * 1000000000ULL / total_ns) /
+ (1024 * 1024));
+
+ kfree(buf);
+
+ return ret;
+}
+
+static void memmove_bench_test(struct kunit *test)
+{
+ KUNIT_ASSERT_EQ_MSG(test, memmove_bench_align(test, false), 0,
+ "aligned memmove benchmark failed");
+ KUNIT_ASSERT_EQ_MSG(test, memmove_bench_align(test, true), 0,
+ "unaligned memmove benchmark failed");
+}
+#endif
+
static struct kunit_case memcpy_test_cases[] = {
KUNIT_CASE(memset_test),
KUNIT_CASE(memcpy_test),
@@ -500,6 +602,10 @@ static struct kunit_case memcpy_test_cases[] = {
KUNIT_CASE_SLOW(memmove_test),
KUNIT_CASE_SLOW(memmove_large_test),
KUNIT_CASE_SLOW(memmove_overlap_test),
+#ifdef CONFIG_MEMCPY_KUNIT_BENCHMARK
+ KUNIT_CASE_SLOW(memcpy_bench_test),
+ KUNIT_CASE_SLOW(memmove_bench_test),
+#endif
{}
};
--
2.52.0
Powered by blists - more mailing lists