github.com/calmw/ethereum@v0.1.1/metrics/sample.go (about)

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