-
Notifications
You must be signed in to change notification settings - Fork 0
/
ewma.go
130 lines (106 loc) · 2.95 KB
/
ewma.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package metrics
import (
"math"
"sync"
"time"
)
// EWMA calculates an exponentially-weighted per-second moving average.
type EWMA interface {
Rate() float64
Snapshot() EWMA
Update(int64)
}
// NewEWMA constructs a new EWMA with the given alpha and period.
func NewEWMA(alpha float64, period time.Duration) EWMA {
if UseNilMetrics {
return NilEWMA{}
}
return &StandardEWMA{
alpha: alpha,
period: period,
timestamp: time.Now(),
}
}
// NewEWMA1 constructs a new EWMA for a one-minute moving average.
func NewEWMA1() EWMA {
return NewEWMA(1-math.Exp(-5.0/60.0/1), 5*time.Second)
}
// NewEWMA5 constructs a new EWMA for a five-minute moving average.
func NewEWMA5() EWMA {
return NewEWMA(1-math.Exp(-5.0/60.0/5), 5*time.Second)
}
// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average.
func NewEWMA15() EWMA {
return NewEWMA(1-math.Exp(-5.0/60.0/15), 5*time.Second)
}
// EWMASnapshot is a read-only copy of another EWMA.
type EWMASnapshot float64
// Rate returns the rate of events per second at the time the snapshot was
// taken.
func (a EWMASnapshot) Rate() float64 { return float64(a) }
// Snapshot returns the snapshot.
func (a EWMASnapshot) Snapshot() EWMA { return a }
// Update is a no-op.
func (EWMASnapshot) Update(int64) {}
// NilEWMA is a no-op EWMA.
type NilEWMA struct{}
// Rate is a no-op.
func (NilEWMA) Rate() float64 { return 0.0 }
// Snapshot is a no-op.
func (NilEWMA) Snapshot() EWMA { return NilEWMA{} }
// Tick is a no-op.
func (NilEWMA) Tick() {}
// Update is a no-op.
func (NilEWMA) Update(n int64) {}
// StandardEWMA is the standard implementation of an EWMA.
type StandardEWMA struct {
alpha float64
period time.Duration
ewma float64
uncounted int64
timestamp time.Time
init bool
mutex sync.Mutex
}
func (s *StandardEWMA) updateRate() {
periods := time.Since(s.timestamp) / s.period
rate := float64(s.uncounted) / float64(s.period)
s.ewma = s.alpha*(rate) + (1-s.alpha)*s.ewma
s.timestamp = s.timestamp.Add(s.period)
s.uncounted = 0
periods -= 1
if !s.init {
s.ewma = rate
s.init = true
}
s.ewma = math.Pow(1-s.alpha, float64(periods)) * s.ewma
s.timestamp = s.timestamp.Add(time.Duration(periods) * s.period)
}
// Rate returns the moving average rate of events per second.
func (s *StandardEWMA) Rate() float64 {
s.mutex.Lock()
defer s.mutex.Unlock()
if time.Since(s.timestamp)/s.period < 1 {
return s.ewma * float64(time.Second)
}
s.updateRate()
return s.ewma * float64(time.Second)
}
// Snapshot returns a read-only copy of the EWMA.
func (s *StandardEWMA) Snapshot() EWMA {
return EWMASnapshot(s.Rate())
}
// Update registers n events that occured within the last ± 0.5 sec.
func (s *StandardEWMA) Update(n int64) {
s.mutex.Lock()
defer s.mutex.Unlock()
if time.Since(s.timestamp)/s.period < 1 {
s.uncounted += n
return
}
s.updateRate()
}
// Used to elapse time in unit tests.
func (s *StandardEWMA) addToTimestamp(d time.Duration) {
s.timestamp = s.timestamp.Add(d)
}