github.com/codingfuture/orig-energi3@v0.8.4/metrics/ewma.go (about) 1 // Copyright 2018 The Energi Core Authors 2 // Copyright 2015 The go-ethereum Authors 3 // This file is part of the Energi Core library. 4 // 5 // The Energi Core library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The Energi Core library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the Energi Core library. If not, see <http://www.gnu.org/licenses/>. 17 18 package metrics 19 20 import ( 21 "math" 22 "sync" 23 "sync/atomic" 24 ) 25 26 // EWMAs continuously calculate an exponentially-weighted moving average 27 // based on an outside source of clock ticks. 28 type EWMA interface { 29 Rate() float64 30 Snapshot() EWMA 31 Tick() 32 Update(int64) 33 } 34 35 // NewEWMA constructs a new EWMA with the given alpha. 36 func NewEWMA(alpha float64) EWMA { 37 return &StandardEWMA{alpha: alpha} 38 } 39 40 // NewEWMA1 constructs a new EWMA for a one-minute moving average. 41 func NewEWMA1() EWMA { 42 return NewEWMA(1 - math.Exp(-5.0/60.0/1)) 43 } 44 45 // NewEWMA5 constructs a new EWMA for a five-minute moving average. 46 func NewEWMA5() EWMA { 47 return NewEWMA(1 - math.Exp(-5.0/60.0/5)) 48 } 49 50 // NewEWMA15 constructs a new EWMA for a fifteen-minute moving average. 51 func NewEWMA15() EWMA { 52 return NewEWMA(1 - math.Exp(-5.0/60.0/15)) 53 } 54 55 // EWMASnapshot is a read-only copy of another EWMA. 56 type EWMASnapshot float64 57 58 // Rate returns the rate of events per second at the time the snapshot was 59 // taken. 60 func (a EWMASnapshot) Rate() float64 { return float64(a) } 61 62 // Snapshot returns the snapshot. 63 func (a EWMASnapshot) Snapshot() EWMA { return a } 64 65 // Tick panics. 66 func (EWMASnapshot) Tick() { 67 panic("Tick called on an EWMASnapshot") 68 } 69 70 // Update panics. 71 func (EWMASnapshot) Update(int64) { 72 panic("Update called on an EWMASnapshot") 73 } 74 75 // NilEWMA is a no-op EWMA. 76 type NilEWMA struct{} 77 78 // Rate is a no-op. 79 func (NilEWMA) Rate() float64 { return 0.0 } 80 81 // Snapshot is a no-op. 82 func (NilEWMA) Snapshot() EWMA { return NilEWMA{} } 83 84 // Tick is a no-op. 85 func (NilEWMA) Tick() {} 86 87 // Update is a no-op. 88 func (NilEWMA) Update(n int64) {} 89 90 // StandardEWMA is the standard implementation of an EWMA and tracks the number 91 // of uncounted events and processes them on each tick. It uses the 92 // sync/atomic package to manage uncounted events. 93 type StandardEWMA struct { 94 uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment 95 alpha float64 96 rate float64 97 init bool 98 mutex sync.Mutex 99 } 100 101 // Rate returns the moving average rate of events per second. 102 func (a *StandardEWMA) Rate() float64 { 103 a.mutex.Lock() 104 defer a.mutex.Unlock() 105 return a.rate * float64(1e9) 106 } 107 108 // Snapshot returns a read-only copy of the EWMA. 109 func (a *StandardEWMA) Snapshot() EWMA { 110 return EWMASnapshot(a.Rate()) 111 } 112 113 // Tick ticks the clock to update the moving average. It assumes it is called 114 // every five seconds. 115 func (a *StandardEWMA) Tick() { 116 count := atomic.LoadInt64(&a.uncounted) 117 atomic.AddInt64(&a.uncounted, -count) 118 instantRate := float64(count) / float64(5e9) 119 a.mutex.Lock() 120 defer a.mutex.Unlock() 121 if a.init { 122 a.rate += a.alpha * (instantRate - a.rate) 123 } else { 124 a.init = true 125 a.rate = instantRate 126 } 127 } 128 129 // Update adds n uncounted events. 130 func (a *StandardEWMA) Update(n int64) { 131 atomic.AddInt64(&a.uncounted, n) 132 }