From 0f050e5fe1b2f34b484a6f082cc483f0712a3135 Mon Sep 17 00:00:00 2001 From: Maycon Santos Date: Sun, 8 Jun 2025 11:19:54 +0100 Subject: [PATCH] [client] Optmize process check time (#3938) This PR optimizes the process check time by updating the implementation of getRunningProcesses and introducing new benchmark tests. Updated getRunningProcesses to use process.Pids() instead of process.Processes() Added benchmark tests for both the new and the legacy implementations Benchmark: https://github.com/netbirdio/netbird/actions/runs/15512741612 todo: evaluate windows optmizations and caching risks --- client/system/process.go | 8 +++-- client/system/process_test.go | 58 +++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 client/system/process_test.go diff --git a/client/system/process.go b/client/system/process.go index 2e43fcfe0..87e21eb9d 100644 --- a/client/system/process.go +++ b/client/system/process.go @@ -11,16 +11,18 @@ import ( // getRunningProcesses returns a list of running process paths. func getRunningProcesses() ([]string, error) { - processes, err := process.Processes() + processIDs, err := process.Pids() if err != nil { return nil, err } processMap := make(map[string]bool) - for _, p := range processes { + for _, pID := range processIDs { + p := &process.Process{Pid: pID} + path, _ := p.Exe() if path != "" { - processMap[path] = true + processMap[path] = false } } diff --git a/client/system/process_test.go b/client/system/process_test.go new file mode 100644 index 000000000..505808a9e --- /dev/null +++ b/client/system/process_test.go @@ -0,0 +1,58 @@ +package system + +import ( + "testing" + + "github.com/shirou/gopsutil/v3/process" +) + +func Benchmark_getRunningProcesses(b *testing.B) { + b.Run("getRunningProcesses new", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ps, err := getRunningProcesses() + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + if len(ps) == 0 { + b.Fatalf("expected non-empty process list, got empty") + } + } + }) + b.Run("getRunningProcesses old", func(b *testing.B) { + for i := 0; i < b.N; i++ { + ps, err := getRunningProcessesOld() + if err != nil { + b.Fatalf("unexpected error: %v", err) + } + if len(ps) == 0 { + b.Fatalf("expected non-empty process list, got empty") + } + } + }) + s, _ := getRunningProcesses() + b.Logf("getRunningProcesses returned %d processes", len(s)) + s, _ = getRunningProcessesOld() + b.Logf("getRunningProcessesOld returned %d processes", len(s)) +} + +func getRunningProcessesOld() ([]string, error) { + processes, err := process.Processes() + if err != nil { + return nil, err + } + + processMap := make(map[string]bool) + for _, p := range processes { + path, _ := p.Exe() + if path != "" { + processMap[path] = true + } + } + + uniqueProcesses := make([]string, 0, len(processMap)) + for p := range processMap { + uniqueProcesses = append(uniqueProcesses, p) + } + + return uniqueProcesses, nil +}