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

     1  package stats
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"math"
     8  	"time"
     9  )
    10  
    11  // Stats is a simple helper for gathering additional statistics like histogram
    12  // during benchmarks. This is not thread safe.
    13  type Stats struct {
    14  	numBuckets int
    15  	unit       time.Duration
    16  	min, max   int64
    17  	histogram  *Histogram
    18  
    19  	durations durationSlice
    20  	dirty     bool
    21  }
    22  
    23  type durationSlice []time.Duration
    24  
    25  // NewStats creates a new Stats instance. If numBuckets is not positive,
    26  // the default value (16) will be used.
    27  func NewStats(numBuckets int) *Stats {
    28  	if numBuckets <= 0 {
    29  		numBuckets = 16
    30  	}
    31  	return &Stats{
    32  		// Use one more bucket for the last unbounded bucket.
    33  		numBuckets: numBuckets + 1,
    34  		durations:  make(durationSlice, 0, 100000),
    35  	}
    36  }
    37  
    38  // Add adds an elapsed time per operation to the stats.
    39  func (stats *Stats) Add(d time.Duration) {
    40  	stats.durations = append(stats.durations, d)
    41  	stats.dirty = true
    42  }
    43  
    44  // Clear resets the stats, removing all values.
    45  func (stats *Stats) Clear() {
    46  	stats.durations = stats.durations[:0]
    47  	stats.histogram = nil
    48  	stats.dirty = false
    49  }
    50  
    51  // maybeUpdate updates internal stat data if there was any newly added
    52  // stats since this was updated.
    53  func (stats *Stats) maybeUpdate() {
    54  	if !stats.dirty {
    55  		return
    56  	}
    57  
    58  	stats.min = math.MaxInt64
    59  	stats.max = 0
    60  	for _, d := range stats.durations {
    61  		if stats.min > int64(d) {
    62  			stats.min = int64(d)
    63  		}
    64  		if stats.max < int64(d) {
    65  			stats.max = int64(d)
    66  		}
    67  	}
    68  
    69  	// Use the largest unit that can represent the minimum time duration.
    70  	stats.unit = time.Nanosecond
    71  	for _, u := range []time.Duration{time.Microsecond, time.Millisecond, time.Second} {
    72  		if stats.min <= int64(u) {
    73  			break
    74  		}
    75  		stats.unit = u
    76  	}
    77  
    78  	// Adjust the min/max according to the new unit.
    79  	stats.min /= int64(stats.unit)
    80  	stats.max /= int64(stats.unit)
    81  	numBuckets := stats.numBuckets
    82  	if n := int(stats.max - stats.min + 1); n < numBuckets {
    83  		numBuckets = n
    84  	}
    85  	stats.histogram = NewHistogram(HistogramOptions{
    86  		NumBuckets: numBuckets,
    87  		// max(i.e., Nth lower bound) = min + (1 + growthFactor)^(numBuckets-2).
    88  		GrowthFactor:       math.Pow(float64(stats.max-stats.min), 1/float64(stats.numBuckets-2)) - 1,
    89  		SmallestBucketSize: 1.0,
    90  		MinValue:           stats.min})
    91  
    92  	for _, d := range stats.durations {
    93  		stats.histogram.Add(int64(d / stats.unit))
    94  	}
    95  
    96  	stats.dirty = false
    97  }
    98  
    99  // Print writes textual output of the Stats.
   100  func (stats *Stats) Print(w io.Writer) {
   101  	stats.maybeUpdate()
   102  
   103  	if stats.histogram == nil {
   104  		fmt.Fprint(w, "Histogram (empty)\n")
   105  	} else {
   106  		fmt.Fprintf(w, "Histogram (unit: %s)\n", fmt.Sprintf("%v", stats.unit)[1:])
   107  		stats.histogram.Value().Print(w)
   108  	}
   109  }
   110  
   111  // String returns the textual output of the Stats as string.
   112  func (stats *Stats) String() string {
   113  	var b bytes.Buffer
   114  	stats.Print(&b)
   115  	return b.String()
   116  }