github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/histogram.go (about)

     1  package stats
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  )
    11  
    12  // HistogramValue is the value of Histogram objects.
    13  type HistogramValue struct {
    14  	// Count is the total number of values added to the histogram.
    15  	Count int64
    16  	// Sum is the sum of all the values added to the histogram.
    17  	Sum int64
    18  	// Min is the minimum of all the values added to the histogram.
    19  	Min int64
    20  	// Max is the maximum of all the values added to the histogram.
    21  	Max int64
    22  	// Buckets contains all the buckets of the histogram.
    23  	Buckets []HistogramBucket
    24  }
    25  
    26  // HistogramBucket is one histogram bucket.
    27  type HistogramBucket struct {
    28  	// LowBound is the lower bound of the bucket.
    29  	LowBound int64
    30  	// Count is the number of values in the bucket.
    31  	Count int64
    32  }
    33  
    34  // Print writes textual output of the histogram values.
    35  func (v HistogramValue) Print(w io.Writer) {
    36  	avg := float64(v.Sum) / float64(v.Count)
    37  	fmt.Fprintf(w, "Count: %d  Min: %d  Max: %d  Avg: %.2f\n", v.Count, v.Min, v.Max, avg)
    38  	fmt.Fprintf(w, "%s\n", strings.Repeat("-", 60))
    39  	if v.Count <= 0 {
    40  		return
    41  	}
    42  
    43  	maxBucketDigitLen := len(strconv.FormatInt(v.Buckets[len(v.Buckets)-1].LowBound, 10))
    44  	if maxBucketDigitLen < 3 {
    45  		// For "inf".
    46  		maxBucketDigitLen = 3
    47  	}
    48  	maxCountDigitLen := len(strconv.FormatInt(v.Count, 10))
    49  	percentMulti := 100 / float64(v.Count)
    50  
    51  	accCount := int64(0)
    52  	for i, b := range v.Buckets {
    53  		fmt.Fprintf(w, "[%*d, ", maxBucketDigitLen, b.LowBound)
    54  		if i+1 < len(v.Buckets) {
    55  			fmt.Fprintf(w, "%*d)", maxBucketDigitLen, v.Buckets[i+1].LowBound)
    56  		} else {
    57  			fmt.Fprintf(w, "%*s)", maxBucketDigitLen, "inf")
    58  		}
    59  
    60  		accCount += b.Count
    61  		fmt.Fprintf(w, "  %*d  %5.1f%%  %5.1f%%", maxCountDigitLen, b.Count, float64(b.Count)*percentMulti, float64(accCount)*percentMulti)
    62  
    63  		const barScale = 0.1
    64  		barLength := int(float64(b.Count)*percentMulti*barScale + 0.5)
    65  		fmt.Fprintf(w, "  %s\n", strings.Repeat("#", barLength))
    66  	}
    67  }
    68  
    69  // String returns the textual output of the histogram values as string.
    70  func (v HistogramValue) String() string {
    71  	var b bytes.Buffer
    72  	v.Print(&b)
    73  	return b.String()
    74  }
    75  
    76  // A Histogram accumulates values in the form of a histogram. The type of the
    77  // values is int64, which is suitable for keeping track of things like RPC
    78  // latency in milliseconds. New histogram objects should be obtained via the
    79  // New() function.
    80  type Histogram struct {
    81  	opts    HistogramOptions
    82  	buckets []bucketInternal
    83  	count   *Counter
    84  	sum     *Counter
    85  	tracker *Tracker
    86  }
    87  
    88  // HistogramOptions contains the parameters that define the histogram's buckets.
    89  type HistogramOptions struct {
    90  	// NumBuckets is the number of buckets.
    91  	NumBuckets int
    92  	// GrowthFactor is the growth factor of the buckets. A value of 0.1
    93  	// indicates that bucket N+1 will be 10% larger than bucket N.
    94  	GrowthFactor float64
    95  	// SmallestBucketSize is the size of the first bucket. Bucket sizes are
    96  	// rounded down to the nearest integer.
    97  	SmallestBucketSize float64
    98  	// MinValue is the lower bound of the first bucket.
    99  	MinValue int64
   100  }
   101  
   102  // bucketInternal is the internal representation of a bucket, which includes a
   103  // rate counter.
   104  type bucketInternal struct {
   105  	lowBound int64
   106  	count    *Counter
   107  }
   108  
   109  // NewHistogram returns a pointer to a new Histogram object that was created
   110  // with the provided options.
   111  func NewHistogram(opts HistogramOptions) *Histogram {
   112  	if opts.NumBuckets == 0 {
   113  		opts.NumBuckets = 32
   114  	}
   115  	if opts.SmallestBucketSize == 0.0 {
   116  		opts.SmallestBucketSize = 1.0
   117  	}
   118  	h := Histogram{
   119  		opts:    opts,
   120  		buckets: make([]bucketInternal, opts.NumBuckets),
   121  		count:   newCounter(),
   122  		sum:     newCounter(),
   123  		tracker: newTracker(),
   124  	}
   125  	low := opts.MinValue
   126  	delta := opts.SmallestBucketSize
   127  	for i := 0; i < opts.NumBuckets; i++ {
   128  		h.buckets[i].lowBound = low
   129  		h.buckets[i].count = newCounter()
   130  		low = low + int64(delta)
   131  		delta = delta * (1.0 + opts.GrowthFactor)
   132  	}
   133  	return &h
   134  }
   135  
   136  // Opts returns a copy of the options used to create the Histogram.
   137  func (h *Histogram) Opts() HistogramOptions {
   138  	return h.opts
   139  }
   140  
   141  // Add adds a value to the histogram.
   142  func (h *Histogram) Add(value int64) error {
   143  	bucket, err := h.findBucket(value)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	h.buckets[bucket].count.Incr(1)
   148  	h.count.Incr(1)
   149  	h.sum.Incr(value)
   150  	h.tracker.Push(value)
   151  	return nil
   152  }
   153  
   154  // LastUpdate returns the time at which the object was last updated.
   155  func (h *Histogram) LastUpdate() time.Time {
   156  	return h.count.LastUpdate()
   157  }
   158  
   159  // Value returns the accumulated state of the histogram since it was created.
   160  func (h *Histogram) Value() HistogramValue {
   161  	b := make([]HistogramBucket, len(h.buckets))
   162  	for i, v := range h.buckets {
   163  		b[i] = HistogramBucket{
   164  			LowBound: v.lowBound,
   165  			Count:    v.count.Value(),
   166  		}
   167  	}
   168  
   169  	v := HistogramValue{
   170  		Count:   h.count.Value(),
   171  		Sum:     h.sum.Value(),
   172  		Min:     h.tracker.Min(),
   173  		Max:     h.tracker.Max(),
   174  		Buckets: b,
   175  	}
   176  	return v
   177  }
   178  
   179  // Delta1h returns the change in the last hour.
   180  func (h *Histogram) Delta1h() HistogramValue {
   181  	b := make([]HistogramBucket, len(h.buckets))
   182  	for i, v := range h.buckets {
   183  		b[i] = HistogramBucket{
   184  			LowBound: v.lowBound,
   185  			Count:    v.count.Delta1h(),
   186  		}
   187  	}
   188  
   189  	v := HistogramValue{
   190  		Count:   h.count.Delta1h(),
   191  		Sum:     h.sum.Delta1h(),
   192  		Min:     h.tracker.Min1h(),
   193  		Max:     h.tracker.Max1h(),
   194  		Buckets: b,
   195  	}
   196  	return v
   197  }
   198  
   199  // Delta10m returns the change in the last 10 minutes.
   200  func (h *Histogram) Delta10m() HistogramValue {
   201  	b := make([]HistogramBucket, len(h.buckets))
   202  	for i, v := range h.buckets {
   203  		b[i] = HistogramBucket{
   204  			LowBound: v.lowBound,
   205  			Count:    v.count.Delta10m(),
   206  		}
   207  	}
   208  
   209  	v := HistogramValue{
   210  		Count:   h.count.Delta10m(),
   211  		Sum:     h.sum.Delta10m(),
   212  		Min:     h.tracker.Min10m(),
   213  		Max:     h.tracker.Max10m(),
   214  		Buckets: b,
   215  	}
   216  	return v
   217  }
   218  
   219  // Delta1m returns the change in the last 10 minutes.
   220  func (h *Histogram) Delta1m() HistogramValue {
   221  	b := make([]HistogramBucket, len(h.buckets))
   222  	for i, v := range h.buckets {
   223  		b[i] = HistogramBucket{
   224  			LowBound: v.lowBound,
   225  			Count:    v.count.Delta1m(),
   226  		}
   227  	}
   228  
   229  	v := HistogramValue{
   230  		Count:   h.count.Delta1m(),
   231  		Sum:     h.sum.Delta1m(),
   232  		Min:     h.tracker.Min1m(),
   233  		Max:     h.tracker.Max1m(),
   234  		Buckets: b,
   235  	}
   236  	return v
   237  }
   238  
   239  // findBucket does a binary search to find in which bucket the value goes.
   240  func (h *Histogram) findBucket(value int64) (int, error) {
   241  	lastBucket := len(h.buckets) - 1
   242  	min, max := 0, lastBucket
   243  	for max >= min {
   244  		b := (min + max) / 2
   245  		if value >= h.buckets[b].lowBound && (b == lastBucket || value < h.buckets[b+1].lowBound) {
   246  			return b, nil
   247  		}
   248  		if value < h.buckets[b].lowBound {
   249  			max = b - 1
   250  			continue
   251  		}
   252  		min = b + 1
   253  	}
   254  	return 0, fmt.Errorf("no bucket for value: %d", value)
   255  }