|
| 1 | +/* |
| 2 | +Copyright 2017 The Kubernetes Authors. |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +package util |
| 18 | + |
| 19 | +import ( |
| 20 | + "errors" |
| 21 | + "fmt" |
| 22 | + "math" |
| 23 | +) |
| 24 | + |
| 25 | +// HistogramOptions define the number and size of buckets of a histogram. |
| 26 | +type HistogramOptions interface { |
| 27 | + // Returns the number of buckets in the histogram. |
| 28 | + NumBuckets() int |
| 29 | + // Returns the index of the bucket to which the given value falls. |
| 30 | + // If the value is outside of the range covered by the histogram, it |
| 31 | + // returns the closest bucket (either the first or the last one). |
| 32 | + FindBucket(value float64) int |
| 33 | + // Returns the start of the bucket with a given index. If the index is |
| 34 | + // outside the [0..NumBuckets() - 1] range, the result is undefined. |
| 35 | + GetBucketStart(bucket int) float64 |
| 36 | + // Returns the minimum weight for a bucket to be considered non-empty. |
| 37 | + Epsilon() float64 |
| 38 | +} |
| 39 | + |
| 40 | +// NewLinearHistogramOptions returns HistogramOptions describing a histogram |
| 41 | +// with a given number of fixed-size buckets, with the first bucket start at 0.0 |
| 42 | +// and the last bucket start larger or equal to maxValue. |
| 43 | +// Requires maxValue > 0, bucketSize > 0, epsilon > 0. |
| 44 | +func NewLinearHistogramOptions( |
| 45 | + maxValue float64, bucketSize float64, epsilon float64) (HistogramOptions, error) { |
| 46 | + if maxValue <= 0.0 || bucketSize <= 0.0 || epsilon <= 0.0 { |
| 47 | + return nil, errors.New("maxValue and bucketSize must both be positive") |
| 48 | + } |
| 49 | + numBuckets := int(math.Ceil(maxValue/bucketSize)) + 1 |
| 50 | + return &linearHistogramOptions{numBuckets, bucketSize, epsilon}, nil |
| 51 | +} |
| 52 | + |
| 53 | +// NewExponentialHistogramOptions returns HistogramOptions describing a |
| 54 | +// histogram with exponentially growing bucket boundaries. The first bucket |
| 55 | +// covers the range [0..firstBucketSize). Consecutive buckets are of the form |
| 56 | +// [x(n)..x(n) * ratio) for n = 1 .. numBuckets - 1. |
| 57 | +// The last bucket start is larger or equal to maxValue. |
| 58 | +// Requires maxValue > 0, firstBucketSize > 0, ratio > 1, epsilon > 0. |
| 59 | +func NewExponentialHistogramOptions( |
| 60 | + maxValue float64, firstBucketSize float64, ratio float64, epsilon float64) (HistogramOptions, error) { |
| 61 | + if maxValue <= 0.0 || firstBucketSize <= 0.0 || ratio <= 1.0 || epsilon <= 0.0 { |
| 62 | + return nil, errors.New( |
| 63 | + "maxValue, firstBucketSize and epsilon must be > 0.0, ratio must be > 1.0") |
| 64 | + } |
| 65 | + numBuckets := int(math.Ceil(math.Log(maxValue/firstBucketSize)/math.Log(ratio))) + 2 |
| 66 | + return &exponentialHistogramOptions{numBuckets, firstBucketSize, ratio, epsilon}, nil |
| 67 | +} |
| 68 | + |
| 69 | +type linearHistogramOptions struct { |
| 70 | + numBuckets int |
| 71 | + bucketSize float64 |
| 72 | + epsilon float64 |
| 73 | +} |
| 74 | + |
| 75 | +type exponentialHistogramOptions struct { |
| 76 | + numBuckets int |
| 77 | + firstBucketSize float64 |
| 78 | + ratio float64 |
| 79 | + epsilon float64 |
| 80 | +} |
| 81 | + |
| 82 | +func (o *linearHistogramOptions) NumBuckets() int { |
| 83 | + return o.numBuckets |
| 84 | +} |
| 85 | + |
| 86 | +func (o *linearHistogramOptions) FindBucket(value float64) int { |
| 87 | + bucket := int(value / o.bucketSize) |
| 88 | + if bucket < 0 { |
| 89 | + return 0 |
| 90 | + } |
| 91 | + if bucket >= o.numBuckets { |
| 92 | + return o.numBuckets - 1 |
| 93 | + } |
| 94 | + return bucket |
| 95 | +} |
| 96 | + |
| 97 | +func (o *linearHistogramOptions) GetBucketStart(bucket int) float64 { |
| 98 | + if bucket < 0 || bucket >= o.numBuckets { |
| 99 | + panic(fmt.Sprintf("index %d out of range [0..%d]", bucket, o.numBuckets-1)) |
| 100 | + } |
| 101 | + return float64(bucket) * o.bucketSize |
| 102 | +} |
| 103 | + |
| 104 | +func (o *linearHistogramOptions) Epsilon() float64 { |
| 105 | + return o.epsilon |
| 106 | +} |
| 107 | + |
| 108 | +func (o *exponentialHistogramOptions) NumBuckets() int { |
| 109 | + return o.numBuckets |
| 110 | +} |
| 111 | + |
| 112 | +func (o *exponentialHistogramOptions) FindBucket(value float64) int { |
| 113 | + if value < o.firstBucketSize { |
| 114 | + return 0 |
| 115 | + } |
| 116 | + bucket := int(math.Log(value/o.firstBucketSize)/math.Log(o.ratio)) + 1 |
| 117 | + if bucket >= o.numBuckets { |
| 118 | + return o.numBuckets - 1 |
| 119 | + } |
| 120 | + return bucket |
| 121 | +} |
| 122 | + |
| 123 | +func (o *exponentialHistogramOptions) GetBucketStart(bucket int) float64 { |
| 124 | + if bucket < 0 || bucket >= o.numBuckets { |
| 125 | + panic(fmt.Sprintf("index %d out of range [0..%d]", bucket, o.numBuckets-1)) |
| 126 | + } |
| 127 | + if bucket == 0 { |
| 128 | + return 0.0 |
| 129 | + } |
| 130 | + return o.firstBucketSize * math.Pow(o.ratio, float64(bucket-1)) |
| 131 | +} |
| 132 | + |
| 133 | +func (o *exponentialHistogramOptions) Epsilon() float64 { |
| 134 | + return o.epsilon |
| 135 | +} |
0 commit comments