github.com/theQRL/go-zond@v0.1.1/metrics/sample_test.go (about)

     1  package metrics
     2  
     3  import (
     4  	"math"
     5  	"math/rand"
     6  	"runtime"
     7  	"testing"
     8  	"time"
     9  )
    10  
    11  const epsilonPercentile = .00000000001
    12  
    13  // Benchmark{Compute,Copy}{1000,1000000} demonstrate that, even for relatively
    14  // expensive computations like Variance, the cost of copying the Sample, as
    15  // approximated by a make and copy, is much greater than the cost of the
    16  // computation for small samples and only slightly less for large samples.
    17  func BenchmarkCompute1000(b *testing.B) {
    18  	s := make([]int64, 1000)
    19  	var sum int64
    20  	for i := 0; i < len(s); i++ {
    21  		s[i] = int64(i)
    22  		sum += int64(i)
    23  	}
    24  	mean := float64(sum) / float64(len(s))
    25  	b.ResetTimer()
    26  	for i := 0; i < b.N; i++ {
    27  		SampleVariance(mean, s)
    28  	}
    29  }
    30  func BenchmarkCompute1000000(b *testing.B) {
    31  	s := make([]int64, 1000000)
    32  	var sum int64
    33  	for i := 0; i < len(s); i++ {
    34  		s[i] = int64(i)
    35  		sum += int64(i)
    36  	}
    37  	mean := float64(sum) / float64(len(s))
    38  	b.ResetTimer()
    39  	for i := 0; i < b.N; i++ {
    40  		SampleVariance(mean, s)
    41  	}
    42  }
    43  func BenchmarkCopy1000(b *testing.B) {
    44  	s := make([]int64, 1000)
    45  	for i := 0; i < len(s); i++ {
    46  		s[i] = int64(i)
    47  	}
    48  	b.ResetTimer()
    49  	for i := 0; i < b.N; i++ {
    50  		sCopy := make([]int64, len(s))
    51  		copy(sCopy, s)
    52  	}
    53  }
    54  func BenchmarkCopy1000000(b *testing.B) {
    55  	s := make([]int64, 1000000)
    56  	for i := 0; i < len(s); i++ {
    57  		s[i] = int64(i)
    58  	}
    59  	b.ResetTimer()
    60  	for i := 0; i < b.N; i++ {
    61  		sCopy := make([]int64, len(s))
    62  		copy(sCopy, s)
    63  	}
    64  }
    65  
    66  func BenchmarkExpDecaySample257(b *testing.B) {
    67  	benchmarkSample(b, NewExpDecaySample(257, 0.015))
    68  }
    69  
    70  func BenchmarkExpDecaySample514(b *testing.B) {
    71  	benchmarkSample(b, NewExpDecaySample(514, 0.015))
    72  }
    73  
    74  func BenchmarkExpDecaySample1028(b *testing.B) {
    75  	benchmarkSample(b, NewExpDecaySample(1028, 0.015))
    76  }
    77  
    78  func BenchmarkUniformSample257(b *testing.B) {
    79  	benchmarkSample(b, NewUniformSample(257))
    80  }
    81  
    82  func BenchmarkUniformSample514(b *testing.B) {
    83  	benchmarkSample(b, NewUniformSample(514))
    84  }
    85  
    86  func BenchmarkUniformSample1028(b *testing.B) {
    87  	benchmarkSample(b, NewUniformSample(1028))
    88  }
    89  
    90  func min(a, b int) int {
    91  	if a < b {
    92  		return a
    93  	}
    94  	return b
    95  }
    96  
    97  func TestExpDecaySample(t *testing.T) {
    98  	for _, tc := range []struct {
    99  		reservoirSize int
   100  		alpha         float64
   101  		updates       int
   102  	}{
   103  		{100, 0.99, 10},
   104  		{1000, 0.01, 100},
   105  		{100, 0.99, 1000},
   106  	} {
   107  		sample := NewExpDecaySample(tc.reservoirSize, tc.alpha)
   108  		for i := 0; i < tc.updates; i++ {
   109  			sample.Update(int64(i))
   110  		}
   111  		snap := sample.Snapshot()
   112  		if have, want := int(snap.Count()), tc.updates; have != want {
   113  			t.Errorf("have %d want %d", have, want)
   114  		}
   115  		if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want {
   116  			t.Errorf("have %d want %d", have, want)
   117  		}
   118  		values := snap.(*sampleSnapshot).values
   119  		if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want {
   120  			t.Errorf("have %d want %d", have, want)
   121  		}
   122  		for _, v := range values {
   123  			if v > int64(tc.updates) || v < 0 {
   124  				t.Errorf("out of range [0, %d): %v", tc.updates, v)
   125  			}
   126  		}
   127  	}
   128  }
   129  
   130  // This test makes sure that the sample's priority is not amplified by using
   131  // nanosecond duration since start rather than second duration since start.
   132  // The priority becomes +Inf quickly after starting if this is done,
   133  // effectively freezing the set of samples until a rescale step happens.
   134  func TestExpDecaySampleNanosecondRegression(t *testing.T) {
   135  	sw := NewExpDecaySample(100, 0.99)
   136  	for i := 0; i < 100; i++ {
   137  		sw.Update(10)
   138  	}
   139  	time.Sleep(1 * time.Millisecond)
   140  	for i := 0; i < 100; i++ {
   141  		sw.Update(20)
   142  	}
   143  	s := sw.Snapshot()
   144  	v := s.(*sampleSnapshot).values
   145  	avg := float64(0)
   146  	for i := 0; i < len(v); i++ {
   147  		avg += float64(v[i])
   148  	}
   149  	avg /= float64(len(v))
   150  	if avg > 16 || avg < 14 {
   151  		t.Errorf("out of range [14, 16]: %v\n", avg)
   152  	}
   153  }
   154  
   155  func TestExpDecaySampleRescale(t *testing.T) {
   156  	s := NewExpDecaySample(2, 0.001).(*ExpDecaySample)
   157  	s.update(time.Now(), 1)
   158  	s.update(time.Now().Add(time.Hour+time.Microsecond), 1)
   159  	for _, v := range s.values.Values() {
   160  		if v.k == 0.0 {
   161  			t.Fatal("v.k == 0.0")
   162  		}
   163  	}
   164  }
   165  
   166  func TestExpDecaySampleSnapshot(t *testing.T) {
   167  	now := time.Now()
   168  	s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1)))
   169  	for i := 1; i <= 10000; i++ {
   170  		s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
   171  	}
   172  	snapshot := s.Snapshot()
   173  	s.Update(1)
   174  	testExpDecaySampleStatistics(t, snapshot)
   175  }
   176  
   177  func TestExpDecaySampleStatistics(t *testing.T) {
   178  	now := time.Now()
   179  	s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1)))
   180  	for i := 1; i <= 10000; i++ {
   181  		s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
   182  	}
   183  	testExpDecaySampleStatistics(t, s.Snapshot())
   184  }
   185  
   186  func TestUniformSample(t *testing.T) {
   187  	sw := NewUniformSample(100)
   188  	for i := 0; i < 1000; i++ {
   189  		sw.Update(int64(i))
   190  	}
   191  	s := sw.Snapshot()
   192  	if size := s.Count(); size != 1000 {
   193  		t.Errorf("s.Count(): 1000 != %v\n", size)
   194  	}
   195  	if size := s.Size(); size != 100 {
   196  		t.Errorf("s.Size(): 100 != %v\n", size)
   197  	}
   198  	values := s.(*sampleSnapshot).values
   199  
   200  	if l := len(values); l != 100 {
   201  		t.Errorf("len(s.Values()): 100 != %v\n", l)
   202  	}
   203  	for _, v := range values {
   204  		if v > 1000 || v < 0 {
   205  			t.Errorf("out of range [0, 100): %v\n", v)
   206  		}
   207  	}
   208  }
   209  
   210  func TestUniformSampleIncludesTail(t *testing.T) {
   211  	sw := NewUniformSample(100)
   212  	max := 100
   213  	for i := 0; i < max; i++ {
   214  		sw.Update(int64(i))
   215  	}
   216  	s := sw.Snapshot()
   217  	v := s.(*sampleSnapshot).values
   218  	sum := 0
   219  	exp := (max - 1) * max / 2
   220  	for i := 0; i < len(v); i++ {
   221  		sum += int(v[i])
   222  	}
   223  	if exp != sum {
   224  		t.Errorf("sum: %v != %v\n", exp, sum)
   225  	}
   226  }
   227  
   228  func TestUniformSampleSnapshot(t *testing.T) {
   229  	s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1)))
   230  	for i := 1; i <= 10000; i++ {
   231  		s.Update(int64(i))
   232  	}
   233  	snapshot := s.Snapshot()
   234  	s.Update(1)
   235  	testUniformSampleStatistics(t, snapshot)
   236  }
   237  
   238  func TestUniformSampleStatistics(t *testing.T) {
   239  	s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1)))
   240  	for i := 1; i <= 10000; i++ {
   241  		s.Update(int64(i))
   242  	}
   243  	testUniformSampleStatistics(t, s.Snapshot())
   244  }
   245  
   246  func benchmarkSample(b *testing.B, s Sample) {
   247  	var memStats runtime.MemStats
   248  	runtime.ReadMemStats(&memStats)
   249  	pauseTotalNs := memStats.PauseTotalNs
   250  	b.ResetTimer()
   251  	for i := 0; i < b.N; i++ {
   252  		s.Update(1)
   253  	}
   254  	b.StopTimer()
   255  	runtime.GC()
   256  	runtime.ReadMemStats(&memStats)
   257  	b.Logf("GC cost: %d ns/op", int(memStats.PauseTotalNs-pauseTotalNs)/b.N)
   258  }
   259  
   260  func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) {
   261  	if count := s.Count(); count != 10000 {
   262  		t.Errorf("s.Count(): 10000 != %v\n", count)
   263  	}
   264  	if min := s.Min(); min != 107 {
   265  		t.Errorf("s.Min(): 107 != %v\n", min)
   266  	}
   267  	if max := s.Max(); max != 10000 {
   268  		t.Errorf("s.Max(): 10000 != %v\n", max)
   269  	}
   270  	if mean := s.Mean(); mean != 4965.98 {
   271  		t.Errorf("s.Mean(): 4965.98 != %v\n", mean)
   272  	}
   273  	if stdDev := s.StdDev(); stdDev != 2959.825156930727 {
   274  		t.Errorf("s.StdDev(): 2959.825156930727 != %v\n", stdDev)
   275  	}
   276  	ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
   277  	if ps[0] != 4615 {
   278  		t.Errorf("median: 4615 != %v\n", ps[0])
   279  	}
   280  	if ps[1] != 7672 {
   281  		t.Errorf("75th percentile: 7672 != %v\n", ps[1])
   282  	}
   283  	if ps[2] != 9998.99 {
   284  		t.Errorf("99th percentile: 9998.99 != %v\n", ps[2])
   285  	}
   286  }
   287  
   288  func testUniformSampleStatistics(t *testing.T, s SampleSnapshot) {
   289  	if count := s.Count(); count != 10000 {
   290  		t.Errorf("s.Count(): 10000 != %v\n", count)
   291  	}
   292  	if min := s.Min(); min != 37 {
   293  		t.Errorf("s.Min(): 37 != %v\n", min)
   294  	}
   295  	if max := s.Max(); max != 9989 {
   296  		t.Errorf("s.Max(): 9989 != %v\n", max)
   297  	}
   298  	if mean := s.Mean(); mean != 4748.14 {
   299  		t.Errorf("s.Mean(): 4748.14 != %v\n", mean)
   300  	}
   301  	if stdDev := s.StdDev(); stdDev != 2826.684117548333 {
   302  		t.Errorf("s.StdDev(): 2826.684117548333 != %v\n", stdDev)
   303  	}
   304  	ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
   305  	if ps[0] != 4599 {
   306  		t.Errorf("median: 4599 != %v\n", ps[0])
   307  	}
   308  	if ps[1] != 7380.5 {
   309  		t.Errorf("75th percentile: 7380.5 != %v\n", ps[1])
   310  	}
   311  	if math.Abs(9986.429999999998-ps[2]) > epsilonPercentile {
   312  		t.Errorf("99th percentile: 9986.429999999998 != %v\n", ps[2])
   313  	}
   314  }
   315  
   316  // TestUniformSampleConcurrentUpdateCount would expose data race problems with
   317  // concurrent Update and Count calls on Sample when test is called with -race
   318  // argument
   319  func TestUniformSampleConcurrentUpdateCount(t *testing.T) {
   320  	if testing.Short() {
   321  		t.Skip("skipping in short mode")
   322  	}
   323  	s := NewUniformSample(100)
   324  	for i := 0; i < 100; i++ {
   325  		s.Update(int64(i))
   326  	}
   327  	quit := make(chan struct{})
   328  	go func() {
   329  		t := time.NewTicker(10 * time.Millisecond)
   330  		defer t.Stop()
   331  		for {
   332  			select {
   333  			case <-t.C:
   334  				s.Update(rand.Int63())
   335  			case <-quit:
   336  				t.Stop()
   337  				return
   338  			}
   339  		}
   340  	}()
   341  	for i := 0; i < 1000; i++ {
   342  		s.Snapshot().Count()
   343  		time.Sleep(5 * time.Millisecond)
   344  	}
   345  	quit <- struct{}{}
   346  }
   347  
   348  func BenchmarkCalculatePercentiles(b *testing.B) {
   349  	pss := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
   350  	var vals []int64
   351  	for i := 0; i < 1000; i++ {
   352  		vals = append(vals, int64(rand.Int31()))
   353  	}
   354  	v := make([]int64, len(vals))
   355  	b.ResetTimer()
   356  	for i := 0; i < b.N; i++ {
   357  		copy(v, vals)
   358  		_ = CalculatePercentiles(v, pss)
   359  	}
   360  }