...
1 package metrics
2
3 import (
4 "math"
5 "sync"
6 "sync/atomic"
7 )
8
9
10
11 type EWMA interface {
12 Rate() float64
13 Snapshot() EWMA
14 Tick()
15 Update(int64)
16 }
17
18
19 func NewEWMA(alpha float64) EWMA {
20 if UseNilMetrics {
21 return NilEWMA{}
22 }
23 return &StandardEWMA{alpha: alpha}
24 }
25
26
27 func NewEWMA1() EWMA {
28 return NewEWMA(1 - math.Exp(-5.0/60.0/1))
29 }
30
31
32 func NewEWMA5() EWMA {
33 return NewEWMA(1 - math.Exp(-5.0/60.0/5))
34 }
35
36
37 func NewEWMA15() EWMA {
38 return NewEWMA(1 - math.Exp(-5.0/60.0/15))
39 }
40
41
42 type EWMASnapshot float64
43
44
45
46 func (a EWMASnapshot) Rate() float64 { return float64(a) }
47
48
49 func (a EWMASnapshot) Snapshot() EWMA { return a }
50
51
52 func (EWMASnapshot) Tick() {
53 panic("Tick called on an EWMASnapshot")
54 }
55
56
57 func (EWMASnapshot) Update(int64) {
58 panic("Update called on an EWMASnapshot")
59 }
60
61
62 type NilEWMA struct{}
63
64
65 func (NilEWMA) Rate() float64 { return 0.0 }
66
67
68 func (NilEWMA) Snapshot() EWMA { return NilEWMA{} }
69
70
71 func (NilEWMA) Tick() {}
72
73
74 func (NilEWMA) Update(n int64) {}
75
76
77
78
79 type StandardEWMA struct {
80 uncounted int64
81 alpha float64
82 rate uint64
83 init uint32
84 mutex sync.Mutex
85 }
86
87
88 func (a *StandardEWMA) Rate() float64 {
89 currentRate := math.Float64frombits(atomic.LoadUint64(&a.rate)) * float64(1e9)
90 return currentRate
91 }
92
93
94 func (a *StandardEWMA) Snapshot() EWMA {
95 return EWMASnapshot(a.Rate())
96 }
97
98
99
100 func (a *StandardEWMA) Tick() {
101
102 if atomic.LoadUint32(&a.init) == 1 {
103 a.updateRate(a.fetchInstantRate())
104 } else {
105
106
107
108
109 a.mutex.Lock()
110 if atomic.LoadUint32(&a.init) == 1 {
111
112
113 a.updateRate(a.fetchInstantRate())
114 } else {
115 atomic.StoreUint32(&a.init, 1)
116 atomic.StoreUint64(&a.rate, math.Float64bits(a.fetchInstantRate()))
117 }
118 a.mutex.Unlock()
119 }
120 }
121
122 func (a *StandardEWMA) fetchInstantRate() float64 {
123 count := atomic.LoadInt64(&a.uncounted)
124 atomic.AddInt64(&a.uncounted, -count)
125 instantRate := float64(count) / float64(5e9)
126 return instantRate
127 }
128
129 func (a *StandardEWMA) updateRate(instantRate float64) {
130 currentRate := math.Float64frombits(atomic.LoadUint64(&a.rate))
131 currentRate += a.alpha * (instantRate - currentRate)
132 atomic.StoreUint64(&a.rate, math.Float64bits(currentRate))
133 }
134
135
136 func (a *StandardEWMA) Update(n int64) {
137 atomic.AddInt64(&a.uncounted, n)
138 }
139
View as plain text