| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- package metrics
- import (
- "math"
- "sort"
- "sync"
- "sync/atomic"
- )
- // Histograms calculate distribution statistics from an int64 value.
- //
- // This is an interface so as to encourage other structs to implement
- // the Histogram API as appropriate.
- type Histogram interface {
- Clear()
- Count() int64
- Max() int64
- Mean() float64
- Min() int64
- Percentile(float64) float64
- Percentiles([]float64) []float64
- StdDev() float64
- Update(int64)
- Variance() float64
- }
- // Create a new Histogram with the given Sample. The initial values compare
- // so that the first value will be both min and max and the variance is flagged
- // for special treatment on its first iteration.
- func NewHistogram(s Sample) Histogram {
- if UseNilMetrics {
- return NilHistogram{}
- }
- return &StandardHistogram{
- max: math.MinInt64,
- min: math.MaxInt64,
- s: s,
- variance: [2]float64{-1.0, 0.0},
- }
- }
- // No-op Histogram.
- type NilHistogram struct{}
- // Force the compiler to check that NilHistogram implements Histogram.
- var _ Histogram = NilHistogram{}
- // No-op.
- func (h NilHistogram) Clear() {}
- // No-op.
- func (h NilHistogram) Count() int64 { return 0 }
- // No-op.
- func (h NilHistogram) Max() int64 { return 0 }
- // No-op.
- func (h NilHistogram) Mean() float64 { return 0.0 }
- // No-op.
- func (h NilHistogram) Min() int64 { return 0 }
- // No-op.
- func (h NilHistogram) Percentile(p float64) float64 { return 0.0 }
- // No-op.
- func (h NilHistogram) Percentiles(ps []float64) []float64 {
- return make([]float64, len(ps))
- }
- // No-op.
- func (h NilHistogram) StdDev() float64 { return 0.0 }
- // No-op.
- func (h NilHistogram) Update(v int64) {}
- // No-op.
- func (h NilHistogram) Variance() float64 { return 0.0 }
- // The standard implementation of a Histogram uses a Sample and a goroutine
- // to synchronize its calculations.
- type StandardHistogram struct {
- count, sum, min, max int64
- mutex sync.Mutex
- s Sample
- variance [2]float64
- }
- // Force the compiler to check that StandardHistogram implements Histogram.
- var _ Histogram = &StandardHistogram{}
- // Clear the histogram.
- func (h *StandardHistogram) Clear() {
- h.mutex.Lock()
- defer h.mutex.Unlock()
- h.count = 0
- h.max = math.MinInt64
- h.min = math.MaxInt64
- h.s.Clear()
- h.sum = 0
- h.variance = [...]float64{-1.0, 0.0}
- }
- // Return the count of inputs since the histogram was last cleared.
- func (h *StandardHistogram) Count() int64 {
- return atomic.LoadInt64(&h.count)
- }
- // Return the maximal value seen since the histogram was last cleared.
- func (h *StandardHistogram) Max() int64 {
- h.mutex.Lock()
- defer h.mutex.Unlock()
- if 0 == h.count {
- return 0
- }
- return h.max
- }
- // Return the mean of all values seen since the histogram was last cleared.
- func (h *StandardHistogram) Mean() float64 {
- h.mutex.Lock()
- defer h.mutex.Unlock()
- if 0 == h.count {
- return 0
- }
- return float64(h.sum) / float64(h.count)
- }
- // Return the minimal value seen since the histogram was last cleared.
- func (h *StandardHistogram) Min() int64 {
- h.mutex.Lock()
- defer h.mutex.Unlock()
- if 0 == h.count {
- return 0
- }
- return h.min
- }
- // Return an arbitrary percentile of all values seen since the histogram was
- // last cleared.
- func (h *StandardHistogram) Percentile(p float64) float64 {
- return h.Percentiles([]float64{p})[0]
- }
- // Return a slice of arbitrary percentiles of all values seen since the
- // histogram was last cleared.
- func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
- scores := make([]float64, len(ps))
- values := int64Slice(h.s.Values())
- size := len(values)
- if size > 0 {
- sort.Sort(values)
- for i, p := range ps {
- pos := p * float64(size+1)
- if pos < 1.0 {
- scores[i] = float64(values[0])
- } else if pos >= float64(size) {
- scores[i] = float64(values[size-1])
- } else {
- lower := float64(values[int(pos)-1])
- upper := float64(values[int(pos)])
- scores[i] = lower + (pos-math.Floor(pos))*(upper-lower)
- }
- }
- }
- return scores
- }
- // Return the standard deviation of all values seen since the histogram was
- // last cleared.
- func (h *StandardHistogram) StdDev() float64 {
- return math.Sqrt(h.Variance())
- }
- // Update the histogram with a new value.
- func (h *StandardHistogram) Update(v int64) {
- h.mutex.Lock()
- defer h.mutex.Unlock()
- h.s.Update(v)
- h.count++
- if v < h.min {
- h.min = v
- }
- if v > h.max {
- h.max = v
- }
- h.sum += v
- fv := float64(v)
- if -1.0 == h.variance[0] {
- h.variance[0] = fv
- h.variance[1] = 0.0
- } else {
- m := h.variance[0]
- s := h.variance[1]
- h.variance[0] = m + (fv-m)/float64(h.count)
- h.variance[1] = s + (fv-m)*(fv-h.variance[0])
- }
- }
- // Return the variance of all values seen since the histogram was last cleared.
- func (h *StandardHistogram) Variance() float64 {
- h.mutex.Lock()
- defer h.mutex.Unlock()
- if 1 >= h.count {
- return 0.0
- }
- return h.variance[1] / float64(h.count-1)
- }
- // Cribbed from the standard library's `sort` package.
- type int64Slice []int64
- func (p int64Slice) Len() int { return len(p) }
- func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] }
- func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
|