histogram.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. package metrics
  2. import (
  3. "math"
  4. "sync"
  5. "sync/atomic"
  6. )
  7. // Histograms calculate distribution statistics from a series of int64 values.
  8. type Histogram interface {
  9. Clear()
  10. Count() int64
  11. Max() int64
  12. Mean() float64
  13. Min() int64
  14. Percentile(float64) float64
  15. Percentiles([]float64) []float64
  16. Sample() Sample
  17. Snapshot() Histogram
  18. StdDev() float64
  19. Update(int64)
  20. Variance() float64
  21. }
  22. // GetOrRegisterHistogram returns an existing Histogram or constructs and
  23. // registers a new StandardHistogram.
  24. func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
  25. if nil == r {
  26. r = DefaultRegistry
  27. }
  28. return r.GetOrRegister(name, NewHistogram(s)).(Histogram)
  29. }
  30. // NewHistogram constructs a new StandardHistogram from a Sample.
  31. func NewHistogram(s Sample) Histogram {
  32. if UseNilMetrics {
  33. return NilHistogram{}
  34. }
  35. return &StandardHistogram{
  36. max: math.MinInt64,
  37. min: math.MaxInt64,
  38. sample: s,
  39. sampleMean: -1.0,
  40. }
  41. }
  42. // NewRegisteredHistogram constructs and registers a new StandardHistogram from
  43. // a Sample.
  44. func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram {
  45. c := NewHistogram(s)
  46. if nil == r {
  47. r = DefaultRegistry
  48. }
  49. r.Register(name, c)
  50. return c
  51. }
  52. // No-op Histogram.
  53. type NilHistogram struct{}
  54. // Clear is a no-op.
  55. func (NilHistogram) Clear() {}
  56. // Count is a no-op.
  57. func (NilHistogram) Count() int64 { return 0 }
  58. // Max is a no-op.
  59. func (NilHistogram) Max() int64 { return 0 }
  60. // Mean is a no-op.
  61. func (NilHistogram) Mean() float64 { return 0.0 }
  62. // Min is a no-op.
  63. func (NilHistogram) Min() int64 { return 0 }
  64. // Percentile is a no-op.
  65. func (NilHistogram) Percentile(p float64) float64 { return 0.0 }
  66. // Percentiles is a no-op.
  67. func (NilHistogram) Percentiles(ps []float64) []float64 {
  68. return make([]float64, len(ps))
  69. }
  70. // Sample is a no-op.
  71. func (NilHistogram) Sample() Sample { return NilSample{} }
  72. // No-op.
  73. func (NilHistogram) StdDev() float64 { return 0.0 }
  74. // StdDev is a no-op.
  75. func (NilHistogram) StdDev() float64 { return 0.0 }
  76. // Update is a no-op.
  77. func (NilHistogram) Update(v int64) {}
  78. // Variance is a no-op.
  79. func (NilHistogram) Variance() float64 { return 0.0 }
  80. // StandardHistogram is the standard implementation of a Histogram and uses a
  81. // Sample to bound its memory use.
  82. type StandardHistogram struct {
  83. count, max, min, sum int64
  84. mutex sync.Mutex
  85. sample Sample
  86. sampleMean float64
  87. varianceNumerator float64
  88. }
  89. // Clear clears the histogram and its sample.
  90. func (h *StandardHistogram) Clear() {
  91. h.mutex.Lock()
  92. defer h.mutex.Unlock()
  93. h.count = 0
  94. h.max = math.MinInt64
  95. h.min = math.MaxInt64
  96. h.sample.Clear()
  97. h.sum = 0
  98. h.sampleMean = -1.0
  99. h.varianceNumerator = 0.0
  100. }
  101. // Count returns the count of events since the histogram was last cleared.
  102. func (h *StandardHistogram) Count() int64 {
  103. return atomic.LoadInt64(&h.count)
  104. }
  105. // Max returns the maximum value seen since the histogram was last cleared.
  106. func (h *StandardHistogram) Max() int64 {
  107. h.mutex.Lock()
  108. defer h.mutex.Unlock()
  109. if 0 == h.count {
  110. return 0
  111. }
  112. return h.max
  113. }
  114. // Mean returns the mean of all values seen since the histogram was last
  115. // cleared.
  116. func (h *StandardHistogram) Mean() float64 {
  117. h.mutex.Lock()
  118. defer h.mutex.Unlock()
  119. if 0 == h.count {
  120. return 0
  121. }
  122. return float64(h.sum) / float64(h.count)
  123. }
  124. // Min returns the minimum value seen since the histogram was last cleared.
  125. func (h *StandardHistogram) Min() int64 {
  126. h.mutex.Lock()
  127. defer h.mutex.Unlock()
  128. if 0 == h.count {
  129. return 0
  130. }
  131. return h.min
  132. }
  133. // Percentile returns an arbitrary percentile of the values in the sample.
  134. func (h *StandardHistogram) Percentile(p float64) float64 {
  135. return h.s.Percentile(p)
  136. }
  137. // Percentiles returns a slice of arbitrary percentiles of the values in the
  138. // sample.
  139. func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
  140. return h.s.Percentiles(ps)
  141. }
  142. // Sample returns a copy of the Sample underlying the Histogram.
  143. func (h *StandardHistogram) Sample() Sample {
  144. return h.s.Dup()
  145. }
  146. // StdDev returns the standard deviation of all values seen since the histogram
  147. // was last cleared.
  148. func (h *StandardHistogram) StdDev() float64 {
  149. return math.Sqrt(h.Variance())
  150. }
  151. // Update updates the histogram with a new value.
  152. func (h *StandardHistogram) Update(v int64) {
  153. h.mutex.Lock()
  154. defer h.mutex.Unlock()
  155. h.sample.Update(v)
  156. h.count++
  157. if v < h.min {
  158. h.min = v
  159. }
  160. if v > h.max {
  161. h.max = v
  162. }
  163. h.sum += v
  164. fv := float64(v)
  165. if -1.0 == h.sampleMean {
  166. h.sampleMean = fv
  167. h.varianceNumerator = 0.0
  168. } else {
  169. sampleMean := h.sampleMean
  170. varianceNumerator := h.varianceNumerator
  171. h.sampleMean = sampleMean + (fv-sampleMean)/float64(h.count)
  172. h.varianceNumerator = varianceNumerator + (fv-sampleMean)*(fv-h.sampleMean)
  173. }
  174. }
  175. // Variance returns the variance of all values seen since the histogram was
  176. // last cleared.
  177. func (h *StandardHistogram) Variance() float64 {
  178. h.mutex.Lock()
  179. defer h.mutex.Unlock()
  180. return h.variance()
  181. }
  182. // variance returns the variance of all the values in the sample but expects
  183. // the lock to already be held.
  184. func (h *StandardHistogram) variance() float64 {
  185. if 1 >= h.count {
  186. return 0.0
  187. }
  188. return h.varianceNumerator / float64(h.count-1)
  189. }
  190. type int64Slice []int64
  191. func (p int64Slice) Len() int { return len(p) }
  192. func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
  193. func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }