|
12 | 12 | import java.util.OptionalLong; |
13 | 13 |
|
14 | 14 | /** |
15 | | - * Interface for implementations of exponential histograms adhering to the <a href="https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram">opentelemetry definition</a>. |
16 | | - * This interface explicitly allows for sparse implementation: It does not offer to directly access buckets by index, instead it |
17 | | - * is only possible to iterate over the buckets.<br> |
| 15 | + * Interface for implementations of exponential histograms adhering to the |
| 16 | + * <a href="https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram">OpenTelemetry definition</a>. |
| 17 | + * This interface supports sparse implementations, allowing iteration over buckets without requiring direct index access.<br> |
18 | 18 | * The most important properties are: |
19 | 19 | * <ul> |
20 | | - * <li>The histogram has a scale parameter, which defines the accuracy. The <code>base</code> for the buckets is defined as <code>base = 2^(2^-scale)</code></li> |
21 | | - * <li>The histogram bucket at index <code>i</code> has the range <code>(base^i, base^(i+1)]</code> </li> |
22 | | - * <li>Negative values are represented by a separate negative range of buckets with the boundaries <code>(-base^(i+1), -base^i]</code></li> |
23 | | - * <li>histograms are perfectly subsetting: Increasing the scale by one exactly merges each pair of neighbouring buckets</li> |
24 | | - * <li>a special {@link ZeroBucket} is used to handle zero and close to zero values</li> |
| 20 | + * <li>The histogram has a scale parameter, which defines the accuracy. |
| 21 | + * The {@code base} for the buckets is defined as {@code base = 2^(2^-scale)}</li> |
| 22 | + * <li>The histogram bucket at index {@code i} has the range {@code (base^i, base^(i+1)]}</li> |
| 23 | + * <li>Negative values are represented by a separate negative range of buckets with the boundaries {@code (-base^(i+1), -base^i]}</li> |
| 24 | + * <li>Histograms are perfectly subsetting: increasing the scale by one merges each pair of neighboring buckets</li> |
| 25 | + * <li>A special {@link ZeroBucket} is used to handle zero and close-to-zero values</li> |
25 | 26 | * </ul> |
26 | 27 | * |
27 | 28 | * <br> |
28 | | - * In addition, in all algorithms we make a central assumption about the distribution of samples within each bucket: |
29 | | - * We assume they all lie on the single point of least error relative to the bucket boundaries (see {@link ExponentialScaleUtils#getPointOfLeastRelativeError(long, int)}). |
| 29 | + * Additionally, all algorithms assume that samples within a bucket are located at a single point: the point of least relative error |
| 30 | + * (see {@link ExponentialScaleUtils#getPointOfLeastRelativeError(long, int)}). |
30 | 31 | */ |
31 | 32 | public interface ExponentialHistogram { |
32 | 33 |
|
33 | | - //TODO: support min/max/sum/count storage and merging |
34 | | - //TODO: Add special positive and negative infinity buckets to allow representation of explicit bucket histograms with open boundaries |
| 34 | + // TODO: support min/max/sum/count storage and merging |
| 35 | + // TODO: Add special positive and negative infinity buckets to allow representation of explicit bucket histograms with open boundaries |
35 | 36 |
|
36 | | - // scale of 38 is the largest scale where at the borders we don't run into problems due to floating point precision when computing |
37 | | - // indices for double values |
38 | | - // Theoretically, a MAX_SCALE of 51 would work and would still cover the entire range of double values |
39 | | - // For that to work, we'll have to rework the math of converting from double to indices and back |
| 37 | + // A scale of 38 is the largest scale where we don't run into problems at the borders due to floating-point precision when computing |
| 38 | + // indices for double values. |
| 39 | + // Theoretically, a MAX_SCALE of 51 would work and would still cover the entire range of double values. |
| 40 | + // For that to work, the math for converting from double to indices and back would need to be reworked. |
40 | 41 | // One option would be to use "Quadruple": https://github.com/m-vokhm/Quadruple |
41 | 42 | int MAX_SCALE = 38; |
42 | 43 |
|
43 | | - // At this scale all double values already fall into a single bucket |
| 44 | + // At this scale, all double values fall into a single bucket. |
44 | 45 | int MIN_SCALE = -11; |
45 | 46 |
|
46 | | - // Only use 62 bit at max to allow to compute the difference between the smallest and largest index without causing overflow |
47 | | - // Also the extra bit gives us room for some tricks for compact storage |
| 47 | + // Only use 62 bits at max to allow computing the difference between the smallest and largest index without causing an overflow. |
| 48 | + // The extra bit also provides room for compact storage tricks. |
48 | 49 | int MAX_INDEX_BITS = 62; |
49 | 50 | long MAX_INDEX = (1L << MAX_INDEX_BITS) - 1; |
50 | 51 | long MIN_INDEX = -MAX_INDEX; |
51 | 52 |
|
52 | 53 | /** |
53 | | - * The scale of the histogram. Higher scales result in higher accuracy, but potentially higher bucket count. |
| 54 | + * The scale of the histogram. Higher scales result in higher accuracy but potentially more buckets. |
54 | 55 | * Must be less than or equal to {@link #MAX_SCALE} and greater than or equal to {@link #MIN_SCALE}. |
| 56 | + * |
| 57 | + * @return the scale of the histogram |
55 | 58 | */ |
56 | 59 | int scale(); |
57 | 60 |
|
58 | 61 | /** |
59 | | - * @return the {@link ZeroBucket} representing the number of zero (or close to zero) values and its threshold |
| 62 | + * @return the {@link ZeroBucket} representing the number of zero (or close-to-zero) values and its threshold |
60 | 63 | */ |
61 | 64 | ZeroBucket zeroBucket(); |
62 | 65 |
|
63 | 66 | /** |
64 | | - * @return a {@link BucketIterator} for the populated, positive buckets of this histogram. {@link BucketIterator#scale()} of the return value must return the same value as {@link #scale()}. |
| 67 | + * @return a {@link BucketIterator} for the populated, positive buckets of this histogram. |
| 68 | + * The {@link BucketIterator#scale()} of the returned iterator must be the same as {@link #scale()}. |
65 | 69 | */ |
66 | 70 | CopyableBucketIterator positiveBuckets(); |
67 | 71 |
|
68 | 72 | /** |
69 | | - * @return a {@link BucketIterator} for the populated, negative buckets of this histogram. {@link BucketIterator#scale()} of the return value must return the same value as {@link #scale()}. |
| 73 | + * @return a {@link BucketIterator} for the populated, negative buckets of this histogram. |
| 74 | + * The {@link BucketIterator#scale()} of the returned iterator must be the same as {@link #scale()}. |
70 | 75 | */ |
71 | 76 | CopyableBucketIterator negativeBuckets(); |
72 | 77 |
|
73 | 78 | /** |
74 | 79 | * Returns the highest populated bucket index, taking both negative and positive buckets into account. |
75 | | - * If there are neither positive nor negative buckets populated, an empty optional is returned. |
| 80 | + * |
| 81 | + * @return the highest populated bucket index, or an empty optional if no buckets are populated |
76 | 82 | */ |
77 | 83 | OptionalLong maximumBucketIndex(); |
78 | 84 |
|
79 | 85 | /** |
80 | | - * Iterator over non-empty buckets of the histogram. Can represent either the positive or negative histogram range. |
| 86 | + * An iterator over the non-empty buckets of the histogram for either the positive or negative range. |
81 | 87 | * <ul> |
82 | | - * <li>The iterator always iterates from the lowest bucket index to the highest</li> |
83 | | - * <li>The iterator never returns duplicate buckets (buckets with the same index) </li> |
84 | | - * <li>The iterator never returns empty buckets ({@link #peekCount() is never zero}</li> |
| 88 | + * <li>The iterator always iterates from the lowest bucket index to the highest.</li> |
| 89 | + * <li>The iterator never returns duplicate buckets (buckets with the same index).</li> |
| 90 | + * <li>The iterator never returns empty buckets ({@link #peekCount()} is never zero).</li> |
85 | 91 | * </ul> |
86 | 92 | */ |
87 | 93 | interface BucketIterator { |
88 | 94 | /** |
89 | 95 | * Checks if there are any buckets remaining to be visited by this iterator. |
90 | | - * If the end has been reached, it is illegal to call {@link #peekCount()}, {@link #peekIndex()} or {@link #advance()}. |
| 96 | + * If the end has been reached, it is illegal to call {@link #peekCount()}, {@link #peekIndex()}, or {@link #advance()}. |
91 | 97 | * |
92 | | - * @return <code>false</code>, if the end has been reached, <code>true</code> otherwise. |
| 98 | + * @return {@code true} if the iterator has more elements, {@code false} otherwise |
93 | 99 | */ |
94 | 100 | boolean hasNext(); |
95 | 101 |
|
96 | 102 | /** |
97 | | - * The number of items in the bucket this iterator currently points at. Does not advance the iterator by itself and therefore can be called repeatedly to return the same value. |
98 | | - * Must not be called if {@link #hasNext()} returns <code>false</code>. |
| 103 | + * The number of items in the bucket at the current iterator position. Does not advance the iterator. |
| 104 | + * Must not be called if {@link #hasNext()} returns {@code false}. |
99 | 105 | * |
100 | 106 | * @return the number of items in the bucket, always greater than zero |
101 | 107 | */ |
102 | 108 | long peekCount(); |
103 | 109 |
|
104 | 110 | /** |
105 | | - * The index of the bucket this iterator currently points at. Does not advance the iterator by itself and therefore can be called repeatedly to return the same value. |
106 | | - * Must not be called if {@link #hasNext()} returns <code>false</code>. |
| 111 | + * The index of the bucket at the current iterator position. Does not advance the iterator. |
| 112 | + * Must not be called if {@link #hasNext()} returns {@code false}. |
107 | 113 | * |
108 | 114 | * @return the index of the bucket, guaranteed to be in the range [{@link #MIN_INDEX}, {@link #MAX_INDEX}] |
109 | 115 | */ |
110 | 116 | long peekIndex(); |
111 | 117 |
|
112 | 118 | /** |
113 | 119 | * Moves the iterator to the next, non-empty bucket. |
114 | | - * If {@link #hasNext()} is <code>true</code> after {@link #advance()}, {@link #peekIndex()} is guaranteed to return a value bigger than prior to the {@link #advance()} call. |
| 120 | + * If {@link #hasNext()} is {@code true} after calling {@link #advance()}, {@link #peekIndex()} is guaranteed to return a value |
| 121 | + * greater than the value returned prior to the {@link #advance()} call. |
115 | 122 | */ |
116 | 123 | void advance(); |
117 | 124 |
|
118 | 125 | /** |
119 | 126 | * Provides the scale that can be used to convert indices returned by {@link #peekIndex()} to the bucket boundaries, |
120 | | - * e.g. via {@link ExponentialScaleUtils#getLowerBucketBoundary(long, int)}. |
| 127 | + * e.g., via {@link ExponentialScaleUtils#getLowerBucketBoundary(long, int)}. |
121 | 128 | * |
122 | | - * @return the scale, which is guaranteed to be constant over the lifetime of this iterator. |
| 129 | + * @return the scale, which is guaranteed to be constant over the lifetime of this iterator |
123 | 130 | */ |
124 | 131 | int scale(); |
125 | 132 | } |
126 | 133 |
|
127 | 134 | /** |
128 | | - * A {@link BucketIterator} which can be copied. |
| 135 | + * A {@link BucketIterator} that can be copied. |
129 | 136 | */ |
130 | 137 | interface CopyableBucketIterator extends BucketIterator { |
131 | 138 |
|
132 | 139 | /** |
133 | | - * Provides a bucket iterator pointing at the same bucket of the same range of buckets as this iterator. |
134 | | - * Calling {@link #advance()} on the copied iterator does not affect <code>this</code> and vice-versa. |
| 140 | + * Creates a copy of this bucket iterator, pointing at the same bucket of the same range of buckets. |
| 141 | + * Calling {@link #advance()} on the copied iterator does not affect this instance and vice-versa. |
| 142 | + * |
| 143 | + * @return a copy of this iterator |
135 | 144 | */ |
136 | 145 | CopyableBucketIterator copy(); |
137 | 146 | } |
138 | | - |
139 | 147 | } |
0 commit comments