OSDN Git Service

new repo
[bytom/vapor.git] / vendor / google.golang.org / grpc / benchmark / stats / histogram.go
1 /*
2  *
3  * Copyright 2017 gRPC authors.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18
19 package stats
20
21 import (
22         "bytes"
23         "fmt"
24         "io"
25         "log"
26         "math"
27         "strconv"
28         "strings"
29 )
30
31 // Histogram accumulates values in the form of a histogram with
32 // exponentially increased bucket sizes.
33 type Histogram struct {
34         // Count is the total number of values added to the histogram.
35         Count int64
36         // Sum is the sum of all the values added to the histogram.
37         Sum int64
38         // SumOfSquares is the sum of squares of all values.
39         SumOfSquares int64
40         // Min is the minimum of all the values added to the histogram.
41         Min int64
42         // Max is the maximum of all the values added to the histogram.
43         Max int64
44         // Buckets contains all the buckets of the histogram.
45         Buckets []HistogramBucket
46
47         opts                          HistogramOptions
48         logBaseBucketSize             float64
49         oneOverLogOnePlusGrowthFactor float64
50 }
51
52 // HistogramOptions contains the parameters that define the histogram's buckets.
53 // The first bucket of the created histogram (with index 0) contains [min, min+n)
54 // where n = BaseBucketSize, min = MinValue.
55 // Bucket i (i>=1) contains [min + n * m^(i-1), min + n * m^i), where m = 1+GrowthFactor.
56 // The type of the values is int64.
57 type HistogramOptions struct {
58         // NumBuckets is the number of buckets.
59         NumBuckets int
60         // GrowthFactor is the growth factor of the buckets. A value of 0.1
61         // indicates that bucket N+1 will be 10% larger than bucket N.
62         GrowthFactor float64
63         // BaseBucketSize is the size of the first bucket.
64         BaseBucketSize float64
65         // MinValue is the lower bound of the first bucket.
66         MinValue int64
67 }
68
69 // HistogramBucket represents one histogram bucket.
70 type HistogramBucket struct {
71         // LowBound is the lower bound of the bucket.
72         LowBound float64
73         // Count is the number of values in the bucket.
74         Count int64
75 }
76
77 // NewHistogram returns a pointer to a new Histogram object that was created
78 // with the provided options.
79 func NewHistogram(opts HistogramOptions) *Histogram {
80         if opts.NumBuckets == 0 {
81                 opts.NumBuckets = 32
82         }
83         if opts.BaseBucketSize == 0.0 {
84                 opts.BaseBucketSize = 1.0
85         }
86         h := Histogram{
87                 Buckets: make([]HistogramBucket, opts.NumBuckets),
88                 Min:     math.MaxInt64,
89                 Max:     math.MinInt64,
90
91                 opts:                          opts,
92                 logBaseBucketSize:             math.Log(opts.BaseBucketSize),
93                 oneOverLogOnePlusGrowthFactor: 1 / math.Log(1+opts.GrowthFactor),
94         }
95         m := 1.0 + opts.GrowthFactor
96         delta := opts.BaseBucketSize
97         h.Buckets[0].LowBound = float64(opts.MinValue)
98         for i := 1; i < opts.NumBuckets; i++ {
99                 h.Buckets[i].LowBound = float64(opts.MinValue) + delta
100                 delta = delta * m
101         }
102         return &h
103 }
104
105 // Print writes textual output of the histogram values.
106 func (h *Histogram) Print(w io.Writer) {
107         h.PrintWithUnit(w, 1)
108 }
109
110 // PrintWithUnit writes textual output of the histogram values  .
111 // Data in histogram is divided by a Unit before print.
112 func (h *Histogram) PrintWithUnit(w io.Writer, unit float64) {
113         avg := float64(h.Sum) / float64(h.Count)
114         fmt.Fprintf(w, "Count: %d  Min: %5.1f  Max: %5.1f  Avg: %.2f\n", h.Count, float64(h.Min)/unit, float64(h.Max)/unit, avg/unit)
115         fmt.Fprintf(w, "%s\n", strings.Repeat("-", 60))
116         if h.Count <= 0 {
117                 return
118         }
119
120         maxBucketDigitLen := len(strconv.FormatFloat(h.Buckets[len(h.Buckets)-1].LowBound, 'f', 6, 64))
121         if maxBucketDigitLen < 3 {
122                 // For "inf".
123                 maxBucketDigitLen = 3
124         }
125         maxCountDigitLen := len(strconv.FormatInt(h.Count, 10))
126         percentMulti := 100 / float64(h.Count)
127
128         accCount := int64(0)
129         for i, b := range h.Buckets {
130                 fmt.Fprintf(w, "[%*f, ", maxBucketDigitLen, b.LowBound/unit)
131                 if i+1 < len(h.Buckets) {
132                         fmt.Fprintf(w, "%*f)", maxBucketDigitLen, h.Buckets[i+1].LowBound/unit)
133                 } else {
134                         fmt.Fprintf(w, "%*s)", maxBucketDigitLen, "inf")
135                 }
136
137                 accCount += b.Count
138                 fmt.Fprintf(w, "  %*d  %5.1f%%  %5.1f%%", maxCountDigitLen, b.Count, float64(b.Count)*percentMulti, float64(accCount)*percentMulti)
139
140                 const barScale = 0.1
141                 barLength := int(float64(b.Count)*percentMulti*barScale + 0.5)
142                 fmt.Fprintf(w, "  %s\n", strings.Repeat("#", barLength))
143         }
144 }
145
146 // String returns the textual output of the histogram values as string.
147 func (h *Histogram) String() string {
148         var b bytes.Buffer
149         h.Print(&b)
150         return b.String()
151 }
152
153 // Clear resets all the content of histogram.
154 func (h *Histogram) Clear() {
155         h.Count = 0
156         h.Sum = 0
157         h.SumOfSquares = 0
158         h.Min = math.MaxInt64
159         h.Max = math.MinInt64
160         for i := range h.Buckets {
161                 h.Buckets[i].Count = 0
162         }
163 }
164
165 // Opts returns a copy of the options used to create the Histogram.
166 func (h *Histogram) Opts() HistogramOptions {
167         return h.opts
168 }
169
170 // Add adds a value to the histogram.
171 func (h *Histogram) Add(value int64) error {
172         bucket, err := h.findBucket(value)
173         if err != nil {
174                 return err
175         }
176         h.Buckets[bucket].Count++
177         h.Count++
178         h.Sum += value
179         h.SumOfSquares += value * value
180         if value < h.Min {
181                 h.Min = value
182         }
183         if value > h.Max {
184                 h.Max = value
185         }
186         return nil
187 }
188
189 func (h *Histogram) findBucket(value int64) (int, error) {
190         delta := float64(value - h.opts.MinValue)
191         var b int
192         if delta >= h.opts.BaseBucketSize {
193                 // b = log_{1+growthFactor} (delta / baseBucketSize) + 1
194                 //   = log(delta / baseBucketSize) / log(1+growthFactor) + 1
195                 //   = (log(delta) - log(baseBucketSize)) * (1 / log(1+growthFactor)) + 1
196                 b = int((math.Log(delta)-h.logBaseBucketSize)*h.oneOverLogOnePlusGrowthFactor + 1)
197         }
198         if b >= len(h.Buckets) {
199                 return 0, fmt.Errorf("no bucket for value: %d", value)
200         }
201         return b, nil
202 }
203
204 // Merge takes another histogram h2, and merges its content into h.
205 // The two histograms must be created by equivalent HistogramOptions.
206 func (h *Histogram) Merge(h2 *Histogram) {
207         if h.opts != h2.opts {
208                 log.Fatalf("failed to merge histograms, created by inequivalent options")
209         }
210         h.Count += h2.Count
211         h.Sum += h2.Sum
212         h.SumOfSquares += h2.SumOfSquares
213         if h2.Min < h.Min {
214                 h.Min = h2.Min
215         }
216         if h2.Max > h.Max {
217                 h.Max = h2.Max
218         }
219         for i, b := range h2.Buckets {
220                 h.Buckets[i].Count += b.Count
221         }
222 }