github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/metrics/sample.go (about)

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