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