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 }