From: Ian Rogers <irogers@google.com>
To: irogers@google.com, acme@kernel.org, haghdoost@uber.com,
namhyung@kernel.org
Cc: adrian.hunter@intel.com, james.clark@linaro.org,
jolsa@kernel.org, linux-kernel@vger.kernel.org,
linux-perf-users@vger.kernel.org, mingo@redhat.com,
peterz@infradead.org
Subject: [PATCH v3 09/20] perf bench internals: Add symbols benchmark
Date: Tue, 10 Nov 2026 14:07:52 -0800 [thread overview]
Message-ID: <20261110220803.2503416-10-irogers@google.com> (raw)
In-Reply-To: <20261110220803.2503416-1-irogers@google.com>
Add a 'perf bench internals symbols' subcommand to measure symbol
loading, sorting, and lookup operations directly:
1. Loads symbols from an ELF binary (defaulting to the running perf
executable, or specified via -b/--binary) without sorting by name,
reporting average load runtime and maximum RSS via getrusage().
2. Sorts the loaded DSO symbols by name (dso__sort_by_name()),
reporting average sort runtime and maximum RSS separately so
address-only/lazy symbol loading can be evaluated independently of
symbol name sorting.
3. Inserts synthetic symbols in two ascending runs (simulating .symtab
and .dynsym), fixes up symbol ends and duplicates, and queries each
symbol by address.
4. Incrementally appends symbols at interior addresses and immediately
queries each appended symbol by address.
Example output:
$ perf bench internals symbols
# Running 'internals/symbols' benchmark:
Average DSO load: 9.174 msec (+- 0.220 msec)
Max RSS (DSO load): 75640 KB
Average DSO sort by name: 2.787 msec (+- 0.019 msec)
Max RSS (with sort by name): 75640 KB
Average bulk insert + lookup: 2030.050 usec (+- 13.376 usec)
Average append + lookup: 54.650 usec (+- 1.466 usec)
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/Documentation/perf-bench.txt | 3 +
tools/perf/bench/Build | 1 +
tools/perf/bench/bench.h | 1 +
tools/perf/bench/symbols.c | 273 ++++++++++++++++++++++++
tools/perf/builtin-bench.c | 1 +
5 files changed, 279 insertions(+)
create mode 100644 tools/perf/bench/symbols.c
diff --git a/tools/perf/Documentation/perf-bench.txt b/tools/perf/Documentation/perf-bench.txt
index 7f820f69740f..54eb809540c4 100644
--- a/tools/perf/Documentation/perf-bench.txt
+++ b/tools/perf/Documentation/perf-bench.txt
@@ -351,6 +351,9 @@ SUITES FOR 'internals'
*synthesize*::
Suite for evaluating perf's event synthesis performance.
+*symbols*::
+Suite for evaluating ELF symbol loading, sorting, and lookup performance.
+
SEE ALSO
--------
linkperf:perf[1]
diff --git a/tools/perf/bench/Build b/tools/perf/bench/Build
index c64c52468d3d..c3c0319696ae 100644
--- a/tools/perf/bench/Build
+++ b/tools/perf/bench/Build
@@ -20,6 +20,7 @@ perf-bench-y += evlist-open-close.o
perf-bench-y += breakpoint.o
perf-bench-y += pmu-scan.o
perf-bench-y += uprobe.o
+perf-bench-y += symbols.o
perf-bench-$(CONFIG_X86_64) += mem-memcpy-x86-64-asm.o
perf-bench-$(CONFIG_X86_64) += mem-memset-x86-64-asm.o
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index 85e3ac471875..e0b35d45559a 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -52,6 +52,7 @@ int bench_uprobe_trace_printk(int argc, const char **argv);
int bench_uprobe_empty_ret(int argc, const char **argv);
int bench_uprobe_trace_printk_ret(int argc, const char **argv);
int bench_pmu_scan(int argc, const char **argv);
+int bench_symbols(int argc, const char **argv);
#define BENCH_FORMAT_DEFAULT_STR "default"
#define BENCH_FORMAT_DEFAULT 0
diff --git a/tools/perf/bench/symbols.c b/tools/perf/bench/symbols.c
new file mode 100644
index 000000000000..c5043fc08198
--- /dev/null
+++ b/tools/perf/bench/symbols.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Benchmark symbol loading, sorting, and address/name lookup.
+ */
+#include <elf.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/time64.h>
+#include <subcmd/parse-options.h>
+#include "bench.h"
+#include "../util/debug.h"
+#include "../util/dso.h"
+#include "../util/map.h"
+#include "../util/stat.h"
+#include "../util/symbol.h"
+#include "../util/util.h"
+
+static unsigned int iterations = 20;
+static unsigned int nr_symbols = 10000;
+static unsigned int nr_appends = 200;
+static const char *binary_path;
+static bool no_demangle;
+
+static const struct option options[] = {
+ OPT_UINTEGER('i', "iterations", &iterations,
+ "Number of iterations used to compute average (default: 20)"),
+ OPT_STRING('b', "binary", &binary_path, "path",
+ "Binary path for ELF symbol load benchmark (default: perf binary)"),
+ OPT_UINTEGER('n', "nr-symbols", &nr_symbols,
+ "Number of synthetic symbols for bulk benchmark (default: 10000)"),
+ OPT_UINTEGER('a', "nr-appends", &nr_appends,
+ "Number of incremental symbol appends per iteration (default: 200)"),
+ OPT_BOOLEAN(0, "no-demangle", &no_demangle,
+ "Disable symbol demangling during ELF load"),
+ OPT_END()
+};
+
+static const char *const bench_usage[] = {
+ "perf bench internals symbols <options>",
+ NULL
+};
+
+static int do_dso_load_bench(const char *path, bool *skipped)
+{
+ struct stats time_stats;
+ struct rusage rusage;
+ unsigned int i;
+
+ *skipped = false;
+ init_stats(&time_stats);
+
+ for (i = 0; i < iterations; i++) {
+ struct timeval start, end, diff;
+ struct map *map;
+ struct dso *dso;
+ u64 runtime_us;
+ int nr;
+
+ gettimeofday(&start, NULL);
+ map = dso__new_map(path);
+ if (!map) {
+ pr_err("Failed to create map for %s\n", path);
+ return -ENOMEM;
+ }
+ dso = map__dso(map);
+ nr = dso__load(dso, map);
+ if (nr < 0) {
+ pr_err("Failed to load symbols from %s\n", path);
+ map__put(map);
+ return -EINVAL;
+ }
+ if (nr == 0) {
+ printf(" Skipping DSO load benchmark (%s has no symbols)\n", path);
+ map__put(map);
+ *skipped = true;
+ return 0;
+ }
+ gettimeofday(&end, NULL);
+
+ map__put(map);
+
+ timersub(&end, &start, &diff);
+ runtime_us = (u64)diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&time_stats, runtime_us);
+ }
+
+ printf(" Average DSO load: %.3f msec (+- %.3f msec)\n",
+ avg_stats(&time_stats) / USEC_PER_MSEC,
+ stddev_stats(&time_stats) / USEC_PER_MSEC);
+ if (getrusage(RUSAGE_SELF, &rusage) == 0)
+ printf(" Max RSS (DSO load): %ld KB\n", rusage.ru_maxrss);
+ return 0;
+}
+
+static int do_dso_sort_by_name_bench(const char *path)
+{
+ struct stats time_stats;
+ struct rusage rusage;
+ unsigned int i;
+
+ init_stats(&time_stats);
+
+ for (i = 0; i < iterations; i++) {
+ struct timeval start, end, diff;
+ struct map *map;
+ struct dso *dso;
+ u64 runtime_us;
+
+ map = dso__new_map(path);
+ if (!map) {
+ pr_err("Failed to create map for %s\n", path);
+ return -ENOMEM;
+ }
+ dso = map__dso(map);
+ if (dso__load(dso, map) <= 0) {
+ map__put(map);
+ return -EINVAL;
+ }
+
+ gettimeofday(&start, NULL);
+ dso__sort_by_name(dso);
+ gettimeofday(&end, NULL);
+
+ map__put(map);
+
+ timersub(&end, &start, &diff);
+ runtime_us = (u64)diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&time_stats, runtime_us);
+ }
+
+ printf(" Average DSO sort by name: %.3f msec (+- %.3f msec)\n",
+ avg_stats(&time_stats) / USEC_PER_MSEC,
+ stddev_stats(&time_stats) / USEC_PER_MSEC);
+ if (getrusage(RUSAGE_SELF, &rusage) == 0)
+ printf(" Max RSS (with sort by name): %ld KB\n", rusage.ru_maxrss);
+ return 0;
+}
+
+static int do_synthetic_symbols_bench(void)
+{
+ struct stats bulk_stats, append_stats;
+ unsigned int i, j, half = nr_symbols / 2;
+
+ if (half == 0)
+ return 0;
+
+ init_stats(&bulk_stats);
+ init_stats(&append_stats);
+
+ for (i = 0; i < iterations; i++) {
+ struct timeval start, end, diff;
+ struct dso *dso = dso__new("[bench_symbols]");
+ u64 runtime_us;
+ char name[32];
+
+ if (!dso)
+ return -ENOMEM;
+
+ /*
+ * Phase 1: Insert symbols in two ascending runs (simulating
+ * .symtab + .dynsym), fixup ends and duplicates, and look up
+ * all symbols by address.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_symbols; j++) {
+ u64 addr = (j < half) ?
+ (0x100000ULL + (u64)j * 0x40ULL) :
+ (0x100020ULL + (u64)(j - half) * 0x40ULL);
+ struct symbol *sym;
+
+ scnprintf(name, sizeof(name), "bench_sym_%06u", j);
+ sym = symbol__new(addr, 0x18, STB_GLOBAL, STT_FUNC, name);
+ if (!sym) {
+ dso__put(dso);
+ return -ENOMEM;
+ }
+ symbols__insert(dso__symbols(dso), sym);
+ }
+ symbols__fixup_end(dso__symbols(dso), false);
+ symbols__fixup_duplicate(dso__symbols(dso));
+
+ for (j = 0; j < nr_symbols; j++) {
+ u64 addr = (j < half) ?
+ (0x100000ULL + (u64)j * 0x40ULL + 4) :
+ (0x100020ULL + (u64)(j - half) * 0x40ULL + 4);
+
+ (void)dso__find_symbol_nocache(dso, addr);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = (u64)diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&bulk_stats, runtime_us);
+
+ /*
+ * Phase 2: Incrementally append one symbol at a time at an
+ * interior address and immediately query by address.
+ */
+ gettimeofday(&start, NULL);
+ for (j = 0; j < nr_appends; j++) {
+ u64 addr = 0x100010ULL + ((u64)(j * 197 % half)) * 0x40ULL;
+ struct symbol *sym;
+
+ scnprintf(name, sizeof(name), "jit_sym_%06u", j);
+ sym = symbol__new(addr, 0x08, STB_GLOBAL, STT_FUNC, name);
+ if (!sym) {
+ dso__put(dso);
+ return -ENOMEM;
+ }
+ dso__insert_symbol(dso, sym);
+ (void)dso__find_symbol_nocache(dso, addr + 2);
+ }
+ gettimeofday(&end, NULL);
+ timersub(&end, &start, &diff);
+ runtime_us = (u64)diff.tv_sec * USEC_PER_SEC + diff.tv_usec;
+ update_stats(&append_stats, runtime_us);
+
+ dso__put(dso);
+ }
+
+ printf(" Average bulk insert + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&bulk_stats), stddev_stats(&bulk_stats));
+ printf(" Average append + lookup: %.3f usec (+- %.3f usec)\n",
+ avg_stats(&append_stats), stddev_stats(&append_stats));
+ return 0;
+}
+
+int bench_symbols(int argc, const char **argv)
+{
+ char exe_buf[PATH_MAX];
+ const char *path;
+ bool orig_demangle;
+ bool skipped = false;
+ int err;
+
+ argc = parse_options(argc, argv, options, bench_usage, 0);
+ if (argc) {
+ usage_with_options(bench_usage, options);
+ exit(EXIT_FAILURE);
+ }
+
+ if (symbol__init(NULL) < 0)
+ return -1;
+
+ orig_demangle = symbol_conf.demangle;
+ if (no_demangle)
+ symbol_conf.demangle = false;
+
+ if (binary_path) {
+ path = binary_path;
+ } else {
+ perf_exe(exe_buf, sizeof(exe_buf));
+ path = exe_buf;
+ }
+
+ err = do_dso_load_bench(path, &skipped);
+ if (!err && !skipped)
+ err = do_dso_sort_by_name_bench(path);
+ if (!err)
+ err = do_synthetic_symbols_bench();
+
+ symbol_conf.demangle = orig_demangle;
+ symbol__exit();
+ return err;
+}
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index 62bebdedd3b8..f937c08f3900 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -97,6 +97,7 @@ static const struct bench internals_benchmarks[] = {
{ "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 },
+ { "symbols", "Benchmark symbol loading, sorting and lookup", bench_symbols },
{ NULL, NULL, NULL }
};
--
2.56.0.542.g3ef89b7020-goog
next prev parent reply other threads:[~2026-11-10 22:08 UTC|newest]
Thread overview: 73+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-28 7:52 [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and LRU shrinking Ian Rogers
2026-09-28 7:52 ` [PATCH v1 1/7] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
2026-09-28 7:52 ` [PATCH v1 2/7] perf symbol: Remove symbol_conf.priv_size and negative-offset allocations Ian Rogers
2026-09-28 7:52 ` [PATCH v1 3/7] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
2026-09-28 7:52 ` [PATCH v1 4/7] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
2026-09-28 7:52 ` [PATCH v1 5/7] perf symbol: Add LRU memory shrinking for symbols, DSOs, and machines Ian Rogers
2026-09-28 7:52 ` [PATCH v1 6/7] perf session: Periodically shrink symbols and DSOs during event processing Ian Rogers
2026-09-28 7:52 ` [PATCH v1 7/7] perf test symbols: Add tests for symbol and DSO LRU shrinking Ian Rogers
2026-09-28 15:37 ` [PATCH v1 0/7] perf symbol: Reference counting, flat array storage, and " Ian Rogers
2026-09-28 19:45 ` Alireza Haghdoost
2026-09-28 20:55 ` Ian Rogers
2026-09-28 21:51 ` Namhyung Kim
2026-09-28 22:04 ` Ian Rogers
2026-09-29 0:12 ` Alireza Haghdoost
2026-09-29 18:09 ` Namhyung Kim
2026-09-29 21:24 ` Alireza Haghdoost
2026-09-29 18:06 ` Namhyung Kim
2026-10-27 23:14 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
2026-10-27 23:14 ` [PATCH v2 01/18] perf bench internals: Add symbols and maps benchmarks Ian Rogers
2026-10-29 19:46 ` Alireza Haghdoost
2026-11-01 2:48 ` Namhyung Kim
2026-10-27 23:14 ` [PATCH v2 02/18] perf demangle: Add fast-path prefix checks for Rust and Java demanglers Ian Rogers
2026-10-27 23:14 ` [PATCH v2 03/18] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
2026-10-30 4:08 ` Alireza Haghdoost
2026-10-27 23:14 ` [PATCH v2 04/18] perf map_browser: Compute symbol browser index on demand Ian Rogers
2026-10-27 23:14 ` [PATCH v2 05/18] perf db-export: Store symbol database IDs in a hashmap Ian Rogers
2026-10-27 23:15 ` [PATCH v2 06/18] perf annotate: Move struct annotation to a hashmap and remove symbol_conf.priv_size Ian Rogers
2026-10-30 12:51 ` Alireza Haghdoost
2026-10-27 23:15 ` [PATCH v2 07/18] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
2026-10-30 13:12 ` Alireza Haghdoost
2026-10-27 23:15 ` [PATCH v2 08/18] perf symbol: Add reference counting and DECLARE_RC_STRUCT(symbol) Ian Rogers
2026-10-30 13:22 ` Alireza Haghdoost
2026-10-27 23:15 ` [PATCH v2 09/18] perf symbol: Optimize symbol sorting with natural-run merge sort and binary insertion Ian Rogers
2026-10-30 13:27 ` Alireza Haghdoost
2026-10-27 23:15 ` [PATCH v2 10/18] perf maps: Optimize tail insertion and small-append sorting Ian Rogers
2026-10-27 23:15 ` [PATCH v2 11/18] perf symbol-elf: Cache ELF program/section headers and defer symbol name lookup Ian Rogers
2026-10-30 13:32 ` Alireza Haghdoost
2026-10-27 23:15 ` [PATCH v2 12/18] perf rwsem: Add down_read_trylock() and down_write_trylock() Ian Rogers
2026-10-30 13:39 ` Alireza Haghdoost
2026-10-27 23:15 ` [PATCH v2 13/18] perf vdso: Constify dso parameter in dso__is_vdso() Ian Rogers
2026-10-27 23:15 ` [PATCH v2 14/18] perf machine: Protect machines->guests rbtree with a rw_semaphore Ian Rogers
2026-10-27 23:15 ` [PATCH v2 15/18] perf symbol: Add memory shrinking for symbols, DSOs, and machines Ian Rogers
2026-10-30 13:53 ` Alireza Haghdoost
2026-10-27 23:15 ` [PATCH v2 16/18] perf session: Add dso__session() and session-scoped allocation wrappers Ian Rogers
2026-10-27 23:15 ` [PATCH v2 17/18] perf symbol: Use session allocation wrappers in symbol, DSO, thread, and hist paths Ian Rogers
2026-10-27 23:15 ` [PATCH v2 18/18] perf test symbols: Add tests for symbol shrinking and session alloc wrappers Ian Rogers
2026-10-30 13:55 ` Alireza Haghdoost
2026-10-28 20:29 ` [PATCH v2 00/18] perf symbol: Reference counting, sorted arrays, and memory shrinking Ian Rogers
2026-10-30 14:02 ` Alireza Haghdoost
2026-10-30 16:10 ` Ian Rogers
2026-10-30 17:57 ` Ian Rogers
2026-10-31 17:22 ` David Laight
2026-11-10 22:07 ` [PATCH v3 00/20] perf symbol: Sorted arrays, DSO-owned lifetimes, " Ian Rogers
2026-11-10 22:07 ` [PATCH v3 01/20] perf map_browser: Fix browser index heap corruption by computing on demand Ian Rogers
2026-11-10 22:07 ` [PATCH v3 02/20] perf machine: Protect machines->guests rbtree with a rw_semaphore Ian Rogers
2026-11-10 22:07 ` [PATCH v3 03/20] perf vdso: Constify dso parameter in dso__is_vdso() Ian Rogers
2026-11-10 22:07 ` [PATCH v3 04/20] perf rwsem: Add down_read_trylock() and down_write_trylock() Ian Rogers
2026-11-10 22:07 ` [PATCH v3 05/20] perf map_symbol: Add struct dso_symbol and use for parent symbols Ian Rogers
2026-11-10 22:07 ` [PATCH v3 06/20] perf call-path: Use struct dso_symbol to retain symbol DSO lifetimes Ian Rogers
2026-11-10 22:07 ` [PATCH v3 07/20] perf db-export: Store symbol database IDs in a hashmap Ian Rogers
2026-11-10 22:07 ` [PATCH v3 08/20] perf annotate: Move struct annotation to a hashmap and remove symbol_conf.priv_size Ian Rogers
2026-11-10 22:07 ` Ian Rogers [this message]
2026-11-10 22:07 ` [PATCH v3 10/20] perf bench internals: Add maps benchmark Ian Rogers
2026-11-10 22:07 ` [PATCH v3 11/20] perf demangle: Add fast-path prefix checks for Rust and Java demanglers Ian Rogers
2026-11-10 22:07 ` [PATCH v3 12/20] perf symbol-elf: Cache ELF program/section headers and defer symbol name lookup Ian Rogers
2026-11-10 22:07 ` [PATCH v3 13/20] perf maps: Optimize tail insertion and small-append sorting Ian Rogers
2026-11-10 22:07 ` [PATCH v3 14/20] perf symbol: Add accessor functions for struct symbol fields Ian Rogers
2026-11-10 22:07 ` [PATCH v3 15/20] perf symbol: Switch backing storage from rbtree to struct symbols array Ian Rogers
2026-11-10 22:07 ` [PATCH v3 16/20] perf symbol: Optimize symbol sorting with natural-run merge sort and binary insertion Ian Rogers
2026-11-10 22:08 ` [PATCH v3 17/20] perf symbol: Add memory shrinking for symbols, DSOs, and machines Ian Rogers
2026-11-10 22:08 ` [PATCH v3 18/20] perf session: Add dso__session() and session-scoped allocation wrappers Ian Rogers
2026-11-10 22:08 ` [PATCH v3 19/20] perf symbol: Use session allocation wrappers in symbol, DSO, thread, and hist paths Ian Rogers
2026-11-10 22:08 ` [PATCH v3 20/20] perf test symbols: Add tests for symbol shrinking and session alloc wrappers Ian Rogers
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=20261110220803.2503416-10-irogers@google.com \
--to=irogers@google.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=haghdoost@uber.com \
--cc=james.clark@linaro.org \
--cc=jolsa@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=peterz@infradead.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 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®