3 * Copyright 2017 gRPC authors.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
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.
36 // Sum is the sum of all the values added to the histogram.
38 // SumOfSquares is the sum of squares of all values.
40 // Min is the minimum of all the values added to the histogram.
42 // Max is the maximum of all the values added to the histogram.
44 // Buckets contains all the buckets of the histogram.
45 Buckets []HistogramBucket
48 logBaseBucketSize float64
49 oneOverLogOnePlusGrowthFactor float64
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.
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.
63 // BaseBucketSize is the size of the first bucket.
64 BaseBucketSize float64
65 // MinValue is the lower bound of the first bucket.
69 // HistogramBucket represents one histogram bucket.
70 type HistogramBucket struct {
71 // LowBound is the lower bound of the bucket.
73 // Count is the number of values in the bucket.
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 {
83 if opts.BaseBucketSize == 0.0 {
84 opts.BaseBucketSize = 1.0
87 Buckets: make([]HistogramBucket, opts.NumBuckets),
92 logBaseBucketSize: math.Log(opts.BaseBucketSize),
93 oneOverLogOnePlusGrowthFactor: 1 / math.Log(1+opts.GrowthFactor),
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
105 // Print writes textual output of the histogram values.
106 func (h *Histogram) Print(w io.Writer) {
107 h.PrintWithUnit(w, 1)
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))
120 maxBucketDigitLen := len(strconv.FormatFloat(h.Buckets[len(h.Buckets)-1].LowBound, 'f', 6, 64))
121 if maxBucketDigitLen < 3 {
123 maxBucketDigitLen = 3
125 maxCountDigitLen := len(strconv.FormatInt(h.Count, 10))
126 percentMulti := 100 / float64(h.Count)
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)
134 fmt.Fprintf(w, "%*s)", maxBucketDigitLen, "inf")
138 fmt.Fprintf(w, " %*d %5.1f%% %5.1f%%", maxCountDigitLen, b.Count, float64(b.Count)*percentMulti, float64(accCount)*percentMulti)
141 barLength := int(float64(b.Count)*percentMulti*barScale + 0.5)
142 fmt.Fprintf(w, " %s\n", strings.Repeat("#", barLength))
146 // String returns the textual output of the histogram values as string.
147 func (h *Histogram) String() string {
153 // Clear resets all the content of histogram.
154 func (h *Histogram) Clear() {
158 h.Min = math.MaxInt64
159 h.Max = math.MinInt64
160 for i := range h.Buckets {
161 h.Buckets[i].Count = 0
165 // Opts returns a copy of the options used to create the Histogram.
166 func (h *Histogram) Opts() HistogramOptions {
170 // Add adds a value to the histogram.
171 func (h *Histogram) Add(value int64) error {
172 bucket, err := h.findBucket(value)
176 h.Buckets[bucket].Count++
179 h.SumOfSquares += value * value
189 func (h *Histogram) findBucket(value int64) (int, error) {
190 delta := float64(value - h.opts.MinValue)
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)
198 if b >= len(h.Buckets) {
199 return 0, fmt.Errorf("no bucket for value: %d", value)
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")
212 h.SumOfSquares += h2.SumOfSquares
219 for i, b := range h2.Buckets {
220 h.Buckets[i].Count += b.Count