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