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  }