From 55da878452cdc2ed3d3369f08d2578a850dd00f8 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Mon, 7 Apr 2025 08:39:06 +0200 Subject: [PATCH] chore: improved perf stats --- cmd/syncthing/perfstats_unix.go | 85 +++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 26 deletions(-) diff --git a/cmd/syncthing/perfstats_unix.go b/cmd/syncthing/perfstats_unix.go index 6da5a2116..81cf2fe7a 100644 --- a/cmd/syncthing/perfstats_unix.go +++ b/cmd/syncthing/perfstats_unix.go @@ -16,7 +16,9 @@ import ( "syscall" "time" + "github.com/syncthing/syncthing/lib/locations" "github.com/syncthing/syncthing/lib/protocol" + "golang.org/x/exp/constraints" ) func startPerfStats() { @@ -29,37 +31,68 @@ func savePerfStats(file string) { panic(err) } - var prevUsage int64 - var prevTime int64 - var rusage syscall.Rusage - var memstats runtime.MemStats + var prevTime time.Time + var curRus, prevRus syscall.Rusage + var curMem, prevMem runtime.MemStats var prevIn, prevOut int64 t0 := time.Now() + syscall.Getrusage(syscall.RUSAGE_SELF, &prevRus) + runtime.ReadMemStats(&prevMem) + + fmt.Fprintf(fd, "TIME_S\tCPU_S\tHEAP_KIB\tRSS_KIB\tNETIN_KBPS\tNETOUT_KBPS\tDBSIZE_KIB\n") + for t := range time.NewTicker(250 * time.Millisecond).C { - if err := syscall.Getrusage(syscall.RUSAGE_SELF, &rusage); err != nil { - continue - } - - curTime := time.Now().UnixNano() - timeDiff := curTime - prevTime - curUsage := rusage.Utime.Nano() + rusage.Stime.Nano() - usageDiff := curUsage - prevUsage - cpuUsagePercent := 100 * float64(usageDiff) / float64(timeDiff) - prevTime = curTime - prevUsage = curUsage + syscall.Getrusage(syscall.RUSAGE_SELF, &curRus) + runtime.ReadMemStats(&curMem) in, out := protocol.TotalInOut() - var inRate, outRate float64 - if timeDiff > 0 { - inRate = float64(in-prevIn) / (float64(timeDiff) / 1e9) // bytes per second - outRate = float64(out-prevOut) / (float64(timeDiff) / 1e9) // bytes per second - } + timeDiff := t.Sub(prevTime) + + fmt.Fprintf(fd, "%.03f\t%f\t%d\t%d\t%.0f\t%.0f\t%d\n", + t.Sub(t0).Seconds(), + rate(cpusec(&prevRus), cpusec(&curRus), timeDiff, 1), + (curMem.Sys-curMem.HeapReleased)/1024, + curRus.Maxrss/1024, + rate(prevIn, in, timeDiff, 1e3), + rate(prevOut, out, timeDiff, 1e3), + dirsize(locations.Get(locations.Database))/1024, + ) + + prevTime = t + prevRus = curRus + prevMem = curMem prevIn, prevOut = in, out - - runtime.ReadMemStats(&memstats) - - startms := int(t.Sub(t0).Seconds() * 1000) - - fmt.Fprintf(fd, "%d\t%f\t%d\t%d\t%.0f\t%.0f\n", startms, cpuUsagePercent, memstats.Alloc, memstats.Sys-memstats.HeapReleased, inRate, outRate) } } + +func cpusec(r *syscall.Rusage) float64 { + return float64(r.Utime.Nano()+r.Stime.Nano()) / float64(time.Second) +} + +type number interface { + constraints.Float | constraints.Integer +} + +func rate[T number](prev, cur T, d time.Duration, div float64) float64 { + diff := cur - prev + rate := float64(diff) / d.Seconds() / div + return rate +} + +func dirsize(location string) int64 { + entries, err := os.ReadDir(location) + if err != nil { + return 0 + } + + var size int64 + for _, entry := range entries { + fi, err := entry.Info() + if err != nil { + continue + } + size += fi.Size() + } + + return size +}