histogram.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. package metrics
  2. import (
  3. "math"
  4. "sync"
  5. "sync/atomic"
  6. )
  7. // Histograms calculate distribution statistics from an int64 value.
  8. //
  9. // This is an interface so as to encourage other structs to implement
  10. // the Histogram API as appropriate.
  11. type Histogram interface {
  12. Clear()
  13. Count() int64
  14. Max() int64
  15. Mean() float64
  16. Min() int64
  17. Percentile(float64) float64
  18. Percentiles([]float64) []float64
  19. Sample() Sample
  20. StdDev() float64
  21. Update(int64)
  22. Variance() float64
  23. }
  24. // Get an existing or create and register a new Histogram.
  25. func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
  26. if nil == r {
  27. r = DefaultRegistry
  28. }
  29. return r.GetOrRegister(name, NewHistogram(s)).(Histogram)
  30. }
  31. // Create a new Histogram with the given Sample. The initial values compare
  32. // so that the first value will be both min and max and the variance is flagged
  33. // for special treatment on its first iteration.
  34. func NewHistogram(s Sample) Histogram {
  35. if UseNilMetrics {
  36. return NilHistogram{}
  37. }
  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. // Create and register a new Histogram.
  46. func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram {
  47. c := NewHistogram(s)
  48. if nil == r {
  49. r = DefaultRegistry
  50. }
  51. r.Register(name, c)
  52. return c
  53. }
  54. // No-op Histogram.
  55. type NilHistogram struct{}
  56. // No-op.
  57. func (NilHistogram) Clear() {}
  58. // No-op.
  59. func (NilHistogram) Count() int64 { return 0 }
  60. // No-op.
  61. func (NilHistogram) Max() int64 { return 0 }
  62. // No-op.
  63. func (NilHistogram) Mean() float64 { return 0.0 }
  64. // No-op.
  65. func (NilHistogram) Min() int64 { return 0 }
  66. // No-op.
  67. func (NilHistogram) Percentile(p float64) float64 { return 0.0 }
  68. // No-op.
  69. func (NilHistogram) Percentiles(ps []float64) []float64 {
  70. return make([]float64, len(ps))
  71. }
  72. // No-op.
  73. func (NilHistogram) Sample() Sample { return NilSample{} }
  74. // No-op.
  75. func (NilHistogram) StdDev() float64 { return 0.0 }
  76. // No-op.
  77. func (NilHistogram) Update(v int64) {}
  78. // No-op.
  79. func (NilHistogram) Variance() float64 { return 0.0 }
  80. // The standard implementation of a Histogram uses a Sample and a goroutine
  81. // to synchronize its calculations.
  82. type StandardHistogram struct {
  83. count, sum, min, max int64
  84. mutex sync.Mutex
  85. s Sample
  86. variance [2]float64
  87. }
  88. // Clear the histogram.
  89. func (h *StandardHistogram) Clear() {
  90. h.mutex.Lock()
  91. defer h.mutex.Unlock()
  92. h.count = 0
  93. h.max = math.MinInt64
  94. h.min = math.MaxInt64
  95. h.s.Clear()
  96. h.sum = 0
  97. h.variance = [2]float64{-1.0, 0.0}
  98. }
  99. // Return the count of inputs since the histogram was last cleared.
  100. func (h *StandardHistogram) Count() int64 {
  101. return atomic.LoadInt64(&h.count)
  102. }
  103. // Return the maximal value seen since the histogram was last cleared.
  104. func (h *StandardHistogram) Max() int64 {
  105. h.mutex.Lock()
  106. defer h.mutex.Unlock()
  107. if 0 == h.count {
  108. return 0
  109. }
  110. return h.max
  111. }
  112. // Return the mean of all values seen since the histogram was last cleared.
  113. func (h *StandardHistogram) Mean() float64 {
  114. h.mutex.Lock()
  115. defer h.mutex.Unlock()
  116. if 0 == h.count {
  117. return 0
  118. }
  119. return float64(h.sum) / float64(h.count)
  120. }
  121. // Return the minimal value seen since the histogram was last cleared.
  122. func (h *StandardHistogram) Min() int64 {
  123. h.mutex.Lock()
  124. defer h.mutex.Unlock()
  125. if 0 == h.count {
  126. return 0
  127. }
  128. return h.min
  129. }
  130. // Percentile returns an arbitrary percentile of sampled values.
  131. func (h *StandardHistogram) Percentile(p float64) float64 {
  132. return h.s.Percentile(p)
  133. }
  134. // Percentiles returns a slice of arbitrary percentiles of sampled values.
  135. func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
  136. return h.s.Percentiles(ps)
  137. }
  138. // Sample returns a copy of the Sample underlying the Histogram.
  139. func (h *StandardHistogram) Sample() Sample {
  140. return h.s.Dup()
  141. }
  142. // Return the standard deviation of all values seen since the histogram was
  143. // last cleared.
  144. func (h *StandardHistogram) StdDev() float64 {
  145. return math.Sqrt(h.Variance())
  146. }
  147. // Update the histogram with a new value.
  148. func (h *StandardHistogram) Update(v int64) {
  149. h.mutex.Lock()
  150. defer h.mutex.Unlock()
  151. h.s.Update(v)
  152. h.count++
  153. if v < h.min {
  154. h.min = v
  155. }
  156. if v > h.max {
  157. h.max = v
  158. }
  159. h.sum += v
  160. fv := float64(v)
  161. if -1.0 == h.variance[0] {
  162. h.variance[0] = fv
  163. h.variance[1] = 0.0
  164. } else {
  165. m := h.variance[0]
  166. s := h.variance[1]
  167. h.variance[0] = m + (fv-m)/float64(h.count)
  168. h.variance[1] = s + (fv-m)*(fv-h.variance[0])
  169. }
  170. }
  171. // Return the variance of all values seen since the histogram was last cleared.
  172. func (h *StandardHistogram) Variance() float64 {
  173. h.mutex.Lock()
  174. defer h.mutex.Unlock()
  175. if 1 >= h.count {
  176. return 0.0
  177. }
  178. return h.variance[1] / float64(h.count-1)
  179. }
  180. // Cribbed from the standard library's `sort` package.
  181. type int64Slice []int64
  182. func (p int64Slice) Len() int { return len(p) }
  183. func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
  184. func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }