histogram.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. // Create a new Histogram with the given Sample. The initial values compare
  25. // so that the first value will be both min and max and the variance is flagged
  26. // for special treatment on its first iteration.
  27. func NewHistogram(s Sample) Histogram {
  28. if UseNilMetrics {
  29. return NilHistogram{}
  30. }
  31. return &StandardHistogram{
  32. max: math.MinInt64,
  33. min: math.MaxInt64,
  34. s: s,
  35. variance: [2]float64{-1.0, 0.0},
  36. }
  37. }
  38. // No-op Histogram.
  39. type NilHistogram struct{}
  40. // Force the compiler to check that NilHistogram implements Histogram.
  41. var _ Histogram = NilHistogram{}
  42. // No-op.
  43. func (h NilHistogram) Clear() {}
  44. // No-op.
  45. func (h NilHistogram) Count() int64 { return 0 }
  46. // No-op.
  47. func (h NilHistogram) Max() int64 { return 0 }
  48. // No-op.
  49. func (h NilHistogram) Mean() float64 { return 0.0 }
  50. // No-op.
  51. func (h NilHistogram) Min() int64 { return 0 }
  52. // No-op.
  53. func (h NilHistogram) Percentile(p float64) float64 { return 0.0 }
  54. // No-op.
  55. func (h NilHistogram) Percentiles(ps []float64) []float64 {
  56. return make([]float64, len(ps))
  57. }
  58. // No-op.
  59. func (h NilHistogram) StdDev() float64 { return 0.0 }
  60. // No-op.
  61. func (h NilHistogram) Update(v int64) {}
  62. // No-op.
  63. func (h NilHistogram) Variance() float64 { return 0.0 }
  64. // The standard implementation of a Histogram uses a Sample and a goroutine
  65. // to synchronize its calculations.
  66. type StandardHistogram struct {
  67. count, sum, min, max int64
  68. mutex sync.Mutex
  69. s Sample
  70. variance [2]float64
  71. }
  72. // Force the compiler to check that StandardHistogram implements Histogram.
  73. var _ Histogram = &StandardHistogram{}
  74. // Clear the histogram.
  75. func (h *StandardHistogram) Clear() {
  76. h.mutex.Lock()
  77. defer h.mutex.Unlock()
  78. h.count = 0
  79. h.max = math.MinInt64
  80. h.min = math.MaxInt64
  81. h.s.Clear()
  82. h.sum = 0
  83. h.variance = [...]float64{-1.0, 0.0}
  84. }
  85. // Return the count of inputs since the histogram was last cleared.
  86. func (h *StandardHistogram) Count() int64 {
  87. return atomic.LoadInt64(&h.count)
  88. }
  89. // Return the maximal value seen since the histogram was last cleared.
  90. func (h *StandardHistogram) Max() int64 {
  91. h.mutex.Lock()
  92. defer h.mutex.Unlock()
  93. if 0 == h.count {
  94. return 0
  95. }
  96. return h.max
  97. }
  98. // Return the mean of all values seen since the histogram was last cleared.
  99. func (h *StandardHistogram) Mean() float64 {
  100. h.mutex.Lock()
  101. defer h.mutex.Unlock()
  102. if 0 == h.count {
  103. return 0
  104. }
  105. return float64(h.sum) / float64(h.count)
  106. }
  107. // Return the minimal value seen since the histogram was last cleared.
  108. func (h *StandardHistogram) Min() int64 {
  109. h.mutex.Lock()
  110. defer h.mutex.Unlock()
  111. if 0 == h.count {
  112. return 0
  113. }
  114. return h.min
  115. }
  116. // Return an arbitrary percentile of all values seen since the histogram was
  117. // last cleared.
  118. func (h *StandardHistogram) Percentile(p float64) float64 {
  119. return h.Percentiles([]float64{p})[0]
  120. }
  121. // Return a slice of arbitrary percentiles of all values seen since the
  122. // histogram was last cleared.
  123. func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
  124. scores := make([]float64, len(ps))
  125. values := int64Slice(h.s.Values())
  126. size := len(values)
  127. if size > 0 {
  128. sort.Sort(values)
  129. for i, p := range ps {
  130. pos := p * float64(size+1)
  131. if pos < 1.0 {
  132. scores[i] = float64(values[0])
  133. } else if pos >= float64(size) {
  134. scores[i] = float64(values[size-1])
  135. } else {
  136. lower := float64(values[int(pos)-1])
  137. upper := float64(values[int(pos)])
  138. scores[i] = lower + (pos-math.Floor(pos))*(upper-lower)
  139. }
  140. }
  141. }
  142. return scores
  143. }
  144. // Return the standard deviation of all values seen since the histogram was
  145. // last cleared.
  146. func (h *StandardHistogram) StdDev() float64 {
  147. return math.Sqrt(h.Variance())
  148. }
  149. // Update the histogram with a new value.
  150. func (h *StandardHistogram) Update(v int64) {
  151. h.mutex.Lock()
  152. defer h.mutex.Unlock()
  153. h.s.Update(v)
  154. h.count++
  155. if v < h.min {
  156. h.min = v
  157. }
  158. if v > h.max {
  159. h.max = v
  160. }
  161. h.sum += v
  162. fv := float64(v)
  163. if -1.0 == h.variance[0] {
  164. h.variance[0] = fv
  165. h.variance[1] = 0.0
  166. } else {
  167. m := h.variance[0]
  168. s := h.variance[1]
  169. h.variance[0] = m + (fv-m)/float64(h.count)
  170. h.variance[1] = s + (fv-m)*(fv-h.variance[0])
  171. }
  172. }
  173. // Return the variance of all values seen since the histogram was last cleared.
  174. func (h *StandardHistogram) Variance() float64 {
  175. h.mutex.Lock()
  176. defer h.mutex.Unlock()
  177. if 1 >= h.count {
  178. return 0.0
  179. }
  180. return h.variance[1] / float64(h.count-1)
  181. }
  182. // Cribbed from the standard library's `sort` package.
  183. type int64Slice []int64
  184. func (p int64Slice) Len() int { return len(p) }
  185. func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
  186. func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }