You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

distribution.go 2.3 kB

4 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. // Copyright 2017 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package distribution
  15. import (
  16. "log"
  17. "math"
  18. "sort"
  19. "sync"
  20. "sync/atomic"
  21. )
  22. // D is a distribution. Methods of D can be called concurrently by multiple
  23. // goroutines.
  24. type D struct {
  25. buckets []uint64
  26. // sumsReuse is the scratch space that is reused
  27. // to store sums during invocations of Percentile.
  28. // After an invocation of New(n):
  29. // len(buckets) == len(sumsReuse) == n
  30. sumsReuse []uint64
  31. mu sync.Mutex
  32. }
  33. // New creates a new distribution capable of holding values from 0 to n-1.
  34. func New(n int) *D {
  35. return &D{
  36. buckets: make([]uint64, n),
  37. sumsReuse: make([]uint64, n),
  38. }
  39. }
  40. // Record records value v to the distribution.
  41. // To help with distributions with long tails, if v is larger than the maximum value,
  42. // Record records the maximum value instead.
  43. // If v is negative, Record panics.
  44. func (d *D) Record(v int) {
  45. if v < 0 {
  46. log.Panicf("Record: value out of range: %d", v)
  47. } else if v >= len(d.buckets) {
  48. v = len(d.buckets) - 1
  49. }
  50. atomic.AddUint64(&d.buckets[v], 1)
  51. }
  52. // Percentile computes the p-th percentile of the distribution where
  53. // p is between 0 and 1. This method may be called by multiple goroutines.
  54. func (d *D) Percentile(p float64) int {
  55. // NOTE: This implementation uses the nearest-rank method.
  56. // https://en.wikipedia.org/wiki/Percentile#The_nearest-rank_method
  57. if p < 0 || p > 1 {
  58. log.Panicf("Percentile: percentile out of range: %f", p)
  59. }
  60. d.mu.Lock()
  61. defer d.mu.Unlock()
  62. var sum uint64
  63. for i := range d.sumsReuse {
  64. sum += atomic.LoadUint64(&d.buckets[i])
  65. d.sumsReuse[i] = sum
  66. }
  67. target := uint64(math.Ceil(float64(sum) * p))
  68. return sort.Search(len(d.sumsReuse), func(i int) bool { return d.sumsReuse[i] >= target })
  69. }