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

perf pmu: Use file system cache to optimize sysfs access

pmu.c does a lot of redundant /sys accesses while parsing aliases
and probing for PMUs. On large systems with a lot of PMUs this
can get expensive (>2s):

% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
27.25 1.227847 8 160888 16976 openat
26.42 1.190481 7 164224 164077 stat

Add a cache to remember if specific file names exist or don't
exist, which eliminates most of this overhead.

Also optimize some stat() calls to be slightly cheaper access()

Resulting in:

0.18 0.004166 2 1851 305 open
0.08 0.001970 2 829 622 access

Signed-off-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Link: http://lore.kernel.org/lkml/20191121001522.180827-2-andi@firstfloor.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Andi Kleen and committed by
Arnaldo Carvalho de Melo
d9664582 5b596e0f

+83 -31
+1
tools/perf/util/Build
··· 49 49 perf-y += callchain.o 50 50 perf-y += values.o 51 51 perf-y += debug.o 52 + perf-y += fncache.o 52 53 perf-y += machine.o 53 54 perf-y += map.o 54 55 perf-y += pstack.o
+63
tools/perf/util/fncache.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* Manage a cache of file names' existence */ 3 + #include <stdlib.h> 4 + #include <unistd.h> 5 + #include <string.h> 6 + #include <linux/list.h> 7 + #include "fncache.h" 8 + 9 + struct fncache { 10 + struct hlist_node nd; 11 + bool res; 12 + char name[]; 13 + }; 14 + 15 + #define FNHSIZE 61 16 + 17 + static struct hlist_head fncache_hash[FNHSIZE]; 18 + 19 + unsigned shash(const unsigned char *s) 20 + { 21 + unsigned h = 0; 22 + while (*s) 23 + h = 65599 * h + *s++; 24 + return h ^ (h >> 16); 25 + } 26 + 27 + static bool lookup_fncache(const char *name, bool *res) 28 + { 29 + int h = shash((const unsigned char *)name) % FNHSIZE; 30 + struct fncache *n; 31 + 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; 39 + } 40 + 41 + static void update_fncache(const char *name, bool res) 42 + { 43 + struct fncache *n = malloc(sizeof(struct fncache) + strlen(name) + 1); 44 + int h = shash((const unsigned char *)name) % FNHSIZE; 45 + 46 + if (!n) 47 + return; 48 + strcpy(n->name, name); 49 + n->res = res; 50 + hlist_add_head(&n->nd, &fncache_hash[h]); 51 + } 52 + 53 + /* No LRU, only use when bounded in some other way. */ 54 + bool file_available(const char *name) 55 + { 56 + bool res; 57 + 58 + if (lookup_fncache(name, &res)) 59 + return res; 60 + res = access(name, R_OK) == 0; 61 + update_fncache(name, res); 62 + return res; 63 + }
+7
tools/perf/util/fncache.h
··· 1 + #ifndef _FCACHE_H 2 + #define _FCACHE_H 1 3 + 4 + unsigned shash(const unsigned char *s); 5 + bool file_available(const char *name); 6 + 7 + #endif
+11 -23
tools/perf/util/pmu.c
··· 24 24 #include "pmu-events/pmu-events.h" 25 25 #include "string2.h" 26 26 #include "strbuf.h" 27 + #include "fncache.h" 27 28 28 29 struct perf_pmu_format { 29 30 char *name; ··· 83 82 */ 84 83 static int pmu_format(const char *name, struct list_head *format) 85 84 { 86 - struct stat st; 87 85 char path[PATH_MAX]; 88 86 const char *sysfs = sysfs__mountpoint(); 89 87 ··· 92 92 snprintf(path, PATH_MAX, 93 93 "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name); 94 94 95 - if (stat(path, &st) < 0) 96 - return 0; /* no error if format does not exist */ 95 + if (!file_available(path)) 96 + return 0; 97 97 98 98 if (perf_pmu__format_parse(path, format)) 99 99 return -1; ··· 475 475 */ 476 476 static int pmu_aliases(const char *name, struct list_head *head) 477 477 { 478 - struct stat st; 479 478 char path[PATH_MAX]; 480 479 const char *sysfs = sysfs__mountpoint(); 481 480 ··· 484 485 snprintf(path, PATH_MAX, 485 486 "%s/bus/event_source/devices/%s/events", sysfs, name); 486 487 487 - if (stat(path, &st) < 0) 488 - return 0; /* no error if 'events' does not exist */ 488 + if (!file_available(path)) 489 + return 0; 489 490 490 491 if (pmu_aliases_parse(path, head)) 491 492 return -1; ··· 524 525 */ 525 526 static int pmu_type(const char *name, __u32 *type) 526 527 { 527 - struct stat st; 528 528 char path[PATH_MAX]; 529 529 FILE *file; 530 530 int ret = 0; ··· 535 537 snprintf(path, PATH_MAX, 536 538 "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name); 537 539 538 - if (stat(path, &st) < 0) 540 + if (access(path, R_OK) < 0) 539 541 return -1; 540 542 541 543 file = fopen(path, "r"); ··· 626 628 static bool pmu_is_uncore(const char *name) 627 629 { 628 630 char path[PATH_MAX]; 629 - struct perf_cpu_map *cpus; 630 - const char *sysfs = sysfs__mountpoint(); 631 + const char *sysfs; 631 632 633 + sysfs = sysfs__mountpoint(); 632 634 snprintf(path, PATH_MAX, CPUS_TEMPLATE_UNCORE, sysfs, name); 633 - cpus = __pmu_cpumask(path); 634 - perf_cpu_map__put(cpus); 635 - 636 - return !!cpus; 635 + return file_available(path); 637 636 } 638 637 639 638 /* ··· 640 645 */ 641 646 static int is_arm_pmu_core(const char *name) 642 647 { 643 - struct stat st; 644 648 char path[PATH_MAX]; 645 649 const char *sysfs = sysfs__mountpoint(); 646 650 ··· 649 655 /* Look for cpu sysfs (specific to arm) */ 650 656 scnprintf(path, PATH_MAX, "%s/bus/event_source/devices/%s/cpus", 651 657 sysfs, name); 652 - if (stat(path, &st) == 0) 653 - return 1; 654 - 655 - return 0; 658 + return file_available(path); 656 659 } 657 660 658 661 static char *perf_pmu__getcpuid(struct perf_pmu *pmu) ··· 1535 1544 1536 1545 static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) 1537 1546 { 1538 - struct stat st; 1539 1547 char path[PATH_MAX]; 1540 1548 const char *sysfs; 1541 1549 ··· 1544 1554 1545 1555 snprintf(path, PATH_MAX, 1546 1556 "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name); 1547 - 1548 - if (stat(path, &st) < 0) 1557 + if (!file_available(path)) 1549 1558 return NULL; 1550 - 1551 1559 return fopen(path, "r"); 1552 1560 } 1553 1561
+1 -8
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 20 20 21 #define MAXSRCCACHE (32*1024*1024) 21 22 #define MAXSRCFILES 64 ··· 36 35 static LIST_HEAD(srcfile_list); 37 36 static long map_total_sz; 38 37 static int num_srcfiles; 39 - 40 - static unsigned shash(unsigned char *s) 41 - { 42 - unsigned h = 0; 43 - while (*s) 44 - h = 65599 * h + *s++; 45 - return h ^ (h >> 16); 46 - } 47 38 48 39 static int countlines(char *map, int maplen) 49 40 {