perf fncache: Switch to using hashmap
authorIan Rogers <irogers@google.com>
Mon, 12 May 2025 19:46:20 +0000 (12:46 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 13 May 2025 19:36:22 +0000 (16:36 -0300)
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>
tools/perf/util/fncache.c
tools/perf/util/fncache.h
tools/perf/util/srccode.c

index 6225cbc523101313c3faa7f645d2d670abbad597..bf9559c55c635da5b90f1e11c1d48ce06f2cc114 100644 (file)
@@ -1,53 +1,58 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /* Manage a cache of file names' existence */
+#include <pthread.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-#include <linux/list.h>
+#include <unistd.h>
+#include <linux/compiler.h>
 #include "fncache.h"
+#include "hashmap.h"
 
-struct fncache {
-       struct hlist_node nd;
-       bool res;
-       char name[];
-};
+static struct hashmap *fncache;
 
-#define FNHSIZE 61
+static size_t fncache__hash(long key, void *ctx __maybe_unused)
+{
+       return str_hash((const char *)key);
+}
 
-static struct hlist_head fncache_hash[FNHSIZE];
+static bool fncache__equal(long key1, long key2, void *ctx __maybe_unused)
+{
+       return strcmp((const char *)key1, (const char *)key2) == 0;
+}
 
-unsigned shash(const unsigned char *s)
+static void fncache__init(void)
 {
-       unsigned h = 0;
-       while (*s)
-               h = 65599 * h + *s++;
-       return h ^ (h >> 16);
+       fncache = hashmap__new(fncache__hash, fncache__equal, /*ctx=*/NULL);
+}
+
+static struct hashmap *fncache__get(void)
+{
+       static pthread_once_t fncache_once = PTHREAD_ONCE_INIT;
+
+       pthread_once(&fncache_once, fncache__init);
+
+       return fncache;
 }
 
 static bool lookup_fncache(const char *name, bool *res)
 {
-       int h = shash((const unsigned char *)name) % FNHSIZE;
-       struct fncache *n;
-
-       hlist_for_each_entry(n, &fncache_hash[h], nd) {
-               if (!strcmp(n->name, name)) {
-                       *res = n->res;
-                       return true;
-               }
-       }
-       return false;
+       long val;
+
+       if (!hashmap__find(fncache__get(), name, &val))
+               return false;
+
+       *res = (val != 0);
+       return true;
 }
 
 static void update_fncache(const char *name, bool res)
 {
-       struct fncache *n = malloc(sizeof(struct fncache) + strlen(name) + 1);
-       int h = shash((const unsigned char *)name) % FNHSIZE;
-
-       if (!n)
-               return;
-       strcpy(n->name, name);
-       n->res = res;
-       hlist_add_head(&n->nd, &fncache_hash[h]);
+       char *old_key = NULL, *key = strdup(name);
+
+       if (key) {
+               hashmap__set(fncache__get(), key, res, &old_key, /*old_value*/NULL);
+               free(old_key);
+       }
 }
 
 /* No LRU, only use when bounded in some other way. */
index fe020beaefb1d7c7644f84dc63188db66918f18e..b6a0f209493efbcb6ef83438612201dcb0b37ce7 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef _FCACHE_H
 #define _FCACHE_H 1
 
-unsigned shash(const unsigned char *s);
 bool file_available(const char *name);
 
 #endif
index 476e99896d5e37a013be89e0e7acf7bcb77014f3..0f4907843ac19726cf96ee4d54e71697a3885e01 100644 (file)
@@ -16,7 +16,7 @@
 #include "srccode.h"
 #include "debug.h"
 #include <internal/lib.h> // page_size
-#include "fncache.h"
+#include "hashmap.h"
 
 #define MAXSRCCACHE (32*1024*1024)
 #define MAXSRCFILES     64
@@ -92,7 +92,7 @@ static struct srcfile *find_srcfile(char *fn)
        struct srcfile *h;
        int fd;
        unsigned long sz;
-       unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ;
+       size_t hval = str_hash(fn) % SRC_HTAB_SZ;
 
        hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) {
                if (!strcmp(fn, h->fn)) {