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] }