]> www.infradead.org Git - users/hch/misc.git/commitdiff
perf bench mem: Add mmap() workloads
authorAnkur Arora <ankur.a.arora@oracle.com>
Wed, 17 Sep 2025 15:24:11 +0000 (08:24 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 19 Sep 2025 15:43:59 +0000 (12:43 -0300)
Add two mmap() workloads: one that eagerly populates a region and
another that demand faults it in.

The intent is to probe the memory subsytem performance incurred
by mmap().

  $ perf bench mem mmap -s 4gb -p 4kb -l 10 -f populate
  # Running 'mem/mmap' benchmark:
  # function 'populate' (Eagerly populated map())
  # Copying 4gb bytes ...

       1.811691 GB/sec

  $ perf bench mem mmap -s 4gb -p 2mb -l 10 -f populate
  # Running 'mem/mmap' benchmark:
  # function 'populate' (Eagerly populated mmap())
  # Copying 4gb bytes ...

      12.272017 GB/sec

  $ perf bench mem mmap -s 4gb -p 1gb -l 10 -f populate
  # Running 'mem/mmap' benchmark:
  # function 'populate' (Eagerly populated mmap())
  # Copying 4gb bytes ...

      17.085927 GB/sec

Signed-off-by: Ankur Arora <ankur.a.arora@oracle.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Mateusz Guzik <mjguzik@gmail.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Raghavendra K T <raghavendra.kt@amd.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/Documentation/perf-bench.txt
tools/perf/bench/bench.h
tools/perf/bench/mem-functions.c
tools/perf/builtin-bench.c

index 3d1455d880c3a2b875223c9ba5aa194f7673a7c0..1160224cb718392d4e6ca79813e18cd5b88646bf 100644 (file)
@@ -240,6 +240,40 @@ Repeat memset invocation this number of times.
 --cycles::
 Use perf's cpu-cycles event instead of gettimeofday syscall.
 
+*mmap*::
+Suite for evaluating memory subsystem performance for mmap()'d memory.
+
+Options of *mmap*
+^^^^^^^^^^^^^^^^^
+-s::
+--size::
+Specify size of memory to set (default: 1MB).
+Available units are B, KB, MB, GB and TB (case insensitive).
+
+-p::
+--page::
+Specify page-size for mapping memory buffers (default: 4KB).
+Available values are 4KB, 2MB, 1GB (case insensitive).
+
+-r::
+--randomize::
+Specify seed to randomize page access offset (default: 0, or not randomized).
+
+-f::
+--function::
+Specify function to set (default: all).
+Available functions are 'demand' and 'populate', with the first
+demand faulting pages in the region and the second using an eager
+mapping.
+
+-l::
+--nr_loops::
+Repeat mmap() invocation this number of times.
+
+-c::
+--cycles::
+Use perf's cpu-cycles event instead of gettimeofday syscall.
+
 SUITES FOR 'numa'
 ~~~~~~~~~~~~~~~~~
 *mem*::
index 9f736423af53c31b30d2e52d8ae967b831a118bc..8519eb5a42faf9a86686c08d1a566c2a2bc34dd9 100644 (file)
@@ -28,6 +28,7 @@ int bench_syscall_fork(int argc, const char **argv);
 int bench_syscall_execve(int argc, const char **argv);
 int bench_mem_memcpy(int argc, const char **argv);
 int bench_mem_memset(int argc, const char **argv);
+int bench_mem_mmap(int argc, const char **argv);
 int bench_mem_find_bit(int argc, const char **argv);
 int bench_futex_hash(int argc, const char **argv);
 int bench_futex_wake(int argc, const char **argv);
index 2a23bed8c2d39dd1c89a3ecfc0fe9e3ea00e3a8e..2908a3a796c932d07d77dc070842569036bd3d4a 100644 (file)
@@ -40,6 +40,7 @@ static const char     *chunk_size_str = "0";
 static unsigned int    nr_loops        = 1;
 static bool            use_cycles;
 static int             cycles_fd;
+static unsigned int    seed;
 
 static const struct option bench_common_options[] = {
        OPT_STRING('s', "size", &size_str, "1MB",
@@ -81,6 +82,7 @@ struct bench_params {
        size_t          chunk_size;
        unsigned int    nr_loops;
        unsigned int    page_shift;
+       unsigned int    seed;
 };
 
 struct bench_mem_info {
@@ -98,6 +100,7 @@ typedef void (*mem_fini_t)(struct bench_mem_info *, struct bench_params *,
                           void **, void **);
 typedef void *(*memcpy_t)(void *, const void *, size_t);
 typedef void *(*memset_t)(void *, int, size_t);
+typedef void (*mmap_op_t)(void *, size_t, unsigned int, bool);
 
 struct function {
        const char *name;
@@ -108,6 +111,7 @@ struct function {
                union {
                        memcpy_t memcpy;
                        memset_t memset;
+                       mmap_op_t mmap_op;
                };
        } fn;
 };
@@ -160,6 +164,14 @@ static union bench_clock clock_diff(union bench_clock *s, union bench_clock *e)
        return t;
 }
 
+static void clock_accum(union bench_clock *a, union bench_clock *b)
+{
+       if (use_cycles)
+               a->cycles += b->cycles;
+       else
+               timeradd(&a->tv, &b->tv, &a->tv);
+}
+
 static double timeval2double(struct timeval *ts)
 {
        return (double)ts->tv_sec + (double)ts->tv_usec / (double)USEC_PER_SEC;
@@ -271,6 +283,8 @@ static int bench_mem_common(int argc, const char **argv, struct bench_mem_info *
        }
        p.page_shift = ilog2(page_size);
 
+       p.seed = seed;
+
        if (!strncmp(function_str, "all", 3)) {
                for (i = 0; info->functions[i].name; i++)
                        __bench_mem_function(info, &p, i);
@@ -465,3 +479,85 @@ int bench_mem_memset(int argc, const char **argv)
 
        return bench_mem_common(argc, argv, &info);
 }
+
+static void mmap_page_touch(void *dst, size_t size, unsigned int page_shift, bool random)
+{
+       unsigned long npages = size / (1 << page_shift);
+       unsigned long offset = 0, r = 0;
+
+       for (unsigned long i = 0; i < npages; i++) {
+               if (random)
+                       r = rand() % (1 << page_shift);
+
+               *((char *)dst + offset + r) = *(char *)(dst + offset + r) + i;
+               offset += 1 << page_shift;
+       }
+}
+
+static int do_mmap(const struct function *r, struct bench_params *p,
+                 void *src __maybe_unused, void *dst __maybe_unused,
+                 union bench_clock *accum)
+{
+       union bench_clock start, end, diff;
+       mmap_op_t fn = r->fn.mmap_op;
+       bool populate = strcmp(r->name, "populate") == 0;
+
+       if (p->seed)
+               srand(p->seed);
+
+       for (unsigned int i = 0; i < p->nr_loops; i++) {
+               clock_get(&start);
+               dst = bench_mmap(p->size, populate, p->page_shift);
+               if (!dst)
+                       goto out;
+
+               fn(dst, p->size, p->page_shift, p->seed);
+               clock_get(&end);
+               diff = clock_diff(&start, &end);
+               clock_accum(accum, &diff);
+
+               bench_munmap(dst, p->size);
+       }
+
+       return 0;
+out:
+       printf("# Memory allocation failed - maybe size (%s) %s?\n", size_str,
+                       p->page_shift != PAGE_SHIFT_4KB ? "has insufficient hugepages" : "is too large");
+       return -1;
+}
+
+static const char * const bench_mem_mmap_usage[] = {
+       "perf bench mem mmap <options>",
+       NULL
+};
+
+static const struct function mmap_functions[] = {
+       { .name         = "demand",
+         .desc         = "Demand loaded mmap()",
+         .fn.mmap_op   = mmap_page_touch },
+
+       { .name         = "populate",
+         .desc         = "Eagerly populated mmap()",
+         .fn.mmap_op   = mmap_page_touch },
+
+       { .name = NULL, }
+};
+
+int bench_mem_mmap(int argc, const char **argv)
+{
+       static const struct option bench_mmap_options[] = {
+               OPT_UINTEGER('r', "randomize", &seed,
+                           "Seed to randomize page access offset."),
+               OPT_PARENT(bench_common_options),
+               OPT_END()
+       };
+
+       struct bench_mem_info info = {
+               .functions              = mmap_functions,
+               .do_op                  = do_mmap,
+               .usage                  = bench_mem_mmap_usage,
+               .options                = bench_mmap_options,
+       };
+
+       return bench_mem_common(argc, argv, &info);
+}
index 2c1a9f3d847a2132eb1336a6d804c4e09889d249..02dea1b8822864e8be47edf962025325004c00b0 100644 (file)
@@ -65,6 +65,7 @@ static struct bench mem_benchmarks[] = {
        { "memcpy",     "Benchmark for memcpy() functions",             bench_mem_memcpy        },
        { "memset",     "Benchmark for memset() functions",             bench_mem_memset        },
        { "find_bit",   "Benchmark for find_bit() functions",           bench_mem_find_bit      },
+       { "mmap",       "Benchmark for mmap() mappings",                bench_mem_mmap          },
        { "all",        "Run all memory access benchmarks",             NULL                    },
        { NULL,         NULL,                                           NULL                    }
 };