mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-29 23:30:04 +01:00
84 lines
2.0 KiB
Go
84 lines
2.0 KiB
Go
|
package pb
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"time"
|
||
|
|
||
|
"github.com/VividCortex/ewma"
|
||
|
)
|
||
|
|
||
|
var speedAddLimit = time.Second / 2
|
||
|
|
||
|
type speed struct {
|
||
|
ewma ewma.MovingAverage
|
||
|
lastStateId uint64
|
||
|
prevValue, startValue int64
|
||
|
prevTime, startTime time.Time
|
||
|
}
|
||
|
|
||
|
func (s *speed) value(state *State) float64 {
|
||
|
if s.ewma == nil {
|
||
|
s.ewma = ewma.NewMovingAverage()
|
||
|
}
|
||
|
if state.IsFirst() || state.Id() < s.lastStateId {
|
||
|
s.reset(state)
|
||
|
return 0
|
||
|
}
|
||
|
if state.Id() == s.lastStateId {
|
||
|
return s.ewma.Value()
|
||
|
}
|
||
|
if state.IsFinished() {
|
||
|
return s.absValue(state)
|
||
|
}
|
||
|
dur := state.Time().Sub(s.prevTime)
|
||
|
if dur < speedAddLimit {
|
||
|
return s.ewma.Value()
|
||
|
}
|
||
|
diff := math.Abs(float64(state.Value() - s.prevValue))
|
||
|
lastSpeed := diff / dur.Seconds()
|
||
|
s.prevTime = state.Time()
|
||
|
s.prevValue = state.Value()
|
||
|
s.lastStateId = state.Id()
|
||
|
s.ewma.Add(lastSpeed)
|
||
|
return s.ewma.Value()
|
||
|
}
|
||
|
|
||
|
func (s *speed) reset(state *State) {
|
||
|
s.lastStateId = state.Id()
|
||
|
s.startTime = state.Time()
|
||
|
s.prevTime = state.Time()
|
||
|
s.startValue = state.Value()
|
||
|
s.prevValue = state.Value()
|
||
|
s.ewma = ewma.NewMovingAverage()
|
||
|
}
|
||
|
|
||
|
func (s *speed) absValue(state *State) float64 {
|
||
|
if dur := state.Time().Sub(s.startTime); dur > 0 {
|
||
|
return float64(state.Value()) / dur.Seconds()
|
||
|
}
|
||
|
return 0
|
||
|
}
|
||
|
|
||
|
func getSpeedObj(state *State) (s *speed) {
|
||
|
if sObj, ok := state.Get(speedObj).(*speed); ok {
|
||
|
return sObj
|
||
|
}
|
||
|
s = new(speed)
|
||
|
state.Set(speedObj, s)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// ElementSpeed calculates current speed by EWMA
|
||
|
// Optionally can take one or two string arguments.
|
||
|
// First string will be used as value for format speed, default is "%s p/s".
|
||
|
// Second string will be used when speed not available, default is "? p/s"
|
||
|
// In template use as follows: {{speed .}} or {{speed . "%s per second"}} or {{speed . "%s ps" "..."}
|
||
|
var ElementSpeed ElementFunc = func(state *State, args ...string) string {
|
||
|
sp := getSpeedObj(state).value(state)
|
||
|
if sp == 0 {
|
||
|
return argsHelper(args).getNotEmptyOr(1, "? p/s")
|
||
|
}
|
||
|
return fmt.Sprintf(argsHelper(args).getNotEmptyOr(0, "%s p/s"), state.Format(int64(round(sp))))
|
||
|
}
|