histogram.go 4.5 KB

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