perf tools: Improve startup time by reducing unnecessary stat() calls
authorKrzysztof Łopatowski <krzysztof.m.lopatowski@gmail.com>
Thu, 6 Feb 2025 11:33:15 +0000 (12:33 +0100)
committerNamhyung Kim <namhyung@kernel.org>
Wed, 19 Feb 2025 21:55:59 +0000 (13:55 -0800)
When testing perf trace on NixOS, I noticed significant startup delays:
- `ls`: ~2ms
- `strace ls`: ~10ms
- `perf trace ls`: ~550ms

Profiling showed that 51% of the time is spent reading files,
26% in loading BPF programs, and 11% in `newfstatat`.

This patch optimizes module path exploration by avoiding `stat()` calls
unless necessary. For filesystems that do not implement `d_type`
(DT_UNKNOWN), it falls back to the old behavior.
See `readdir(3)` for details.

This reduces `perf trace ls` time to ~500ms.

A more thorough startup optimization based on command parameters would
be ideal, but that is a larger effort.

Signed-off-by: Krzysztof Łopatowski <krzysztof.m.lopatowski@gmail.com>
Acked-by: Howard Chu <howardchu95@gmail.com>
Link: https://lore.kernel.org/r/20250206113314.335376-2-krzysztof.m.lopatowski@gmail.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
tools/perf/util/machine.c

index d96cbfd97ad8fb4da3d3e42d0aec341101242c4a..316f0879e5e08d66791020ac21cd7cfe264e8cd5 100644 (file)
@@ -1339,7 +1339,7 @@ static int maps__set_module_path(struct maps *maps, const char *path, struct kmo
 
 static int maps__set_modules_path_dir(struct maps *maps, const char *dir_name, int depth)
 {
-       struct dirent *dent;
+       const struct dirent *dent;
        DIR *dir = opendir(dir_name);
        int ret = 0;
 
@@ -1350,14 +1350,20 @@ static int maps__set_modules_path_dir(struct maps *maps, const char *dir_name, i
 
        while ((dent = readdir(dir)) != NULL) {
                char path[PATH_MAX];
-               struct stat st;
+               unsigned char d_type = dent->d_type;
 
-               /*sshfs might return bad dent->d_type, so we have to stat*/
                path__join(path, sizeof(path), dir_name, dent->d_name);
-               if (stat(path, &st))
-                       continue;
 
-               if (S_ISDIR(st.st_mode)) {
+               if (d_type == DT_UNKNOWN) {
+                       struct stat st;
+
+                       if (stat(path, &st))
+                               continue;
+                       if (S_ISDIR(st.st_mode))
+                               d_type = DT_DIR;
+               }
+
+               if (d_type == DT_DIR) {
                        if (!strcmp(dent->d_name, ".") ||
                            !strcmp(dent->d_name, ".."))
                                continue;