histogram.go 5.4 KB

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