histogram.go 4.6 KB

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