github.com/codingfuture/orig-energi3@v0.8.4/metrics/resetting_timer.go (about)

     1  // Copyright 2018 The Energi Core Authors
     2  // Copyright 2018 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  	"sort"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  // Initial slice capacity for the values stored in a ResettingTimer
    28  const InitialResettingTimerSliceCap = 10
    29  
    30  // ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval.
    31  type ResettingTimer interface {
    32  	Values() []int64
    33  	Snapshot() ResettingTimer
    34  	Percentiles([]float64) []int64
    35  	Mean() float64
    36  	Time(func())
    37  	Update(time.Duration)
    38  	UpdateSince(time.Time)
    39  }
    40  
    41  // GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a
    42  // new StandardResettingTimer.
    43  func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer {
    44  	if nil == r {
    45  		r = DefaultRegistry
    46  	}
    47  	return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer)
    48  }
    49  
    50  // NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer.
    51  func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer {
    52  	c := NewResettingTimer()
    53  	if nil == r {
    54  		r = DefaultRegistry
    55  	}
    56  	r.Register(name, c)
    57  	return c
    58  }
    59  
    60  // NewResettingTimer constructs a new StandardResettingTimer
    61  func NewResettingTimer() ResettingTimer {
    62  	if !Enabled {
    63  		return NilResettingTimer{}
    64  	}
    65  	return &StandardResettingTimer{
    66  		values: make([]int64, 0, InitialResettingTimerSliceCap),
    67  	}
    68  }
    69  
    70  // NilResettingTimer is a no-op ResettingTimer.
    71  type NilResettingTimer struct {
    72  }
    73  
    74  // Values is a no-op.
    75  func (NilResettingTimer) Values() []int64 { return nil }
    76  
    77  // Snapshot is a no-op.
    78  func (NilResettingTimer) Snapshot() ResettingTimer {
    79  	return &ResettingTimerSnapshot{
    80  		values: []int64{},
    81  	}
    82  }
    83  
    84  // Time is a no-op.
    85  func (NilResettingTimer) Time(func()) {}
    86  
    87  // Update is a no-op.
    88  func (NilResettingTimer) Update(time.Duration) {}
    89  
    90  // Percentiles panics.
    91  func (NilResettingTimer) Percentiles([]float64) []int64 {
    92  	panic("Percentiles called on a NilResettingTimer")
    93  }
    94  
    95  // Mean panics.
    96  func (NilResettingTimer) Mean() float64 {
    97  	panic("Mean called on a NilResettingTimer")
    98  }
    99  
   100  // UpdateSince is a no-op.
   101  func (NilResettingTimer) UpdateSince(time.Time) {}
   102  
   103  // StandardResettingTimer is the standard implementation of a ResettingTimer.
   104  // and Meter.
   105  type StandardResettingTimer struct {
   106  	values []int64
   107  	mutex  sync.Mutex
   108  }
   109  
   110  // Values returns a slice with all measurements.
   111  func (t *StandardResettingTimer) Values() []int64 {
   112  	return t.values
   113  }
   114  
   115  // Snapshot resets the timer and returns a read-only copy of its contents.
   116  func (t *StandardResettingTimer) Snapshot() ResettingTimer {
   117  	t.mutex.Lock()
   118  	defer t.mutex.Unlock()
   119  	currentValues := t.values
   120  	t.values = make([]int64, 0, InitialResettingTimerSliceCap)
   121  
   122  	return &ResettingTimerSnapshot{
   123  		values: currentValues,
   124  	}
   125  }
   126  
   127  // Percentiles panics.
   128  func (t *StandardResettingTimer) Percentiles([]float64) []int64 {
   129  	panic("Percentiles called on a StandardResettingTimer")
   130  }
   131  
   132  // Mean panics.
   133  func (t *StandardResettingTimer) Mean() float64 {
   134  	panic("Mean called on a StandardResettingTimer")
   135  }
   136  
   137  // Record the duration of the execution of the given function.
   138  func (t *StandardResettingTimer) Time(f func()) {
   139  	ts := time.Now()
   140  	f()
   141  	t.Update(time.Since(ts))
   142  }
   143  
   144  // Record the duration of an event.
   145  func (t *StandardResettingTimer) Update(d time.Duration) {
   146  	t.mutex.Lock()
   147  	defer t.mutex.Unlock()
   148  	t.values = append(t.values, int64(d))
   149  }
   150  
   151  // Record the duration of an event that started at a time and ends now.
   152  func (t *StandardResettingTimer) UpdateSince(ts time.Time) {
   153  	t.mutex.Lock()
   154  	defer t.mutex.Unlock()
   155  	t.values = append(t.values, int64(time.Since(ts)))
   156  }
   157  
   158  // ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer.
   159  type ResettingTimerSnapshot struct {
   160  	values              []int64
   161  	mean                float64
   162  	thresholdBoundaries []int64
   163  	calculated          bool
   164  }
   165  
   166  // Snapshot returns the snapshot.
   167  func (t *ResettingTimerSnapshot) Snapshot() ResettingTimer { return t }
   168  
   169  // Time panics.
   170  func (*ResettingTimerSnapshot) Time(func()) {
   171  	panic("Time called on a ResettingTimerSnapshot")
   172  }
   173  
   174  // Update panics.
   175  func (*ResettingTimerSnapshot) Update(time.Duration) {
   176  	panic("Update called on a ResettingTimerSnapshot")
   177  }
   178  
   179  // UpdateSince panics.
   180  func (*ResettingTimerSnapshot) UpdateSince(time.Time) {
   181  	panic("UpdateSince called on a ResettingTimerSnapshot")
   182  }
   183  
   184  // Values returns all values from snapshot.
   185  func (t *ResettingTimerSnapshot) Values() []int64 {
   186  	return t.values
   187  }
   188  
   189  // Percentiles returns the boundaries for the input percentiles.
   190  func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []int64 {
   191  	t.calc(percentiles)
   192  
   193  	return t.thresholdBoundaries
   194  }
   195  
   196  // Mean returns the mean of the snapshotted values
   197  func (t *ResettingTimerSnapshot) Mean() float64 {
   198  	if !t.calculated {
   199  		t.calc([]float64{})
   200  	}
   201  
   202  	return t.mean
   203  }
   204  
   205  func (t *ResettingTimerSnapshot) calc(percentiles []float64) {
   206  	sort.Sort(Int64Slice(t.values))
   207  
   208  	count := len(t.values)
   209  	if count > 0 {
   210  		min := t.values[0]
   211  		max := t.values[count-1]
   212  
   213  		cumulativeValues := make([]int64, count)
   214  		cumulativeValues[0] = min
   215  		for i := 1; i < count; i++ {
   216  			cumulativeValues[i] = t.values[i] + cumulativeValues[i-1]
   217  		}
   218  
   219  		t.thresholdBoundaries = make([]int64, len(percentiles))
   220  
   221  		thresholdBoundary := max
   222  
   223  		for i, pct := range percentiles {
   224  			if count > 1 {
   225  				var abs float64
   226  				if pct >= 0 {
   227  					abs = pct
   228  				} else {
   229  					abs = 100 + pct
   230  				}
   231  				// poor man's math.Round(x):
   232  				// math.Floor(x + 0.5)
   233  				indexOfPerc := int(math.Floor(((abs / 100.0) * float64(count)) + 0.5))
   234  				if pct >= 0 && indexOfPerc > 0 {
   235  					indexOfPerc -= 1 // index offset=0
   236  				}
   237  				thresholdBoundary = t.values[indexOfPerc]
   238  			}
   239  
   240  			t.thresholdBoundaries[i] = thresholdBoundary
   241  		}
   242  
   243  		sum := cumulativeValues[count-1]
   244  		t.mean = float64(sum) / float64(count)
   245  	} else {
   246  		t.thresholdBoundaries = make([]int64, len(percentiles))
   247  		t.mean = 0
   248  	}
   249  
   250  	t.calculated = true
   251  }
   252  
   253  // Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order.
   254  type Int64Slice []int64
   255  
   256  func (s Int64Slice) Len() int           { return len(s) }
   257  func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] }
   258  func (s Int64Slice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }