github.com/ethereum/go-ethereum@v1.14.3/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 TestExpDecaySample(t *testing.T) {
    91  	for _, tc := range []struct {
    92  		reservoirSize int
    93  		alpha         float64
    94  		updates       int
    95  	}{
    96  		{100, 0.99, 10},
    97  		{1000, 0.01, 100},
    98  		{100, 0.99, 1000},
    99  	} {
   100  		sample := NewExpDecaySample(tc.reservoirSize, tc.alpha)
   101  		for i := 0; i < tc.updates; i++ {
   102  			sample.Update(int64(i))
   103  		}
   104  		snap := sample.Snapshot()
   105  		if have, want := int(snap.Count()), tc.updates; have != want {
   106  			t.Errorf("have %d want %d", have, want)
   107  		}
   108  		if have, want := snap.Size(), min(tc.updates, tc.reservoirSize); have != want {
   109  			t.Errorf("have %d want %d", have, want)
   110  		}
   111  		values := snap.(*sampleSnapshot).values
   112  		if have, want := len(values), min(tc.updates, tc.reservoirSize); have != want {
   113  			t.Errorf("have %d want %d", have, want)
   114  		}
   115  		for _, v := range values {
   116  			if v > int64(tc.updates) || v < 0 {
   117  				t.Errorf("out of range [0, %d): %v", tc.updates, v)
   118  			}
   119  		}
   120  	}
   121  }
   122  
   123  // This test makes sure that the sample's priority is not amplified by using
   124  // nanosecond duration since start rather than second duration since start.
   125  // The priority becomes +Inf quickly after starting if this is done,
   126  // effectively freezing the set of samples until a rescale step happens.
   127  func TestExpDecaySampleNanosecondRegression(t *testing.T) {
   128  	sw := NewExpDecaySample(100, 0.99)
   129  	for i := 0; i < 100; i++ {
   130  		sw.Update(10)
   131  	}
   132  	time.Sleep(1 * time.Millisecond)
   133  	for i := 0; i < 100; i++ {
   134  		sw.Update(20)
   135  	}
   136  	s := sw.Snapshot()
   137  	v := s.(*sampleSnapshot).values
   138  	avg := float64(0)
   139  	for i := 0; i < len(v); i++ {
   140  		avg += float64(v[i])
   141  	}
   142  	avg /= float64(len(v))
   143  	if avg > 16 || avg < 14 {
   144  		t.Errorf("out of range [14, 16]: %v\n", avg)
   145  	}
   146  }
   147  
   148  func TestExpDecaySampleRescale(t *testing.T) {
   149  	s := NewExpDecaySample(2, 0.001).(*ExpDecaySample)
   150  	s.update(time.Now(), 1)
   151  	s.update(time.Now().Add(time.Hour+time.Microsecond), 1)
   152  	for _, v := range s.values.Values() {
   153  		if v.k == 0.0 {
   154  			t.Fatal("v.k == 0.0")
   155  		}
   156  	}
   157  }
   158  
   159  func TestExpDecaySampleSnapshot(t *testing.T) {
   160  	now := time.Now()
   161  	s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1)))
   162  	for i := 1; i <= 10000; i++ {
   163  		s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
   164  	}
   165  	snapshot := s.Snapshot()
   166  	s.Update(1)
   167  	testExpDecaySampleStatistics(t, snapshot)
   168  }
   169  
   170  func TestExpDecaySampleStatistics(t *testing.T) {
   171  	now := time.Now()
   172  	s := NewExpDecaySample(100, 0.99).(*ExpDecaySample).SetRand(rand.New(rand.NewSource(1)))
   173  	for i := 1; i <= 10000; i++ {
   174  		s.(*ExpDecaySample).update(now.Add(time.Duration(i)), int64(i))
   175  	}
   176  	testExpDecaySampleStatistics(t, s.Snapshot())
   177  }
   178  
   179  func TestUniformSample(t *testing.T) {
   180  	sw := NewUniformSample(100)
   181  	for i := 0; i < 1000; i++ {
   182  		sw.Update(int64(i))
   183  	}
   184  	s := sw.Snapshot()
   185  	if size := s.Count(); size != 1000 {
   186  		t.Errorf("s.Count(): 1000 != %v\n", size)
   187  	}
   188  	if size := s.Size(); size != 100 {
   189  		t.Errorf("s.Size(): 100 != %v\n", size)
   190  	}
   191  	values := s.(*sampleSnapshot).values
   192  
   193  	if l := len(values); l != 100 {
   194  		t.Errorf("len(s.Values()): 100 != %v\n", l)
   195  	}
   196  	for _, v := range values {
   197  		if v > 1000 || v < 0 {
   198  			t.Errorf("out of range [0, 100): %v\n", v)
   199  		}
   200  	}
   201  }
   202  
   203  func TestUniformSampleIncludesTail(t *testing.T) {
   204  	sw := NewUniformSample(100)
   205  	max := 100
   206  	for i := 0; i < max; i++ {
   207  		sw.Update(int64(i))
   208  	}
   209  	s := sw.Snapshot()
   210  	v := s.(*sampleSnapshot).values
   211  	sum := 0
   212  	exp := (max - 1) * max / 2
   213  	for i := 0; i < len(v); i++ {
   214  		sum += int(v[i])
   215  	}
   216  	if exp != sum {
   217  		t.Errorf("sum: %v != %v\n", exp, sum)
   218  	}
   219  }
   220  
   221  func TestUniformSampleSnapshot(t *testing.T) {
   222  	s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1)))
   223  	for i := 1; i <= 10000; i++ {
   224  		s.Update(int64(i))
   225  	}
   226  	snapshot := s.Snapshot()
   227  	s.Update(1)
   228  	testUniformSampleStatistics(t, snapshot)
   229  }
   230  
   231  func TestUniformSampleStatistics(t *testing.T) {
   232  	s := NewUniformSample(100).(*UniformSample).SetRand(rand.New(rand.NewSource(1)))
   233  	for i := 1; i <= 10000; i++ {
   234  		s.Update(int64(i))
   235  	}
   236  	testUniformSampleStatistics(t, s.Snapshot())
   237  }
   238  
   239  func benchmarkSample(b *testing.B, s Sample) {
   240  	var memStats runtime.MemStats
   241  	runtime.ReadMemStats(&memStats)
   242  	pauseTotalNs := memStats.PauseTotalNs
   243  	b.ResetTimer()
   244  	for i := 0; i < b.N; i++ {
   245  		s.Update(1)
   246  	}
   247  	b.StopTimer()
   248  	runtime.GC()
   249  	runtime.ReadMemStats(&memStats)
   250  	b.Logf("GC cost: %d ns/op", int(memStats.PauseTotalNs-pauseTotalNs)/b.N)
   251  }
   252  
   253  func testExpDecaySampleStatistics(t *testing.T, s SampleSnapshot) {
   254  	if count := s.Count(); count != 10000 {
   255  		t.Errorf("s.Count(): 10000 != %v\n", count)
   256  	}
   257  	if min := s.Min(); min != 107 {
   258  		t.Errorf("s.Min(): 107 != %v\n", min)
   259  	}
   260  	if max := s.Max(); max != 10000 {
   261  		t.Errorf("s.Max(): 10000 != %v\n", max)
   262  	}
   263  	if mean := s.Mean(); mean != 4965.98 {
   264  		t.Errorf("s.Mean(): 4965.98 != %v\n", mean)
   265  	}
   266  	if stdDev := s.StdDev(); stdDev != 2959.825156930727 {
   267  		t.Errorf("s.StdDev(): 2959.825156930727 != %v\n", stdDev)
   268  	}
   269  	ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
   270  	if ps[0] != 4615 {
   271  		t.Errorf("median: 4615 != %v\n", ps[0])
   272  	}
   273  	if ps[1] != 7672 {
   274  		t.Errorf("75th percentile: 7672 != %v\n", ps[1])
   275  	}
   276  	if ps[2] != 9998.99 {
   277  		t.Errorf("99th percentile: 9998.99 != %v\n", ps[2])
   278  	}
   279  }
   280  
   281  func testUniformSampleStatistics(t *testing.T, s SampleSnapshot) {
   282  	if count := s.Count(); count != 10000 {
   283  		t.Errorf("s.Count(): 10000 != %v\n", count)
   284  	}
   285  	if min := s.Min(); min != 37 {
   286  		t.Errorf("s.Min(): 37 != %v\n", min)
   287  	}
   288  	if max := s.Max(); max != 9989 {
   289  		t.Errorf("s.Max(): 9989 != %v\n", max)
   290  	}
   291  	if mean := s.Mean(); mean != 4748.14 {
   292  		t.Errorf("s.Mean(): 4748.14 != %v\n", mean)
   293  	}
   294  	if stdDev := s.StdDev(); stdDev != 2826.684117548333 {
   295  		t.Errorf("s.StdDev(): 2826.684117548333 != %v\n", stdDev)
   296  	}
   297  	ps := s.Percentiles([]float64{0.5, 0.75, 0.99})
   298  	if ps[0] != 4599 {
   299  		t.Errorf("median: 4599 != %v\n", ps[0])
   300  	}
   301  	if ps[1] != 7380.5 {
   302  		t.Errorf("75th percentile: 7380.5 != %v\n", ps[1])
   303  	}
   304  	if math.Abs(9986.429999999998-ps[2]) > epsilonPercentile {
   305  		t.Errorf("99th percentile: 9986.429999999998 != %v\n", ps[2])
   306  	}
   307  }
   308  
   309  // TestUniformSampleConcurrentUpdateCount would expose data race problems with
   310  // concurrent Update and Count calls on Sample when test is called with -race
   311  // argument
   312  func TestUniformSampleConcurrentUpdateCount(t *testing.T) {
   313  	if testing.Short() {
   314  		t.Skip("skipping in short mode")
   315  	}
   316  	s := NewUniformSample(100)
   317  	for i := 0; i < 100; i++ {
   318  		s.Update(int64(i))
   319  	}
   320  	quit := make(chan struct{})
   321  	go func() {
   322  		t := time.NewTicker(10 * time.Millisecond)
   323  		defer t.Stop()
   324  		for {
   325  			select {
   326  			case <-t.C:
   327  				s.Update(rand.Int63())
   328  			case <-quit:
   329  				t.Stop()
   330  				return
   331  			}
   332  		}
   333  	}()
   334  	for i := 0; i < 1000; i++ {
   335  		s.Snapshot().Count()
   336  		time.Sleep(5 * time.Millisecond)
   337  	}
   338  	quit <- struct{}{}
   339  }
   340  
   341  func BenchmarkCalculatePercentiles(b *testing.B) {
   342  	pss := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}
   343  	var vals []int64
   344  	for i := 0; i < 1000; i++ {
   345  		vals = append(vals, int64(rand.Int31()))
   346  	}
   347  	v := make([]int64, len(vals))
   348  	b.ResetTimer()
   349  	for i := 0; i < b.N; i++ {
   350  		copy(v, vals)
   351  		_ = CalculatePercentiles(v, pss)
   352  	}
   353  }