gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/common/metrics/sample.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  	"math/rand"
    22  	"sort"
    23  	"sync"
    24  	"time"
    25  )
    26  
    27  const rescaleThreshold = time.Hour
    28  
    29  // Samples maintain a statistically-significant selection of values from
    30  // a stream.
    31  type Sample interface {
    32  	Clear()
    33  	Count() int64
    34  	Max() int64
    35  	Mean() float64
    36  	Min() int64
    37  	Percentile(float64) float64
    38  	Percentiles([]float64) []float64
    39  	Size() int
    40  	Snapshot() Sample
    41  	StdDev() float64
    42  	Sum() int64
    43  	Update(int64)
    44  	Values() []int64
    45  	Variance() float64
    46  }
    47  
    48  // ExpDecaySample is an exponentially-decaying sample using a forward-decaying
    49  // priority reservoir.  See Cormode et al's "Forward Decay: A Practical Time
    50  // Decay Model for Streaming Systems".
    51  //
    52  // <http://dimacs.rutgers.edu/~graham/pubs/papers/fwddecay.pdf>
    53  type ExpDecaySample struct {
    54  	alpha         float64
    55  	count         int64
    56  	mutex         sync.Mutex
    57  	reservoirSize int
    58  	t0, t1        time.Time
    59  	values        *expDecaySampleHeap
    60  }
    61  
    62  // NewExpDecaySample constructs a new exponentially-decaying sample with the
    63  // given reservoir size and alpha.
    64  func NewExpDecaySample(reservoirSize int, alpha float64) Sample {
    65  	if !Enabled {
    66  		return NilSample{}
    67  	}
    68  	s := &ExpDecaySample{
    69  		alpha:         alpha,
    70  		reservoirSize: reservoirSize,
    71  		t0:            time.Now(),
    72  		values:        newExpDecaySampleHeap(reservoirSize),
    73  	}
    74  	s.t1 = s.t0.Add(rescaleThreshold)
    75  	return s
    76  }
    77  
    78  // Clear clears all samples.
    79  func (s *ExpDecaySample) Clear() {
    80  	s.mutex.Lock()
    81  	defer s.mutex.Unlock()
    82  	s.count = 0
    83  	s.t0 = time.Now()
    84  	s.t1 = s.t0.Add(rescaleThreshold)
    85  	s.values.Clear()
    86  }
    87  
    88  // Count returns the number of samples recorded, which may exceed the
    89  // reservoir size.
    90  func (s *ExpDecaySample) Count() int64 {
    91  	s.mutex.Lock()
    92  	defer s.mutex.Unlock()
    93  	return s.count
    94  }
    95  
    96  // Max returns the maximum value in the sample, which may not be the maximum
    97  // value ever to be part of the sample.
    98  func (s *ExpDecaySample) Max() int64 {
    99  	return SampleMax(s.Values())
   100  }
   101  
   102  // Mean returns the mean of the values in the sample.
   103  func (s *ExpDecaySample) Mean() float64 {
   104  	return SampleMean(s.Values())
   105  }
   106  
   107  // Min returns the minimum value in the sample, which may not be the minimum
   108  // value ever to be part of the sample.
   109  func (s *ExpDecaySample) Min() int64 {
   110  	return SampleMin(s.Values())
   111  }
   112  
   113  // Percentile returns an arbitrary percentile of values in the sample.
   114  func (s *ExpDecaySample) Percentile(p float64) float64 {
   115  	return SamplePercentile(s.Values(), p)
   116  }
   117  
   118  // Percentiles returns a slice of arbitrary percentiles of values in the
   119  // sample.
   120  func (s *ExpDecaySample) Percentiles(ps []float64) []float64 {
   121  	return SamplePercentiles(s.Values(), ps)
   122  }
   123  
   124  // Size returns the size of the sample, which is at most the reservoir size.
   125  func (s *ExpDecaySample) Size() int {
   126  	s.mutex.Lock()
   127  	defer s.mutex.Unlock()
   128  	return s.values.Size()
   129  }
   130  
   131  // Snapshot returns a read-only copy of the sample.
   132  func (s *ExpDecaySample) Snapshot() Sample {
   133  	s.mutex.Lock()
   134  	defer s.mutex.Unlock()
   135  	vals := s.values.Values()
   136  	values := make([]int64, len(vals))
   137  	for i, v := range vals {
   138  		values[i] = v.v
   139  	}
   140  	return &SampleSnapshot{
   141  		count:  s.count,
   142  		values: values,
   143  	}
   144  }
   145  
   146  // StdDev returns the standard deviation of the values in the sample.
   147  func (s *ExpDecaySample) StdDev() float64 {
   148  	return SampleStdDev(s.Values())
   149  }
   150  
   151  // Sum returns the sum of the values in the sample.
   152  func (s *ExpDecaySample) Sum() int64 {
   153  	return SampleSum(s.Values())
   154  }
   155  
   156  // Update samples a new value.
   157  func (s *ExpDecaySample) Update(v int64) {
   158  	s.update(time.Now(), v)
   159  }
   160  
   161  // Values returns a copy of the values in the sample.
   162  func (s *ExpDecaySample) Values() []int64 {
   163  	s.mutex.Lock()
   164  	defer s.mutex.Unlock()
   165  	vals := s.values.Values()
   166  	values := make([]int64, len(vals))
   167  	for i, v := range vals {
   168  		values[i] = v.v
   169  	}
   170  	return values
   171  }
   172  
   173  // Variance returns the variance of the values in the sample.
   174  func (s *ExpDecaySample) Variance() float64 {
   175  	return SampleVariance(s.Values())
   176  }
   177  
   178  // update samples a new value at a particular timestamp.  This is a method all
   179  // its own to facilitate testing.
   180  func (s *ExpDecaySample) update(t time.Time, v int64) {
   181  	s.mutex.Lock()
   182  	defer s.mutex.Unlock()
   183  	s.count++
   184  	if s.values.Size() == s.reservoirSize {
   185  		s.values.Pop()
   186  	}
   187  	s.values.Push(expDecaySample{
   188  		k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / rand.Float64(),
   189  		v: v,
   190  	})
   191  	if t.After(s.t1) {
   192  		values := s.values.Values()
   193  		t0 := s.t0
   194  		s.values.Clear()
   195  		s.t0 = t
   196  		s.t1 = s.t0.Add(rescaleThreshold)
   197  		for _, v := range values {
   198  			v.k = v.k * math.Exp(-s.alpha*s.t0.Sub(t0).Seconds())
   199  			s.values.Push(v)
   200  		}
   201  	}
   202  }
   203  
   204  // NilSample is a no-op Sample.
   205  type NilSample struct{}
   206  
   207  // Clear is a no-op.
   208  func (NilSample) Clear() {}
   209  
   210  // Count is a no-op.
   211  func (NilSample) Count() int64 { return 0 }
   212  
   213  // Max is a no-op.
   214  func (NilSample) Max() int64 { return 0 }
   215  
   216  // Mean is a no-op.
   217  func (NilSample) Mean() float64 { return 0.0 }
   218  
   219  // Min is a no-op.
   220  func (NilSample) Min() int64 { return 0 }
   221  
   222  // Percentile is a no-op.
   223  func (NilSample) Percentile(p float64) float64 { return 0.0 }
   224  
   225  // Percentiles is a no-op.
   226  func (NilSample) Percentiles(ps []float64) []float64 {
   227  	return make([]float64, len(ps))
   228  }
   229  
   230  // Size is a no-op.
   231  func (NilSample) Size() int { return 0 }
   232  
   233  // Sample is a no-op.
   234  func (NilSample) Snapshot() Sample { return NilSample{} }
   235  
   236  // StdDev is a no-op.
   237  func (NilSample) StdDev() float64 { return 0.0 }
   238  
   239  // Sum is a no-op.
   240  func (NilSample) Sum() int64 { return 0 }
   241  
   242  // Update is a no-op.
   243  func (NilSample) Update(v int64) {}
   244  
   245  // Values is a no-op.
   246  func (NilSample) Values() []int64 { return []int64{} }
   247  
   248  // Variance is a no-op.
   249  func (NilSample) Variance() float64 { return 0.0 }
   250  
   251  // SampleMax returns the maximum value of the slice of int64.
   252  func SampleMax(values []int64) int64 {
   253  	if 0 == len(values) {
   254  		return 0
   255  	}
   256  	var max int64 = math.MinInt64
   257  	for _, v := range values {
   258  		if max < v {
   259  			max = v
   260  		}
   261  	}
   262  	return max
   263  }
   264  
   265  // SampleMean returns the mean value of the slice of int64.
   266  func SampleMean(values []int64) float64 {
   267  	if 0 == len(values) {
   268  		return 0.0
   269  	}
   270  	return float64(SampleSum(values)) / float64(len(values))
   271  }
   272  
   273  // SampleMin returns the minimum value of the slice of int64.
   274  func SampleMin(values []int64) int64 {
   275  	if 0 == len(values) {
   276  		return 0
   277  	}
   278  	var min int64 = math.MaxInt64
   279  	for _, v := range values {
   280  		if min > v {
   281  			min = v
   282  		}
   283  	}
   284  	return min
   285  }
   286  
   287  // SamplePercentiles returns an arbitrary percentile of the slice of int64.
   288  func SamplePercentile(values int64Slice, p float64) float64 {
   289  	return SamplePercentiles(values, []float64{p})[0]
   290  }
   291  
   292  // SamplePercentiles returns a slice of arbitrary percentiles of the slice of
   293  // int64.
   294  func SamplePercentiles(values int64Slice, ps []float64) []float64 {
   295  	scores := make([]float64, len(ps))
   296  	size := len(values)
   297  	if size > 0 {
   298  		sort.Sort(values)
   299  		for i, p := range ps {
   300  			pos := p * float64(size+1)
   301  			if pos < 1.0 {
   302  				scores[i] = float64(values[0])
   303  			} else if pos >= float64(size) {
   304  				scores[i] = float64(values[size-1])
   305  			} else {
   306  				lower := float64(values[int(pos)-1])
   307  				upper := float64(values[int(pos)])
   308  				scores[i] = lower + (pos-math.Floor(pos))*(upper-lower)
   309  			}
   310  		}
   311  	}
   312  	return scores
   313  }
   314  
   315  // SampleSnapshot is a read-only copy of another Sample.
   316  type SampleSnapshot struct {
   317  	count  int64
   318  	values []int64
   319  }
   320  
   321  func NewSampleSnapshot(count int64, values []int64) *SampleSnapshot {
   322  	return &SampleSnapshot{
   323  		count:  count,
   324  		values: values,
   325  	}
   326  }
   327  
   328  // Clear panics.
   329  func (*SampleSnapshot) Clear() {
   330  	panic("Clear called on a SampleSnapshot")
   331  }
   332  
   333  // Count returns the count of inputs at the time the snapshot was taken.
   334  func (s *SampleSnapshot) Count() int64 { return s.count }
   335  
   336  // Max returns the maximal value at the time the snapshot was taken.
   337  func (s *SampleSnapshot) Max() int64 { return SampleMax(s.values) }
   338  
   339  // Mean returns the mean value at the time the snapshot was taken.
   340  func (s *SampleSnapshot) Mean() float64 { return SampleMean(s.values) }
   341  
   342  // Min returns the minimal value at the time the snapshot was taken.
   343  func (s *SampleSnapshot) Min() int64 { return SampleMin(s.values) }
   344  
   345  // Percentile returns an arbitrary percentile of values at the time the
   346  // snapshot was taken.
   347  func (s *SampleSnapshot) Percentile(p float64) float64 {
   348  	return SamplePercentile(s.values, p)
   349  }
   350  
   351  // Percentiles returns a slice of arbitrary percentiles of values at the time
   352  // the snapshot was taken.
   353  func (s *SampleSnapshot) Percentiles(ps []float64) []float64 {
   354  	return SamplePercentiles(s.values, ps)
   355  }
   356  
   357  // Size returns the size of the sample at the time the snapshot was taken.
   358  func (s *SampleSnapshot) Size() int { return len(s.values) }
   359  
   360  // Snapshot returns the snapshot.
   361  func (s *SampleSnapshot) Snapshot() Sample { return s }
   362  
   363  // StdDev returns the standard deviation of values at the time the snapshot was
   364  // taken.
   365  func (s *SampleSnapshot) StdDev() float64 { return SampleStdDev(s.values) }
   366  
   367  // Sum returns the sum of values at the time the snapshot was taken.
   368  func (s *SampleSnapshot) Sum() int64 { return SampleSum(s.values) }
   369  
   370  // Update panics.
   371  func (*SampleSnapshot) Update(int64) {
   372  	panic("Update called on a SampleSnapshot")
   373  }
   374  
   375  // Values returns a copy of the values in the sample.
   376  func (s *SampleSnapshot) Values() []int64 {
   377  	values := make([]int64, len(s.values))
   378  	copy(values, s.values)
   379  	return values
   380  }
   381  
   382  // Variance returns the variance of values at the time the snapshot was taken.
   383  func (s *SampleSnapshot) Variance() float64 { return SampleVariance(s.values) }
   384  
   385  // SampleStdDev returns the standard deviation of the slice of int64.
   386  func SampleStdDev(values []int64) float64 {
   387  	return math.Sqrt(SampleVariance(values))
   388  }
   389  
   390  // SampleSum returns the sum of the slice of int64.
   391  func SampleSum(values []int64) int64 {
   392  	var sum int64
   393  	for _, v := range values {
   394  		sum += v
   395  	}
   396  	return sum
   397  }
   398  
   399  // SampleVariance returns the variance of the slice of int64.
   400  func SampleVariance(values []int64) float64 {
   401  	if 0 == len(values) {
   402  		return 0.0
   403  	}
   404  	m := SampleMean(values)
   405  	var sum float64
   406  	for _, v := range values {
   407  		d := float64(v) - m
   408  		sum += d * d
   409  	}
   410  	return sum / float64(len(values))
   411  }
   412  
   413  // A uniform sample using Vitter's Algorithm R.
   414  //
   415  // <http://www.cs.umd.edu/~samir/498/vitter.pdf>
   416  type UniformSample struct {
   417  	count         int64
   418  	mutex         sync.Mutex
   419  	reservoirSize int
   420  	values        []int64
   421  }
   422  
   423  // NewUniformSample constructs a new uniform sample with the given reservoir
   424  // size.
   425  func NewUniformSample(reservoirSize int) Sample {
   426  	if !Enabled {
   427  		return NilSample{}
   428  	}
   429  	return &UniformSample{
   430  		reservoirSize: reservoirSize,
   431  		values:        make([]int64, 0, reservoirSize),
   432  	}
   433  }
   434  
   435  // Clear clears all samples.
   436  func (s *UniformSample) Clear() {
   437  	s.mutex.Lock()
   438  	defer s.mutex.Unlock()
   439  	s.count = 0
   440  	s.values = make([]int64, 0, s.reservoirSize)
   441  }
   442  
   443  // Count returns the number of samples recorded, which may exceed the
   444  // reservoir size.
   445  func (s *UniformSample) Count() int64 {
   446  	s.mutex.Lock()
   447  	defer s.mutex.Unlock()
   448  	return s.count
   449  }
   450  
   451  // Max returns the maximum value in the sample, which may not be the maximum
   452  // value ever to be part of the sample.
   453  func (s *UniformSample) Max() int64 {
   454  	s.mutex.Lock()
   455  	defer s.mutex.Unlock()
   456  	return SampleMax(s.values)
   457  }
   458  
   459  // Mean returns the mean of the values in the sample.
   460  func (s *UniformSample) Mean() float64 {
   461  	s.mutex.Lock()
   462  	defer s.mutex.Unlock()
   463  	return SampleMean(s.values)
   464  }
   465  
   466  // Min returns the minimum value in the sample, which may not be the minimum
   467  // value ever to be part of the sample.
   468  func (s *UniformSample) Min() int64 {
   469  	s.mutex.Lock()
   470  	defer s.mutex.Unlock()
   471  	return SampleMin(s.values)
   472  }
   473  
   474  // Percentile returns an arbitrary percentile of values in the sample.
   475  func (s *UniformSample) Percentile(p float64) float64 {
   476  	s.mutex.Lock()
   477  	defer s.mutex.Unlock()
   478  	return SamplePercentile(s.values, p)
   479  }
   480  
   481  // Percentiles returns a slice of arbitrary percentiles of values in the
   482  // sample.
   483  func (s *UniformSample) Percentiles(ps []float64) []float64 {
   484  	s.mutex.Lock()
   485  	defer s.mutex.Unlock()
   486  	return SamplePercentiles(s.values, ps)
   487  }
   488  
   489  // Size returns the size of the sample, which is at most the reservoir size.
   490  func (s *UniformSample) Size() int {
   491  	s.mutex.Lock()
   492  	defer s.mutex.Unlock()
   493  	return len(s.values)
   494  }
   495  
   496  // Snapshot returns a read-only copy of the sample.
   497  func (s *UniformSample) Snapshot() Sample {
   498  	s.mutex.Lock()
   499  	defer s.mutex.Unlock()
   500  	values := make([]int64, len(s.values))
   501  	copy(values, s.values)
   502  	return &SampleSnapshot{
   503  		count:  s.count,
   504  		values: values,
   505  	}
   506  }
   507  
   508  // StdDev returns the standard deviation of the values in the sample.
   509  func (s *UniformSample) StdDev() float64 {
   510  	s.mutex.Lock()
   511  	defer s.mutex.Unlock()
   512  	return SampleStdDev(s.values)
   513  }
   514  
   515  // Sum returns the sum of the values in the sample.
   516  func (s *UniformSample) Sum() int64 {
   517  	s.mutex.Lock()
   518  	defer s.mutex.Unlock()
   519  	return SampleSum(s.values)
   520  }
   521  
   522  // Update samples a new value.
   523  func (s *UniformSample) Update(v int64) {
   524  	s.mutex.Lock()
   525  	defer s.mutex.Unlock()
   526  	s.count++
   527  	if len(s.values) < s.reservoirSize {
   528  		s.values = append(s.values, v)
   529  	} else {
   530  		r := rand.Int63n(s.count)
   531  		if r < int64(len(s.values)) {
   532  			s.values[int(r)] = v
   533  		}
   534  	}
   535  }
   536  
   537  // Values returns a copy of the values in the sample.
   538  func (s *UniformSample) Values() []int64 {
   539  	s.mutex.Lock()
   540  	defer s.mutex.Unlock()
   541  	values := make([]int64, len(s.values))
   542  	copy(values, s.values)
   543  	return values
   544  }
   545  
   546  // Variance returns the variance of the values in the sample.
   547  func (s *UniformSample) Variance() float64 {
   548  	s.mutex.Lock()
   549  	defer s.mutex.Unlock()
   550  	return SampleVariance(s.values)
   551  }
   552  
   553  // expDecaySample represents an individual sample in a heap.
   554  type expDecaySample struct {
   555  	k float64
   556  	v int64
   557  }
   558  
   559  func newExpDecaySampleHeap(reservoirSize int) *expDecaySampleHeap {
   560  	return &expDecaySampleHeap{make([]expDecaySample, 0, reservoirSize)}
   561  }
   562  
   563  // expDecaySampleHeap is a min-heap of expDecaySamples.
   564  // The internal implementation is copied from the standard library's container/heap
   565  type expDecaySampleHeap struct {
   566  	s []expDecaySample
   567  }
   568  
   569  func (h *expDecaySampleHeap) Clear() {
   570  	h.s = h.s[:0]
   571  }
   572  
   573  func (h *expDecaySampleHeap) Push(s expDecaySample) {
   574  	n := len(h.s)
   575  	h.s = h.s[0 : n+1]
   576  	h.s[n] = s
   577  	h.up(n)
   578  }
   579  
   580  func (h *expDecaySampleHeap) Pop() expDecaySample {
   581  	n := len(h.s) - 1
   582  	h.s[0], h.s[n] = h.s[n], h.s[0]
   583  	h.down(0, n)
   584  
   585  	n = len(h.s)
   586  	s := h.s[n-1]
   587  	h.s = h.s[0 : n-1]
   588  	return s
   589  }
   590  
   591  func (h *expDecaySampleHeap) Size() int {
   592  	return len(h.s)
   593  }
   594  
   595  func (h *expDecaySampleHeap) Values() []expDecaySample {
   596  	return h.s
   597  }
   598  
   599  func (h *expDecaySampleHeap) up(j int) {
   600  	for {
   601  		i := (j - 1) / 2 // parent
   602  		if i == j || !(h.s[j].k < h.s[i].k) {
   603  			break
   604  		}
   605  		h.s[i], h.s[j] = h.s[j], h.s[i]
   606  		j = i
   607  	}
   608  }
   609  
   610  func (h *expDecaySampleHeap) down(i, n int) {
   611  	for {
   612  		j1 := 2*i + 1
   613  		if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
   614  			break
   615  		}
   616  		j := j1 // left child
   617  		if j2 := j1 + 1; j2 < n && !(h.s[j1].k < h.s[j2].k) {
   618  			j = j2 // = 2*i + 2  // right child
   619  		}
   620  		if !(h.s[j].k < h.s[i].k) {
   621  			break
   622  		}
   623  		h.s[i], h.s[j] = h.s[j], h.s[i]
   624  		i = j
   625  	}
   626  }
   627  
   628  type int64Slice []int64
   629  
   630  func (p int64Slice) Len() int           { return len(p) }
   631  func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
   632  func (p int64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }