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
This commit is contained in:
Jakob Borg 2017-04-20 08:33:09 +00:00
parent 6b188ebcf3
commit 10894695c6
6 changed files with 78 additions and 136 deletions

View File

@ -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())
}

View File

@ -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())
}

View File

@ -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())
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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
}
}