perf bench: Add pmu-scan benchmark
authorNamhyung Kim <namhyung@kernel.org>
Fri, 31 Mar 2023 20:29:44 +0000 (13:29 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 4 Apr 2023 16:23:58 +0000 (13:23 -0300)
The pmu-scan benchmark will repeatedly scan the sysfs to get the
available PMU information.

  $ ./perf bench internals pmu-scan
  # Running 'internals/pmu-scan' benchmark:
  Computing performance of sysfs PMU event scan for 100 times
    Average PMU scanning took: 6850.990 usec (+- 48.445 usec)

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20230331202949.810326-2-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/bench/Build
tools/perf/bench/bench.h
tools/perf/bench/pmu-scan.c [new file with mode: 0644]
tools/perf/builtin-bench.c

index 6b6155a..0f158dc 100644 (file)
@@ -15,6 +15,7 @@ perf-y += find-bit-bench.o
 perf-y += inject-buildid.o
 perf-y += evlist-open-close.o
 perf-y += breakpoint.o
+perf-y += pmu-scan.o
 
 perf-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
 perf-$(CONFIG_X86_64) += mem-memset-x86-64-asm.o
index edfc257..0d2b659 100644 (file)
@@ -42,6 +42,7 @@ int bench_inject_build_id(int argc, const char **argv);
 int bench_evlist_open_close(int argc, const char **argv);
 int bench_breakpoint_thread(int argc, const char **argv);
 int bench_breakpoint_enable(int argc, const char **argv);
+int bench_pmu_scan(int argc, const char **argv);
 
 #define BENCH_FORMAT_DEFAULT_STR       "default"
 #define BENCH_FORMAT_DEFAULT           0
diff --git a/tools/perf/bench/pmu-scan.c b/tools/perf/bench/pmu-scan.c
new file mode 100644 (file)
index 0000000..f0f0078
--- /dev/null
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Benchmark scanning sysfs files for PMU information.
+ *
+ * Copyright 2023 Google LLC.
+ */
+#include <stdio.h>
+#include "bench.h"
+#include "util/debug.h"
+#include "util/pmu.h"
+#include "util/pmus.h"
+#include "util/stat.h"
+#include <linux/atomic.h>
+#include <linux/err.h>
+#include <linux/time64.h>
+#include <subcmd/parse-options.h>
+
+static unsigned int iterations = 100;
+
+struct pmu_scan_result {
+       char *name;
+       int nr_aliases;
+       int nr_formats;
+       int nr_caps;
+};
+
+static const struct option options[] = {
+       OPT_UINTEGER('i', "iterations", &iterations,
+               "Number of iterations used to compute average"),
+       OPT_END()
+};
+
+static const char *const bench_usage[] = {
+       "perf bench internals pmu-scan <options>",
+       NULL
+};
+
+static int nr_pmus;
+static struct pmu_scan_result *results;
+
+static int save_result(void)
+{
+       struct perf_pmu *pmu;
+       struct list_head *list;
+       struct pmu_scan_result *r;
+
+       perf_pmu__scan(NULL);
+
+       perf_pmus__for_each_pmu(pmu) {
+               r = realloc(results, (nr_pmus + 1) * sizeof(*r));
+               if (r == NULL)
+                       return -ENOMEM;
+
+               results = r;
+               r = results + nr_pmus;
+
+               r->name = strdup(pmu->name);
+               r->nr_caps = pmu->nr_caps;
+
+               r->nr_aliases = 0;
+               list_for_each(list, &pmu->aliases)
+                       r->nr_aliases++;
+
+               r->nr_formats = 0;
+               list_for_each(list, &pmu->format)
+                       r->nr_formats++;
+
+               pr_debug("pmu[%d] name=%s, nr_caps=%d, nr_aliases=%d, nr_formats=%d\n",
+                       nr_pmus, r->name, r->nr_caps, r->nr_aliases, r->nr_formats);
+               nr_pmus++;
+       }
+
+       perf_pmu__destroy();
+       return 0;
+}
+
+static int check_result(void)
+{
+       struct pmu_scan_result *r;
+       struct perf_pmu *pmu;
+       struct list_head *list;
+       int nr;
+
+       for (int i = 0; i < nr_pmus; i++) {
+               r = &results[i];
+               pmu = perf_pmu__find(r->name);
+               if (pmu == NULL) {
+                       pr_err("Cannot find PMU %s\n", r->name);
+                       return -1;
+               }
+
+               if (pmu->nr_caps != (u32)r->nr_caps) {
+                       pr_err("Unmatched number of event caps in %s: expect %d vs got %d\n",
+                               pmu->name, r->nr_caps, pmu->nr_caps);
+                       return -1;
+               }
+
+               nr = 0;
+               list_for_each(list, &pmu->aliases)
+                       nr++;
+               if (nr != r->nr_aliases) {
+                       pr_err("Unmatched number of event aliases in %s: expect %d vs got %d\n",
+                               pmu->name, r->nr_aliases, nr);
+                       return -1;
+               }
+
+               nr = 0;
+               list_for_each(list, &pmu->format)
+                       nr++;
+               if (nr != r->nr_formats) {
+                       pr_err("Unmatched number of event formats in %s: expect %d vs got %d\n",
+                               pmu->name, r->nr_formats, nr);
+                       return -1;
+               }
+       }
+       return 0;
+}
+
+static void delete_result(void)
+{
+       for (int i = 0; i < nr_pmus; i++)
+               free(results[i].name);
+       free(results);
+
+       results = NULL;
+       nr_pmus = 0;
+}
+
+static int run_pmu_scan(void)
+{
+       struct stats stats;
+       struct timeval start, end, diff;
+       double time_average, time_stddev;
+       u64 runtime_us;
+       unsigned int i;
+       int ret;
+
+       init_stats(&stats);
+       pr_info("Computing performance of sysfs PMU event scan for %u times\n",
+               iterations);
+
+       if (save_result() < 0) {
+               pr_err("Failed to initialize PMU scan result\n");
+               return -1;
+       }
+
+       for (i = 0; i < iterations; i++) {
+               gettimeofday(&start, NULL);
+               perf_pmu__scan(NULL);
+               gettimeofday(&end, NULL);
+
+               timersub(&end, &start, &diff);
+               runtime_us = diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+               update_stats(&stats, runtime_us);
+
+               ret = check_result();
+               perf_pmu__destroy();
+               if (ret < 0)
+                       break;
+       }
+
+       time_average = avg_stats(&stats);
+       time_stddev = stddev_stats(&stats);
+       pr_info("  Average PMU scanning took: %.3f usec (+- %.3f usec)\n",
+               time_average, time_stddev);
+
+       delete_result();
+       return 0;
+}
+
+int bench_pmu_scan(int argc, const char **argv)
+{
+       int err = 0;
+
+       argc = parse_options(argc, argv, options, bench_usage, 0);
+       if (argc) {
+               usage_with_options(bench_usage, options);
+               exit(EXIT_FAILURE);
+       }
+
+       err = run_pmu_scan();
+
+       return err;
+}
index d0fcc3c..58f1cfe 100644 (file)
@@ -92,6 +92,7 @@ static struct bench internals_benchmarks[] = {
        { "kallsyms-parse", "Benchmark kallsyms parsing",       bench_kallsyms_parse    },
        { "inject-build-id", "Benchmark build-id injection",    bench_inject_build_id   },
        { "evlist-open-close", "Benchmark evlist open and close",       bench_evlist_open_close },
+       { "pmu-scan", "Benchmark sysfs PMU info scanning",      bench_pmu_scan          },
        { NULL,         NULL,                                   NULL                    }
 };