Skip to content

Commit 0d2a50c

Browse files
committed
Review fixes
1 parent 941223a commit 0d2a50c

File tree

6 files changed

+49
-20
lines changed

6 files changed

+49
-20
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import static org.elasticsearch.exponentialhistogram.ExponentialScaleUtils.computeIndex;
1515

1616
/**
17+
* Only intended for use in tests currently.
1718
* A class for accumulating raw values into an {@link ExponentialHistogram} with a given maximum number of buckets.
1819
*
1920
* If the number of values is less than or equal to the bucket capacity, the resulting histogram is guaranteed
@@ -86,9 +87,13 @@ private void mergeValuesToHistogram() {
8687
valueBuffer.reset();
8788
int scale = valueBuffer.scale();
8889

89-
// Buckets must be provided in ascending index-order
90-
// for the negative range, smaller bigger correspond to -INF and smaller ones closer to zero
91-
// therefore we have to iterate the negative values in reverse order, from the value closest to -INF to the value closest to zero
90+
// Buckets must be provided with their indices in ascending order.
91+
// For the negative range, higher bucket indices correspond to bucket boundaries closer to -INF
92+
// and smaller bucket indices correspond to bucket boundaries closer to zero.
93+
// therefore we have to iterate the negative values in the sorted rawValueBuffer reverse order,
94+
// from the value closest to -INF to the value closest to zero.
95+
// not that i here is the index of the value in the rawValueBuffer array
96+
// and is unrelated to the histogram bucket index for the value.
9297
for (int i = negativeValuesCount - 1; i >= 0; i--) {
9398
long count = 1;
9499
long index = computeIndex(rawValueBuffer[i], scale);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private void doMerge(ExponentialHistogram b) {
8888
CopyableBucketIterator negBucketsB = b.negativeBuckets().iterator();
8989

9090
ZeroBucket zeroBucket = a.zeroBucket().merge(b.zeroBucket());
91-
zeroBucket = zeroBucket.collapseOverlappingBuckets(posBucketsA, negBucketsA, posBucketsB, negBucketsB);
91+
zeroBucket = zeroBucket.collapseOverlappingBucketsForAll(posBucketsA, negBucketsA, posBucketsB, negBucketsB);
9292

9393
buffer.setZeroBucket(zeroBucket);
9494

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

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,17 @@ public static double getQuantile(ExponentialHistogram histo, double quantile) {
4949

5050
ValueAndPreviousValue values = getElementAtRank(histo, upperRank);
5151

52+
double result;
5253
if (lowerRank == upperRank) {
53-
return values.valueAtRank();
54+
result = values.valueAtRank();
5455
} else {
55-
return values.valueAtPreviousRank() * (1 - upperFactor) + values.valueAtRank() * upperFactor;
56+
result = values.valueAtPreviousRank() * (1 - upperFactor) + values.valueAtRank() * upperFactor;
5657
}
58+
return removeNegativeZero(result);
5759
}
5860

59-
/**
60-
* @param valueAtPreviousRank the value at the rank before the desired rank, NaN if not applicable.
61-
* @param valueAtRank the value at the desired rank
62-
*/
63-
private record ValueAndPreviousValue(double valueAtPreviousRank, double valueAtRank) {
64-
ValueAndPreviousValue negateAndSwap() {
65-
return new ValueAndPreviousValue(-valueAtRank, -valueAtPreviousRank);
66-
}
61+
private static double removeNegativeZero(double result) {
62+
return result == 0.0 ? 0.0 : result;
6763
}
6864

6965
private static ValueAndPreviousValue getElementAtRank(ExponentialHistogram histo, long rank) {
@@ -144,4 +140,14 @@ private static ValueAndPreviousValue getBucketMidpointForRank(BucketIterator buc
144140
}
145141
throw new IllegalStateException("The total number of elements in the buckets is less than the desired rank.");
146142
}
143+
144+
/**
145+
* @param valueAtPreviousRank the value at the rank before the desired rank, NaN if not applicable.
146+
* @param valueAtRank the value at the desired rank
147+
*/
148+
private record ValueAndPreviousValue(double valueAtPreviousRank, double valueAtRank) {
149+
ValueAndPreviousValue negateAndSwap() {
150+
return new ValueAndPreviousValue(-valueAtRank, -valueAtPreviousRank);
151+
}
152+
}
147153
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ void setZeroBucket(ZeroBucket zeroBucket) {
8787
* <br>
8888
* Callers must adhere to the following rules:
8989
* <ul>
90-
* <li>All buckets from the negative range must be provided before the first one from the positive range.</li>
91-
* <li>For both the negative and positive ranges, buckets must be provided in ascending index order.</li>
90+
* <li>All buckets for the negative values range must be provided before the first one from the positive values range.</li>
91+
* <li>For both the negative and positive ranges, buckets must be provided with their indices in ascending order.</li>
9292
* <li>It is not allowed to provide the same bucket more than once.</li>
9393
* <li>It is not allowed to add empty buckets ({@code count <= 0}).</li>
9494
* </ul>
@@ -105,7 +105,7 @@ void setZeroBucket(ZeroBucket zeroBucket) {
105105
boolean tryAddBucket(long index, long count, boolean isPositive) {
106106
assert index >= MIN_INDEX && index <= MAX_INDEX : "index must be in range [" + MIN_INDEX + ".." + MAX_INDEX + "]";
107107
assert isPositive || positiveBuckets.numBuckets == 0 : "Cannot add negative buckets after a positive bucket has been added";
108-
assert count > 0 : "Cannot add an empty or negative bucket";
108+
assert count > 0 : "Cannot add a bucket with empty or negative count";
109109
if (isPositive) {
110110
return positiveBuckets.tryAddBucket(index, count);
111111
} else {
@@ -144,7 +144,7 @@ private class Buckets implements ExponentialHistogram.Buckets {
144144
}
145145

146146
/**
147-
* @return the array index of the first bucket of this set of buckets within {@link #bucketCounts} and {@link #bucketIndices}.
147+
* @return the position of the first bucket of this set of buckets within {@link #bucketCounts} and {@link #bucketIndices}.
148148
*/
149149
int startSlot() {
150150
return isPositive ? negativeBuckets.numBuckets : 0;
@@ -158,6 +158,8 @@ final void reset() {
158158

159159
boolean tryAddBucket(long index, long count) {
160160
int slot = startSlot() + numBuckets;
161+
assert numBuckets == 0 || bucketIndices[slot - 1] < index
162+
: "Histogram buckets must be added with their indices in ascending order";
161163
if (slot >= bucketCounts.length) {
162164
return false; // no more space
163165
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
/**
2020
* Represents the bucket for values around zero in an exponential histogram.
2121
* The range of this bucket is {@code [-zeroThreshold, +zeroThreshold]}.
22+
* To allow efficient comparison with bucket boundaries, this class internally
23+
* represents the zero threshold as a exponential histogram bucket index with a scale,
24+
* computed via {@link ExponentialScaleUtils#computeIndex(double, int)}.
2225
*
2326
* @param index The index used with the scale to determine the zero threshold.
2427
* @param scale The scale used with the index to determine the zero threshold.
@@ -63,7 +66,8 @@ public static ZeroBucket minimalWithCount(long count) {
6366
/**
6467
* Merges this zero bucket with another one.
6568
* <ul>
66-
* <li>If the other zero bucket is empty, this instance is returned unchanged.</li>
69+
* <li>If the other zero bucket or both are empty, this instance is returned unchanged.</li>
70+
* <li>If the this zero bucket is empty and the other one is populated, the other instance is returned unchanged.</li>
6771
* <li>Otherwise, the zero threshold is increased if necessary (by taking the maximum of the two), and the counts are summed.</li>
6872
* </ul>
6973
*
@@ -73,6 +77,8 @@ public static ZeroBucket minimalWithCount(long count) {
7377
public ZeroBucket merge(ZeroBucket other) {
7478
if (other.count == 0) {
7579
return this;
80+
} else if (count == 0) {
81+
return other;
7682
} else {
7783
long totalCount = count + other.count;
7884
// Both are populated, so we need to use the higher zero-threshold.
@@ -91,7 +97,7 @@ public ZeroBucket merge(ZeroBucket other) {
9197
* @param bucketIterators The iterators whose buckets may be collapsed.
9298
* @return A potentially updated {@link ZeroBucket} with the collapsed buckets' counts and an adjusted threshold.
9399
*/
94-
public ZeroBucket collapseOverlappingBuckets(BucketIterator... bucketIterators) {
100+
public ZeroBucket collapseOverlappingBucketsForAll(BucketIterator... bucketIterators) {
95101
ZeroBucket current = this;
96102
ZeroBucket previous;
97103
do {

libs/exponential-histogram/src/test/java/org/elasticsearch/exponentialhistogram/QuantileAccuracyTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.stream.IntStream;
2828

2929
import static org.elasticsearch.exponentialhistogram.ExponentialHistogram.MAX_SCALE;
30+
import static org.elasticsearch.exponentialhistogram.ExponentialHistogram.MIN_INDEX;
3031
import static org.elasticsearch.exponentialhistogram.ExponentialScaleUtils.computeIndex;
3132
import static org.hamcrest.Matchers.closeTo;
3233
import static org.hamcrest.Matchers.equalTo;
@@ -43,6 +44,15 @@ private static int randomBucketCount() {
4344
return (int) Math.round(5 + Math.pow(1995, randomDouble()));
4445
}
4546

47+
public void testNoNegativeZeroReturned() {
48+
FixedCapacityExponentialHistogram histogram = new FixedCapacityExponentialHistogram(2);
49+
histogram.resetBuckets(MAX_SCALE);
50+
// add a single, negative bucket close to zero
51+
histogram.tryAddBucket(MIN_INDEX, 3, false);
52+
double median = ExponentialHistogramQuantile.getQuantile(histogram, 0.5);
53+
assertThat(median, equalTo(0.0));
54+
}
55+
4656
public void testUniformDistribution() {
4757
testDistributionQuantileAccuracy(new UniformRealDistribution(new Well19937c(randomInt()), 0, 100));
4858
}

0 commit comments

Comments
 (0)