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 }