Skip to content

Commit 2cea9a7

Browse files
committed
Felix review fixes
1 parent af51b7c commit 2cea9a7

File tree

3 files changed

+34
-29
lines changed

3 files changed

+34
-29
lines changed

libs/exponential-histogram/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ This library provides an implementation of merging and analysis algorithms for e
22

33
## Overview
44

5-
The library implements base-2 exponential histograms with perfect subsetting. The most imporant properties are:
5+
The library implements base-2 exponential histograms with perfect subsetting. The most important properties are:
66

77
* The histogram has a scale parameter, which defines the accuracy. A higher scale implies a higher accuracy.
88
* The `base` for the buckets is defined as `base = 2^(2^-scale)`.
99
* The histogram bucket at index `i` has the range `(base^i, base^(i+1)]`
1010
* Negative values are represented by a separate negative range of buckets with the boundaries `(-base^(i+1), -base^i]`
11-
* Histograms are perfectly subsetting: increasing the scale by one merges each pair of neighboring buckets
11+
* Histograms support perfect subsetting: when the scale is decreased by one, each pair of adjacent buckets is merged into a single bucket without introducing error
1212
* A special zero bucket with a zero-threshold is used to handle zero and close-to-zero values
1313

1414
For more details please refer to the [OpenTelemetry definition](https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram).
@@ -89,7 +89,7 @@ bucketIndiciesToCounts: {
8989
```
9090

9191
Downscaling on the sparse representation only happens if either:
92-
* The number of populated buckets would become bigger than our maximum bucket count. We have to downscale to make neighboring, populated buckets combine to a single bucket until we are below our limit again.
92+
* The number of populated buckets would become bigger than our maximum bucket count. We have to downscale to combine neighboring, populated buckets to a single bucket until we are below our limit again.
9393
* The highest or smallest indices require more bits to store than we allow. This does not happen in our implementation for normal inputs, because we allow up to 62 bits for index storage, which fits the entire numeric range of IEEE 754 double precision floats at our maximum scale.
9494

9595
### Handling Explicit Bucket Histograms

libs/exponential-histogram/src/main/java/org/elasticsearch/exponentialhistogram/ExponentialHistogramMerger.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
package org.elasticsearch.exponentialhistogram;
1111

12+
import java.util.OptionalLong;
13+
1214
import static org.elasticsearch.exponentialhistogram.ExponentialScaleUtils.getMaximumScaleIncrease;
1315

1416
/**
@@ -97,14 +99,18 @@ private void doMerge(ExponentialHistogram b) {
9799
if (targetScale > b.scale()) {
98100
if (negBucketsB.hasNext()) {
99101
long smallestIndex = negBucketsB.peekIndex();
100-
long highestIndex = b.negativeBuckets().maxBucketIndex().getAsLong();
101-
int maxScaleIncrease = Math.min(getMaximumScaleIncrease(smallestIndex), getMaximumScaleIncrease(highestIndex));
102+
OptionalLong maximumIndex = b.negativeBuckets().maxBucketIndex();
103+
assert maximumIndex.isPresent()
104+
: "We checked that the negative bucket range is not empty, therefore the maximum index should be present";
105+
int maxScaleIncrease = Math.min(getMaximumScaleIncrease(smallestIndex), getMaximumScaleIncrease(maximumIndex.getAsLong()));
102106
targetScale = Math.min(targetScale, b.scale() + maxScaleIncrease);
103107
}
104108
if (posBucketsB.hasNext()) {
105109
long smallestIndex = posBucketsB.peekIndex();
106-
long highestIndex = b.positiveBuckets().maxBucketIndex().getAsLong();
107-
int maxScaleIncrease = Math.min(getMaximumScaleIncrease(smallestIndex), getMaximumScaleIncrease(highestIndex));
110+
OptionalLong maximumIndex = b.positiveBuckets().maxBucketIndex();
111+
assert maximumIndex.isPresent()
112+
: "We checked that the positive bucket range is not empty, therefore the maximum index should be present";
113+
int maxScaleIncrease = Math.min(getMaximumScaleIncrease(smallestIndex), getMaximumScaleIncrease(maximumIndex.getAsLong()));
108114
targetScale = Math.min(targetScale, b.scale() + maxScaleIncrease);
109115
}
110116
}

libs/exponential-histogram/src/main/java/org/elasticsearch/exponentialhistogram/FixedCapacityExponentialHistogram.java

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,19 @@
2020
final class FixedCapacityExponentialHistogram implements ExponentialHistogram {
2121

2222
// These arrays represent both the positive and the negative buckets.
23-
// They store all negative buckets first, in ascending index order, followed by all positive buckets, also in ascending index order.
23+
// They store all buckets for the negative range first, in ascending index order,
24+
// followed by all buckets for the positive range, also in ascending index order.
25+
// This means we store the buckets ordered by their boundaries in ascending order (from -INF to +INF).
2426
private final long[] bucketIndices;
2527
private final long[] bucketCounts;
2628

2729
private int bucketScale;
2830

29-
private final AbstractBuckets negativeBuckets = new AbstractBuckets() {
30-
@Override
31-
int startSlot() {
32-
return 0;
33-
}
34-
};
31+
private final Buckets negativeBuckets = new Buckets(false);
3532

3633
private ZeroBucket zeroBucket;
3734

38-
private final AbstractBuckets positiveBuckets = new AbstractBuckets() {
39-
@Override
40-
int startSlot() {
41-
return negativeBuckets.numBuckets;
42-
}
43-
};
35+
private final Buckets positiveBuckets = new Buckets(true);
4436

4537
/**
4638
* Creates an empty histogram with the given capacity and a {@link ZeroBucket#minimalEmpty()} zero bucket.
@@ -57,7 +49,7 @@ int startSlot() {
5749
/**
5850
* Resets this histogram to the same state as a newly constructed one with the same capacity.
5951
*/
60-
public void reset() {
52+
void reset() {
6153
setZeroBucket(ZeroBucket.minimalEmpty());
6254
resetBuckets(MAX_SCALE);
6355
}
@@ -67,7 +59,7 @@ public void reset() {
6759
*
6860
* @param scale the scale to set for this histogram
6961
*/
70-
public void resetBuckets(int scale) {
62+
void resetBuckets(int scale) {
7163
if (scale > MAX_SCALE || scale < MIN_SCALE) {
7264
throw new IllegalArgumentException("scale must be in range [" + MIN_SCALE + ".." + MAX_SCALE + "]");
7365
}
@@ -88,7 +80,7 @@ public ZeroBucket zeroBucket() {
8880
*
8981
* @param zeroBucket the zero bucket to set
9082
*/
91-
public void setZeroBucket(ZeroBucket zeroBucket) {
83+
void setZeroBucket(ZeroBucket zeroBucket) {
9284
this.zeroBucket = zeroBucket;
9385
}
9486

@@ -112,7 +104,7 @@ public void setZeroBucket(ZeroBucket zeroBucket) {
112104
* @param isPositive {@code true} if the bucket belongs to the positive range, {@code false} if it belongs to the negative range
113105
* @return {@code true} if the bucket was added, {@code false} if it could not be added due to insufficient capacity
114106
*/
115-
public boolean tryAddBucket(long index, long count, boolean isPositive) {
107+
boolean tryAddBucket(long index, long count, boolean isPositive) {
116108
if (index < MIN_INDEX || index > MAX_INDEX) {
117109
throw new IllegalArgumentException("index must be in range [" + MIN_INDEX + ".." + MAX_INDEX + "]");
118110
}
@@ -135,29 +127,36 @@ public int scale() {
135127
}
136128

137129
@Override
138-
public Buckets negativeBuckets() {
130+
public ExponentialHistogram.Buckets negativeBuckets() {
139131
return negativeBuckets;
140132
}
141133

142134
@Override
143-
public Buckets positiveBuckets() {
135+
public ExponentialHistogram.Buckets positiveBuckets() {
144136
return positiveBuckets;
145137
}
146138

147-
private abstract class AbstractBuckets implements Buckets {
139+
private class Buckets implements ExponentialHistogram.Buckets {
148140

141+
private final boolean isPositive;
149142
private int numBuckets;
150143
private int cachedValueSumForNumBuckets;
151144
private long cachedValueSum;
152145

153-
AbstractBuckets() {
146+
/**
147+
* @param isPositive true, if this object should represent the positive bucket range, false for the negative range
148+
*/
149+
Buckets(boolean isPositive) {
150+
this.isPositive = isPositive;
154151
reset();
155152
}
156153

157154
/**
158155
* @return the array index of the first bucket of this set of buckets within {@link #bucketCounts} and {@link #bucketIndices}.
159156
*/
160-
abstract int startSlot();
157+
int startSlot() {
158+
return isPositive ? negativeBuckets.numBuckets : 0;
159+
}
161160

162161
final void reset() {
163162
numBuckets = 0;

0 commit comments

Comments
 (0)