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 }