github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/metrics/resetting_timer.go (about)

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