github.com/grafana/pyroscope@v1.18.0/pkg/segmentwriter/client/distributor/placement/adaptiveplacement/ewma/ewma.go (about) 1 package ewma 2 3 import ( 4 "math" 5 "time" 6 ) 7 8 // Rate is an exponentially weighted moving average event rate. 9 // The rate is always calculated per second and samples are 10 // summed up within the second duration. 11 type Rate struct { 12 lifetime float64 13 last int64 14 cumulative float64 15 ewma float64 16 } 17 18 // New creates a new rate with a given lifetime. 19 // 20 // lifetime is the time required for the decaying quantity to 21 // reduced to 1⁄e ≈ 0.367879441 times its initial value. 22 func New(lifetime time.Duration) *Rate { 23 return &Rate{lifetime: max(1, lifetime.Seconds())} 24 } 25 26 // NewHalfLife creates a new rate with a given half-life. 27 // 28 // halflife is the time required for the decaying quantity 29 // to fall to one half of its initial value. 30 func NewHalfLife(halflife time.Duration) *Rate { 31 // https://en.wikipedia.org/wiki/Exponential_decay: 32 // halflife = ln(2)/λ = tau * ln(2) 33 // tau = 1/λ = halflife/ln(2) 34 return &Rate{lifetime: max(1, halflife.Seconds()) / math.Ln2} 35 } 36 37 func (r *Rate) Value() float64 { return r.value(time.Now().UnixNano()) } 38 func (r *Rate) Update(v float64) { r.update(v, time.Now().UnixNano()) } 39 40 func (r *Rate) ValueAt(t int64) float64 { return r.value(t) } 41 func (r *Rate) UpdateAt(v float64, t int64) { r.update(v, t) } 42 43 func (r *Rate) LastUpdate() time.Time { return time.Unix(0, r.last) } 44 45 func (r *Rate) value(now int64) float64 { 46 delta := float64(now - r.last) 47 if delta < 0 { 48 return 0 49 } 50 delta /= 1e9 51 if delta >= 1 { 52 // Correct the result for the time passed since the last update. 53 // Over time, the delta increases and the decreased value (the 54 // instant rate cumulative/delta) dominates in both ways: 55 // directly and via the impact of the exponent. The value is 56 // asymptotically approaching zero, if no updates are made. 57 return ewma(r.ewma, r.cumulative/delta, delta, r.lifetime) 58 } 59 return r.ewma 60 } 61 62 // update updates the rate with a new value and timestamp in nanoseconds. 63 // It's assumed that time never goes backwards. 64 func (r *Rate) update(v float64, now int64) { 65 delta := float64(now - r.last) 66 if delta < 0 { 67 return 68 } 69 delta /= 1e9 70 if delta >= 1 { 71 r.ewma = ewma(r.ewma, r.cumulative/delta, delta, r.lifetime) 72 r.cumulative = 0 73 r.last = now 74 } 75 r.cumulative += v 76 } 77 78 func ewma(old, value, delta, lifetime float64) float64 { 79 alpha := 1 - math.Exp(-delta/lifetime) 80 return alpha*value + (1-alpha)*old 81 }