histogram.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package metrics
  2. import (
  3. "math"
  4. "sort"
  5. "sync"
  6. "sync/atomic"
  7. )
  8. // Histograms calculate distribution statistics from an int64 value.
  9. //
  10. // This is an interface so as to encourage other structs to implement
  11. // the Histogram API as appropriate.
  12. type Histogram interface {
  13. Clear()
  14. Count() int64
  15. Max() int64
  16. Mean() float64
  17. Min() int64
  18. Percentile(float64) float64
  19. Percentiles([]float64) []float64
  20. StdDev() float64
  21. Update(int64)
  22. Variance() float64
  23. }
  24. // The standard implementation of a Histogram uses a Sample and a goroutine
  25. // to synchronize its calculations.
  26. type StandardHistogram struct {
  27. count, sum, min, max int64
  28. mutex sync.Mutex
  29. s Sample
  30. variance [2]float64
  31. }
  32. // Force the compiler to check that StandardHistogram implements Histogram.
  33. var _ Histogram = &StandardHistogram{}
  34. // Create a new histogram with the given Sample. The initial values compare
  35. // so that the first value will be both min and max and the variance is flagged
  36. // for special treatment on its first iteration.
  37. func NewHistogram(s Sample) *StandardHistogram {
  38. return &StandardHistogram{
  39. max: math.MinInt64,
  40. min: math.MaxInt64,
  41. s: s,
  42. variance: [2]float64{-1.0, 0.0},
  43. }
  44. }
  45. // Clear the histogram.
  46. func (h *StandardHistogram) Clear() {
  47. h.mutex.Lock()
  48. defer h.mutex.Unlock()
  49. h.count = 0
  50. h.max = math.MinInt64
  51. h.min = math.MaxInt64
  52. h.s.Clear()
  53. h.sum = 0
  54. h.variance = [...]float64{-1.0, 0.0}
  55. }
  56. // Return the count of inputs since the histogram was last cleared.
  57. func (h *StandardHistogram) Count() int64 {
  58. return atomic.LoadInt64(&h.count)
  59. }
  60. // Return the maximal value seen since the histogram was last cleared.
  61. func (h *StandardHistogram) Max() int64 {
  62. h.mutex.Lock()
  63. defer h.mutex.Unlock()
  64. if 0 == h.count {
  65. return 0
  66. }
  67. return h.max
  68. }
  69. // Return the mean of all values seen since the histogram was last cleared.
  70. func (h *StandardHistogram) Mean() float64 {
  71. h.mutex.Lock()
  72. defer h.mutex.Unlock()
  73. if 0 == h.count {
  74. return 0
  75. }
  76. return float64(h.sum) / float64(h.count)
  77. }
  78. // Return the minimal value seen since the histogram was last cleared.
  79. func (h *StandardHistogram) Min() int64 {
  80. h.mutex.Lock()
  81. defer h.mutex.Unlock()
  82. if 0 == h.count {
  83. return 0
  84. }
  85. return h.min
  86. }
  87. // Return an arbitrary percentile of all values seen since the histogram was
  88. // last cleared.
  89. func (h *StandardHistogram) Percentile(p float64) float64 {
  90. return h.Percentiles([]float64{p})[0]
  91. }
  92. // Return a slice of arbitrary percentiles of all values seen since the
  93. // histogram was last cleared.
  94. func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
  95. scores := make([]float64, len(ps))
  96. values := int64Slice(h.s.Values())
  97. size := len(values)
  98. if size > 0 {
  99. sort.Sort(values)
  100. for i, p := range ps {
  101. pos := p * float64(size+1)
  102. if pos < 1.0 {
  103. scores[i] = float64(values[0])
  104. } else if pos >= float64(size) {
  105. scores[i] = float64(values[size-1])
  106. } else {
  107. lower := float64(values[int(pos)-1])
  108. upper := float64(values[int(pos)])
  109. scores[i] = lower + (pos-math.Floor(pos))*(upper-lower)
  110. }
  111. }
  112. }
  113. return scores
  114. }
  115. // Return the standard deviation of all values seen since the histogram was
  116. // last cleared.
  117. func (h *StandardHistogram) StdDev() float64 {
  118. return math.Sqrt(h.Variance())
  119. }
  120. // Update the histogram with a new value.
  121. func (h *StandardHistogram) Update(v int64) {
  122. h.mutex.Lock()
  123. defer h.mutex.Unlock()
  124. h.s.Update(v)
  125. h.count++
  126. if v < h.min {
  127. h.min = v
  128. }
  129. if v > h.max {
  130. h.max = v
  131. }
  132. h.sum += v
  133. fv := float64(v)
  134. if -1.0 == h.variance[0] {
  135. h.variance[0] = fv
  136. h.variance[1] = 0.0
  137. } else {
  138. m := h.variance[0]
  139. s := h.variance[1]
  140. h.variance[0] = m + (fv-m)/float64(h.count)
  141. h.variance[1] = s + (fv-m)*(fv-h.variance[0])
  142. }
  143. }
  144. // Return the variance of all values seen since the histogram was last cleared.
  145. func (h *StandardHistogram) Variance() float64 {
  146. h.mutex.Lock()
  147. defer h.mutex.Unlock()
  148. if 1 >= h.count {
  149. return 0.0
  150. }
  151. return h.variance[1] / float64(h.count-1)
  152. }
  153. // Cribbed from the standard library's `sort` package.
  154. type int64Slice []int64
  155. func (p int64Slice) Len() int { return len(p) }
  156. func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
  157. func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }