histogram.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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 { return hv.max }
  69. return 0
  70. }
  71. // Return the mean of all values seen since the histogram was last cleared.
  72. func (h *StandardHistogram) Mean() float64 {
  73. hv := <-h.out
  74. if 0 < hv.count {
  75. return float64(hv.sum) / float64(hv.count)
  76. }
  77. return 0
  78. }
  79. // Return the minimal value seen since the histogram was last cleared.
  80. func (h *StandardHistogram) Min() int64 {
  81. hv := <-h.out
  82. if 0 < hv.count { return hv.min }
  83. return 0
  84. }
  85. // Return an arbitrary percentile of all values seen since the histogram was
  86. // last cleared.
  87. func (h *StandardHistogram) Percentile(p float64) float64 {
  88. return h.Percentiles([]float64{p})[0]
  89. }
  90. // Return a slice of arbitrary percentiles of all values seen since the
  91. // histogram was last cleared.
  92. func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
  93. scores := make([]float64, len(ps))
  94. values := int64Slice(h.s.Values())
  95. size := len(values)
  96. if size > 0 {
  97. sort.Sort(values)
  98. for i, p := range ps {
  99. pos := p * float64(size + 1)
  100. if pos < 1.0 {
  101. scores[i] = float64(values[0])
  102. } else if pos >= float64(size) {
  103. scores[i] = float64(values[size - 1])
  104. } else {
  105. lower := float64(values[int(pos) - 1])
  106. upper := float64(values[int(pos)])
  107. scores[i] = lower + (pos - math.Floor(pos)) * (upper - lower)
  108. }
  109. }
  110. }
  111. return scores
  112. }
  113. // Return the standard deviation of all values seen since the histogram was
  114. // last cleared.
  115. func (h *StandardHistogram) StdDev() float64 {
  116. return math.Sqrt(h.Variance())
  117. }
  118. // Update the histogram with a new value.
  119. func (h *StandardHistogram) Update(v int64) {
  120. h.in <- v
  121. }
  122. // Return the variance of all values seen since the histogram was last cleared.
  123. func (h *StandardHistogram) Variance() float64 {
  124. hv := <-h.out
  125. if 1 >= hv.count { return 0.0 }
  126. return hv.variance[1] / float64(hv.count - 1)
  127. }
  128. // Receive inputs and send outputs. Sample each input and update values in
  129. // the histogramV. Send a copy of the histogramV as output.
  130. func (h *StandardHistogram) arbiter() {
  131. hv := newHistogramV()
  132. for {
  133. select {
  134. case v := <-h.in:
  135. h.s.Update(v)
  136. hv.count++
  137. if v < hv.min { hv.min = v }
  138. if v > hv.max { hv.max = v }
  139. hv.sum += v
  140. fv := float64(v)
  141. if -1.0 == hv.variance[0] {
  142. hv.variance[0] = fv
  143. hv.variance[1] = 0.0
  144. } else {
  145. m := hv.variance[0]
  146. s := hv.variance[1]
  147. hv.variance[0] = m + (fv - m) / float64(hv.count)
  148. hv.variance[1] = s + (fv - m) * (fv - hv.variance[0])
  149. }
  150. case h.out <- hv:
  151. case <- h.reset:
  152. h.s.Clear()
  153. hv = newHistogramV()
  154. }
  155. }
  156. }
  157. // Cribbed from the standard library's `sort` package.
  158. type int64Slice []int64
  159. func (p int64Slice) Len() int { return len(p) }
  160. func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
  161. func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }