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:
parent
6b188ebcf3
commit
10894695c6
@ -59,40 +59,20 @@ type prusage_t struct {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func solarisPrusage(pid int, rusage *prusage_t) error {
|
var procFile = fmt.Sprintf("/proc/%d/usage", os.Getpid())
|
||||||
fd, err := os.Open(fmt.Sprintf("/proc/%d/usage", pid))
|
|
||||||
|
func cpuUsage() time.Duration {
|
||||||
|
fd, err := os.Open(procFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rusage prusage_t
|
||||||
err = binary.Read(fd, binary.LittleEndian, rusage)
|
err = binary.Read(fd, binary.LittleEndian, rusage)
|
||||||
fd.Close()
|
fd.Close()
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return 0
|
||||||
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return time.Duration(rusage.Pr_utime.Nano() + rusage.Pr_stime.Nano())
|
||||||
}
|
}
|
||||||
18
cmd/syncthing/cpuusage_unix.go
Normal file
18
cmd/syncthing/cpuusage_unix.go
Normal 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())
|
||||||
|
}
|
||||||
26
cmd/syncthing/cpuusage_windows.go
Normal file
26
cmd/syncthing/cpuusage_windows.go
Normal 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())
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -846,8 +847,25 @@ func (s *apiService) flushResponse(resp string, w http.ResponseWriter) {
|
|||||||
f.Flush()
|
f.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
var cpuUsagePercent [10]float64 // The last ten seconds
|
// 10 second average. Magic alpha value comes from looking at EWMA package
|
||||||
var cpuUsageLock = sync.NewRWMutex()
|
// 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) {
|
func (s *apiService) getSystemStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
var m runtime.MemStats
|
var m runtime.MemStats
|
||||||
@ -875,14 +893,9 @@ func (s *apiService) getSystemStatus(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res["connectionServiceStatus"] = s.connectionsService.Status()
|
res["connectionServiceStatus"] = s.connectionsService.Status()
|
||||||
|
// cpuUsage.Rate() is in milliseconds per second, so dividing by ten
|
||||||
cpuUsageLock.RLock()
|
// gives us percent
|
||||||
var cpusum float64
|
res["cpuPercent"] = cpuAverage.Rate() / 10 / float64(runtime.NumCPU())
|
||||||
for _, p := range cpuUsagePercent {
|
|
||||||
cpusum += p
|
|
||||||
}
|
|
||||||
cpuUsageLock.RUnlock()
|
|
||||||
res["cpuPercent"] = cpusum / float64(len(cpuUsagePercent)) / float64(runtime.NumCPU())
|
|
||||||
res["pathSeparator"] = string(filepath.Separator)
|
res["pathSeparator"] = string(filepath.Separator)
|
||||||
res["uptime"] = int(time.Since(startTime).Seconds())
|
res["uptime"] = int(time.Since(startTime).Seconds())
|
||||||
res["startTime"] = startTime
|
res["startTime"] = startTime
|
||||||
|
|||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user