From 10894695c63a71d0dd0b6b96c8fc526bb1072554 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Thu, 20 Apr 2017 08:33:09 +0000 Subject: [PATCH] cmd/syncthing: Use a 10 second EWMA for CPU usage, refactor a little We have it lying around so might as well use it instead of inventing our own thing. GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4111 --- .../{gui_solaris.go => cpuusage_solaris.go} | 42 ++++---------- cmd/syncthing/cpuusage_unix.go | 18 ++++++ cmd/syncthing/cpuusage_windows.go | 26 +++++++++ cmd/syncthing/gui.go | 33 +++++++---- cmd/syncthing/gui_unix.go | 37 ------------ cmd/syncthing/gui_windows.go | 58 ------------------- 6 files changed, 78 insertions(+), 136 deletions(-) rename cmd/syncthing/{gui_solaris.go => cpuusage_solaris.go} (72%) create mode 100644 cmd/syncthing/cpuusage_unix.go create mode 100644 cmd/syncthing/cpuusage_windows.go delete mode 100644 cmd/syncthing/gui_unix.go delete mode 100644 cmd/syncthing/gui_windows.go diff --git a/cmd/syncthing/gui_solaris.go b/cmd/syncthing/cpuusage_solaris.go similarity index 72% rename from cmd/syncthing/gui_solaris.go rename to cmd/syncthing/cpuusage_solaris.go index 8a479de6..db59359e 100644 --- a/cmd/syncthing/gui_solaris.go +++ b/cmd/syncthing/cpuusage_solaris.go @@ -59,40 +59,20 @@ type prusage_t struct { } -func solarisPrusage(pid int, rusage *prusage_t) error { - fd, err := os.Open(fmt.Sprintf("/proc/%d/usage", pid)) +var procFile = fmt.Sprintf("/proc/%d/usage", os.Getpid()) + +func cpuUsage() time.Duration { + fd, err := os.Open(procFile) if err != nil { - return err + return 0 } + + var rusage prusage_t err = binary.Read(fd, binary.LittleEndian, rusage) fd.Close() - return err -} - -func init() { - go trackCPUUsage() -} - -func trackCPUUsage() { - var prevUsage int64 - var prevTime = time.Now().UnixNano() - var rusage prusage_t - var pid = os.Getpid() - for range time.NewTicker(time.Second).C { - err := solarisPrusage(pid, &rusage) - if err != nil { - l.Warnln("getting prusage:", err) - continue - } - curTime := time.Now().UnixNano() - timeDiff := curTime - prevTime - curUsage := rusage.Pr_utime.Nano() + rusage.Pr_stime.Nano() - usageDiff := curUsage - prevUsage - cpuUsageLock.Lock() - copy(cpuUsagePercent[1:], cpuUsagePercent[0:]) - cpuUsagePercent[0] = 100 * float64(usageDiff) / float64(timeDiff) - cpuUsageLock.Unlock() - prevTime = curTime - prevUsage = curUsage + if err != nil { + return 0 } + + return time.Duration(rusage.Pr_utime.Nano() + rusage.Pr_stime.Nano()) } diff --git a/cmd/syncthing/cpuusage_unix.go b/cmd/syncthing/cpuusage_unix.go new file mode 100644 index 00000000..fab82854 --- /dev/null +++ b/cmd/syncthing/cpuusage_unix.go @@ -0,0 +1,18 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +//+build !windows,!solaris + +package main + +import "syscall" +import "time" + +func cpuUsage() time.Duration { + var rusage syscall.Rusage + syscall.Getrusage(syscall.RUSAGE_SELF, &rusage) + return time.Duration(rusage.Utime.Nano() + rusage.Stime.Nano()) +} diff --git a/cmd/syncthing/cpuusage_windows.go b/cmd/syncthing/cpuusage_windows.go new file mode 100644 index 00000000..9aac2bf1 --- /dev/null +++ b/cmd/syncthing/cpuusage_windows.go @@ -0,0 +1,26 @@ +// Copyright (C) 2014 The Syncthing Authors. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +//+build windows + +package main + +import "syscall" +import "time" + +func cpuUsage() time.Duration { + handle, err := syscall.GetCurrentProcess() + if err != nil { + return 0 + } + + var ctime, etime, ktime, utime syscall.Filetime + if err := syscall.GetProcessTimes(handle, &ctime, &etime, &ktime, &utime); err != nil { + return 0 + } + + return time.Duration(ktime.Nanoseconds() + utime.Nanoseconds()) +} diff --git a/cmd/syncthing/gui.go b/cmd/syncthing/gui.go index 05a2b19f..9aa3b20b 100644 --- a/cmd/syncthing/gui.go +++ b/cmd/syncthing/gui.go @@ -11,6 +11,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math" "net" "net/http" "os" @@ -846,8 +847,25 @@ func (s *apiService) flushResponse(resp string, w http.ResponseWriter) { f.Flush() } -var cpuUsagePercent [10]float64 // The last ten seconds -var cpuUsageLock = sync.NewRWMutex() +// 10 second average. Magic alpha value comes from looking at EWMA package +// definitions of EWMA1, EWMA5. +var cpuTickRate = 2 * time.Second +var cpuAverage = metrics.NewEWMA(1 - math.Exp(-float64(cpuTickRate)/float64(time.Second)/10.0)) + +func init() { + if !innerProcess { + return + } + go func() { + var prevUsage time.Duration + for range time.NewTicker(cpuTickRate).C { + curUsage := cpuUsage() + cpuAverage.Update(int64((curUsage - prevUsage) / time.Millisecond)) + prevUsage = curUsage + cpuAverage.Tick() + } + }() +} func (s *apiService) getSystemStatus(w http.ResponseWriter, r *http.Request) { var m runtime.MemStats @@ -875,14 +893,9 @@ func (s *apiService) getSystemStatus(w http.ResponseWriter, r *http.Request) { } res["connectionServiceStatus"] = s.connectionsService.Status() - - cpuUsageLock.RLock() - var cpusum float64 - for _, p := range cpuUsagePercent { - cpusum += p - } - cpuUsageLock.RUnlock() - res["cpuPercent"] = cpusum / float64(len(cpuUsagePercent)) / float64(runtime.NumCPU()) + // cpuUsage.Rate() is in milliseconds per second, so dividing by ten + // gives us percent + res["cpuPercent"] = cpuAverage.Rate() / 10 / float64(runtime.NumCPU()) res["pathSeparator"] = string(filepath.Separator) res["uptime"] = int(time.Since(startTime).Seconds()) res["startTime"] = startTime diff --git a/cmd/syncthing/gui_unix.go b/cmd/syncthing/gui_unix.go deleted file mode 100644 index 2962bbd9..00000000 --- a/cmd/syncthing/gui_unix.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -//+build !windows,!solaris - -package main - -import ( - "syscall" - "time" -) - -func init() { - go trackCPUUsage() -} - -func trackCPUUsage() { - var prevUsage int64 - var prevTime = time.Now().UnixNano() - var rusage syscall.Rusage - for range time.NewTicker(time.Second).C { - syscall.Getrusage(syscall.RUSAGE_SELF, &rusage) - curTime := time.Now().UnixNano() - timeDiff := curTime - prevTime - curUsage := rusage.Utime.Nano() + rusage.Stime.Nano() - usageDiff := curUsage - prevUsage - cpuUsageLock.Lock() - copy(cpuUsagePercent[1:], cpuUsagePercent[0:]) - cpuUsagePercent[0] = 100 * float64(usageDiff) / float64(timeDiff) - cpuUsageLock.Unlock() - prevTime = curTime - prevUsage = curUsage - } -} diff --git a/cmd/syncthing/gui_windows.go b/cmd/syncthing/gui_windows.go deleted file mode 100644 index 779b676b..00000000 --- a/cmd/syncthing/gui_windows.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2014 The Syncthing Authors. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at https://mozilla.org/MPL/2.0/. - -//+build windows - -package main - -import ( - "syscall" - "time" -) - -func init() { - go trackCPUUsage() -} - -func trackCPUUsage() { - handle, err := syscall.GetCurrentProcess() - if err != nil { - l.Warnln("Cannot track CPU usage:", err) - return - } - - var ctime, etime, ktime, utime syscall.Filetime - err = syscall.GetProcessTimes(handle, &ctime, &etime, &ktime, &utime) - if err != nil { - l.Warnln("Cannot track CPU usage:", err) - return - } - - prevTime := ctime.Nanoseconds() - prevUsage := ktime.Nanoseconds() + utime.Nanoseconds() // Always overflows - - for range time.NewTicker(time.Second).C { - err := syscall.GetProcessTimes(handle, &ctime, &etime, &ktime, &utime) - if err != nil { - continue - } - - curTime := time.Now().UnixNano() - timeDiff := curTime - prevTime - // This is sometimes 0, no clue why. - if timeDiff == 0 { - continue - } - curUsage := ktime.Nanoseconds() + utime.Nanoseconds() - usageDiff := curUsage - prevUsage - cpuUsageLock.Lock() - copy(cpuUsagePercent[1:], cpuUsagePercent[0:]) - cpuUsagePercent[0] = 100 * float64(usageDiff) / float64(timeDiff) - cpuUsageLock.Unlock() - prevTime = curTime - prevUsage = curUsage - } -}