* [patch v3] mm: test_vmstat: add synthetic benchmark for vm stats
@ 2026-09-12 1:08 David Rientjes
2026-09-12 5:37 ` Andrew Morton
0 siblings, 1 reply; 2+ messages in thread
From: David Rientjes @ 2026-09-12 1:08 UTC (permalink / raw)
To: David Hildenbrand (Arm), Andrew Morton, Christoph Lameter
Cc: Vlastimil Babka, Mathieu Desnoyers, linux-mm, linux-kernel,
Sarthak Sharma
From: Christoph Lameter <cl@gentwo.org>
Add a synthetic benchmark that can be used to measure performance of VM
statistics. This is used to analyze any improvements or regressions in
functions that are frequently used in hot code paths.
The test is run by KUnit or doing modprobe vmstat_kunit directly.
Assisted-by: Gemini:gemini-3.8-flash
Signed-off-by: Christoph Lameter <cl@gentwo.org>
Signed-off-by: David Rientjes <rientjes@google.com>
---
v3:
- switch to KUnit per David
MAINTAINERS | 1 +
mm/Kconfig | 11 +++
mm/Makefile | 1 +
mm/tests/vmstat_kunit.c | 160 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 173 insertions(+)
create mode 100644 mm/tests/vmstat_kunit.c
diff --git a/MAINTAINERS b/MAINTAINERS
index cc6a2d0e0d38..885c68a79490 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -17161,6 +17161,7 @@ F: mm/ptdump.c
F: mm/sparse-vmemmap.c
F: mm/sparse.c
F: mm/sparse.h
+F: mm/tests/vmstat_kunit.c
F: mm/util.c
F: mm/vmpressure.c
F: mm/vmstat.c
diff --git a/mm/Kconfig b/mm/Kconfig
index 604c58199acb..2d021fb5ace6 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -1511,6 +1511,17 @@ config LAZY_MMU_MODE_KUNIT_TEST
If unsure, say N.
+config VMSTAT_KUNIT_TEST
+ tristate "KUnit test for VM statistics" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ Enable this option to test and benchmark the performance of VM
+ statistics updates (zone page state and VM event counters), which
+ are used frequently in hot memory management code paths.
+
+ If unsure, say N.
+
source "mm/damon/Kconfig"
endmenu
diff --git a/mm/Makefile b/mm/Makefile
index e7245cb88c66..3d4f2c43b8d3 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -147,4 +147,5 @@ obj-$(CONFIG_SHRINKER_DEBUG) += shrinker_debug.o
obj-$(CONFIG_EXECMEM) += execmem.o
obj-$(CONFIG_TMPFS_QUOTA) += shmem_quota.o
obj-$(CONFIG_LAZY_MMU_MODE_KUNIT_TEST) += tests/lazy_mmu_mode_kunit.o
+obj-$(CONFIG_VMSTAT_KUNIT_TEST) += tests/vmstat_kunit.o
obj-$(CONFIG_MEM_ALLOC_PROFILING) += alloc_tag.o
diff --git a/mm/tests/vmstat_kunit.c b/mm/tests/vmstat_kunit.c
new file mode 100644
index 000000000000..5462e3d02800
--- /dev/null
+++ b/mm/tests/vmstat_kunit.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit synthetic performance benchmark for VM statistics.
+ *
+ * (C) 2009 Linux Foundation, Christoph Lameter <cl@gentwo.org>
+ * (C) 2026 Google LLC, David Rientjes <rientjes@google.com>
+ */
+#include <kunit/test.h>
+#include <linux/mm.h>
+#include <linux/vmstat.h>
+#include <linux/timex.h>
+#include <linux/ktime.h>
+#include <linux/math64.h>
+
+#define TEST_COUNT 10000
+
+static void vmstat_test_free_page(void *arg)
+{
+ __free_page((struct page *)arg);
+}
+
+/*
+ * Test 1: Sequential inc_zone_page_state() followed by dec_zone_page_state().
+ * Net change to zone counters is 0.
+ */
+static void vmstat_test_inc_dec_zone_page_state(struct kunit *test)
+{
+ struct page *page;
+ cycles_t time1, time2, time;
+ u64 t1_ns, t2_ns;
+ u64 inc_cycles, dec_cycles;
+ u64 inc_ns, dec_ns;
+ unsigned int i;
+ int rem;
+
+ page = alloc_page(GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, page);
+ KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, vmstat_test_free_page, page), 0);
+
+ /* Benchmark inc_zone_page_state() */
+ time1 = get_cycles();
+ t1_ns = ktime_get_ns();
+ for (i = 0; i < TEST_COUNT; i++)
+ inc_zone_page_state(page, NR_FREE_CMA_PAGES);
+ t2_ns = ktime_get_ns();
+ time2 = get_cycles();
+
+ time = time2 - time1;
+ inc_cycles = div_u64_rem(time, TEST_COUNT, &rem);
+ inc_ns = div_u64(t2_ns - t1_ns, TEST_COUNT);
+
+ /* Benchmark dec_zone_page_state() */
+ time1 = get_cycles();
+ t1_ns = ktime_get_ns();
+ for (i = 0; i < TEST_COUNT; i++)
+ dec_zone_page_state(page, NR_FREE_CMA_PAGES);
+ t2_ns = ktime_get_ns();
+ time2 = get_cycles();
+
+ time = time2 - time1;
+ dec_cycles = div_u64_rem(time, TEST_COUNT, &rem);
+ dec_ns = div_u64(t2_ns - t1_ns, TEST_COUNT);
+
+ if (inc_cycles || dec_cycles)
+ kunit_info(test, "%u ops: inc_zone_page_state -> %llu cycles (%llu ns/op), dec_zone_page_state -> %llu cycles (%llu ns/op)\n",
+ TEST_COUNT, inc_cycles, inc_ns, dec_cycles, dec_ns);
+ else
+ kunit_info(test, "%u ops: inc_zone_page_state -> %llu ns/op, dec_zone_page_state -> %llu ns/op\n",
+ TEST_COUNT, inc_ns, dec_ns);
+}
+
+/*
+ * Test 2: Paired inc_zone_page_state() and dec_zone_page_state().
+ * Net change to zone counters is 0.
+ */
+static void vmstat_test_interleaved_zone_page_state(struct kunit *test)
+{
+ struct page *page;
+ cycles_t time1, time2, time;
+ u64 t1_ns, t2_ns;
+ u64 avg_cycles, avg_ns;
+ unsigned int i;
+ int rem;
+
+ page = alloc_page(GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, page);
+ KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, vmstat_test_free_page, page), 0);
+
+ time1 = get_cycles();
+ t1_ns = ktime_get_ns();
+ for (i = 0; i < TEST_COUNT; i++) {
+ inc_zone_page_state(page, NR_FREE_CMA_PAGES);
+ dec_zone_page_state(page, NR_FREE_CMA_PAGES);
+ }
+ t2_ns = ktime_get_ns();
+ time2 = get_cycles();
+
+ time = time2 - time1;
+ avg_cycles = div_u64_rem(time, TEST_COUNT, &rem);
+ avg_ns = div_u64(t2_ns - t1_ns, TEST_COUNT);
+
+ if (avg_cycles)
+ kunit_info(test, "%u ops: inc/dec pair -> %llu cycles (%llu ns/op)\n",
+ TEST_COUNT, avg_cycles, avg_ns);
+ else
+ kunit_info(test, "%u ops: inc/dec pair -> %llu ns/op\n",
+ TEST_COUNT, avg_ns);
+}
+
+/*
+ * Test 3: count_vm_event() benchmark.
+ * Restores counter balance with count_vm_events(item, -TEST_COUNT).
+ */
+static void vmstat_test_count_vm_event(struct kunit *test)
+{
+ cycles_t time1, time2, time;
+ u64 t1_ns, t2_ns;
+ u64 avg_cycles, avg_ns;
+ unsigned int i;
+ int rem;
+
+ time1 = get_cycles();
+ t1_ns = ktime_get_ns();
+ for (i = 0; i < TEST_COUNT; i++)
+ count_vm_event(SLABS_SCANNED);
+ t2_ns = ktime_get_ns();
+ time2 = get_cycles();
+
+ /* Restore balance */
+ count_vm_events(SLABS_SCANNED, -TEST_COUNT);
+
+ time = time2 - time1;
+ avg_cycles = div_u64_rem(time, TEST_COUNT, &rem);
+ avg_ns = div_u64(t2_ns - t1_ns, TEST_COUNT);
+
+ if (avg_cycles)
+ kunit_info(test, "%u ops: count_vm_event -> %llu cycles (%llu ns/op)\n",
+ TEST_COUNT, avg_cycles, avg_ns);
+ else
+ kunit_info(test, "%u ops: count_vm_event -> %llu ns/op\n",
+ TEST_COUNT, avg_ns);
+}
+
+static struct kunit_case vmstat_test_cases[] = {
+ KUNIT_CASE(vmstat_test_inc_dec_zone_page_state),
+ KUNIT_CASE(vmstat_test_interleaved_zone_page_state),
+ KUNIT_CASE(vmstat_test_count_vm_event),
+ {}
+};
+
+static struct kunit_suite vmstat_test_suite = {
+ .name = "vmstat",
+ .test_cases = vmstat_test_cases,
+};
+kunit_test_suite(vmstat_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Christoph Lameter <cl@gentwo.org>");
+MODULE_AUTHOR("David Rientjes <rientjes@google.com>");
+MODULE_DESCRIPTION("KUnit benchmark test for VM statistics");
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-12 5:37 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-12 1:08 [patch v3] mm: test_vmstat: add synthetic benchmark for vm stats David Rientjes
2026-09-12 5:37 ` 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®