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  }