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