Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf fncache: Switch to using hashmap

The existing fncache can get large in testing situations. As the
bucket array is a fixed size this leads to it degrading to O(n)
performance. Use a regular hashmap that can dynamically reallocate its
array.

Before:
```
$ time perf test "Parsing of PMU event table metrics"
10.3: Parsing of PMU event table metrics : Ok
10.4: Parsing of PMU event table metrics with fake PMUs : Ok

real 0m14.132s
user 0m17.806s
sys 0m0.557s
```

After:
```
$ time perf test "Parsing of PMU event table metrics"
10.3: Parsing of PMU event table metrics : Ok
10.4: Parsing of PMU event table metrics with fake PMUs : Ok

real 0m13.287s
user 0m13.026s
sys 0m0.532s
```

Committer notes:

root@number:~# grep -m1 'model name' /proc/cpuinfo
model name : AMD Ryzen 9 9950X3D 16-Core Processor
root@number:~#

Before:

root@number:~# time perf test "Parsing of PMU event table metrics"
10.3: Parsing of PMU event table metrics : Ok
10.4: Parsing of PMU event table metrics with fake PMUs : Ok

real 0m9.277s
user 0m9.979s
sys 0m0.055s
root@number:~#

After:

root@number:~# time perf test "Parsing of PMU event table metrics"
10.3: Parsing of PMU event table metrics : Ok
10.4: Parsing of PMU event table metrics with fake PMUs : Ok

real 0m9.296s
user 0m9.361s
sys 0m0.063s
root@number:~#

Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Xu Yang <xu.yang_2@nxp.com>
Link: https://lore.kernel.org/r/20250512194622.33258-2-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
375368a9 f3061d52

+39 -35
+37 -32
tools/perf/util/fncache.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 2 /* Manage a cache of file names' existence */ 3 + #include <pthread.h> 3 4 #include <stdlib.h> 4 - #include <unistd.h> 5 5 #include <string.h> 6 - #include <linux/list.h> 6 + #include <unistd.h> 7 + #include <linux/compiler.h> 7 8 #include "fncache.h" 9 + #include "hashmap.h" 8 10 9 - struct fncache { 10 - struct hlist_node nd; 11 - bool res; 12 - char name[]; 13 - }; 11 + static struct hashmap *fncache; 14 12 15 - #define FNHSIZE 61 16 - 17 - static struct hlist_head fncache_hash[FNHSIZE]; 18 - 19 - unsigned shash(const unsigned char *s) 13 + static size_t fncache__hash(long key, void *ctx __maybe_unused) 20 14 { 21 - unsigned h = 0; 22 - while (*s) 23 - h = 65599 * h + *s++; 24 - return h ^ (h >> 16); 15 + return str_hash((const char *)key); 16 + } 17 + 18 + static bool fncache__equal(long key1, long key2, void *ctx __maybe_unused) 19 + { 20 + return strcmp((const char *)key1, (const char *)key2) == 0; 21 + } 22 + 23 + static void fncache__init(void) 24 + { 25 + fncache = hashmap__new(fncache__hash, fncache__equal, /*ctx=*/NULL); 26 + } 27 + 28 + static struct hashmap *fncache__get(void) 29 + { 30 + static pthread_once_t fncache_once = PTHREAD_ONCE_INIT; 31 + 32 + pthread_once(&fncache_once, fncache__init); 33 + 34 + return fncache; 25 35 } 26 36 27 37 static bool lookup_fncache(const char *name, bool *res) 28 38 { 29 - int h = shash((const unsigned char *)name) % FNHSIZE; 30 - struct fncache *n; 39 + long val; 31 40 32 - hlist_for_each_entry(n, &fncache_hash[h], nd) { 33 - if (!strcmp(n->name, name)) { 34 - *res = n->res; 35 - return true; 36 - } 37 - } 38 - return false; 41 + if (!hashmap__find(fncache__get(), name, &val)) 42 + return false; 43 + 44 + *res = (val != 0); 45 + return true; 39 46 } 40 47 41 48 static void update_fncache(const char *name, bool res) 42 49 { 43 - struct fncache *n = malloc(sizeof(struct fncache) + strlen(name) + 1); 44 - int h = shash((const unsigned char *)name) % FNHSIZE; 50 + char *old_key = NULL, *key = strdup(name); 45 51 46 - if (!n) 47 - return; 48 - strcpy(n->name, name); 49 - n->res = res; 50 - hlist_add_head(&n->nd, &fncache_hash[h]); 52 + if (key) { 53 + hashmap__set(fncache__get(), key, res, &old_key, /*old_value*/NULL); 54 + free(old_key); 55 + } 51 56 } 52 57 53 58 /* No LRU, only use when bounded in some other way. */
-1
tools/perf/util/fncache.h
··· 1 1 #ifndef _FCACHE_H 2 2 #define _FCACHE_H 1 3 3 4 - unsigned shash(const unsigned char *s); 5 4 bool file_available(const char *name); 6 5 7 6 #endif
+2 -2
tools/perf/util/srccode.c
··· 16 16 #include "srccode.h" 17 17 #include "debug.h" 18 18 #include <internal/lib.h> // page_size 19 - #include "fncache.h" 19 + #include "hashmap.h" 20 20 21 21 #define MAXSRCCACHE (32*1024*1024) 22 22 #define MAXSRCFILES 64 ··· 92 92 struct srcfile *h; 93 93 int fd; 94 94 unsigned long sz; 95 - unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ; 95 + size_t hval = str_hash(fn) % SRC_HTAB_SZ; 96 96 97 97 hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) { 98 98 if (!strcmp(fn, h->fn)) {