Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make TDigestState configurable #96794

Merged
merged 166 commits into from Jun 18, 2023
Merged
Show file tree
Hide file tree
Changes from 159 commits
Commits
Show all changes
166 commits
Select commit Hold shift + click to select a range
3c6c424
Initial import for TDigest forking.
kkrik-es May 9, 2023
d719767
Fix MedianTest.
kkrik-es May 10, 2023
e816b57
Fix Dist.
kkrik-es May 11, 2023
0079943
Fix AVLTreeDigest.quantile to match Dist for uniform centroids.
kkrik-es May 11, 2023
0a18e70
Update docs/changelog/96086.yaml
kkrik-es May 15, 2023
96ea462
Fix `MergingDigest.quantile` to match `Dist` on uniform distribution.
kkrik-es May 12, 2023
56350ee
Add merging to TDigestState.hashCode and .equals.
kkrik-es May 16, 2023
d731aee
Fix style violations for tdigest library.
kkrik-es May 16, 2023
18c1cd5
Fix typo.
kkrik-es May 16, 2023
f96b4d2
Fix more style violations.
kkrik-es May 16, 2023
6d0201b
Fix more style violations.
kkrik-es May 16, 2023
9d48856
Fix remaining style violations in tdigest library.
kkrik-es May 16, 2023
68e489f
Update results in docs based on the forked tdigest.
kkrik-es May 17, 2023
2b3b463
Fix YAML tests in aggs module.
kkrik-es May 18, 2023
0d6ea1e
Fix YAML tests in x-pack/plugin.
kkrik-es May 18, 2023
98c1146
Skip failing V7 compat tests in modules/aggregations.
kkrik-es May 18, 2023
d49ed67
Fix TDigest library unittests.
kkrik-es May 22, 2023
13f17e6
Remove YAML test versions for older releases.
kkrik-es May 22, 2023
5d9ae8e
Fix test failures in docs and mixed cluster.
kkrik-es May 22, 2023
1ad14f8
Reduce buffer sizes in MergingDigest to avoid oom.
kkrik-es May 22, 2023
af93c26
Exclude more failing V7 compatibility tests.
kkrik-es May 23, 2023
d82ec3e
Update results for JdbcCsvSpecIT tests.
kkrik-es May 23, 2023
c9ab354
Update results for JdbcDocCsvSpecIT tests.
kkrik-es May 23, 2023
c19de44
Revert unrelated change.
kkrik-es May 23, 2023
9753468
More test fixes.
kkrik-es May 23, 2023
a23ae32
Use version skips instead of blacklisting in mixed cluster tests.
kkrik-es May 23, 2023
054cd90
Switch TDigestState back to AVLTreeDigest.
kkrik-es May 23, 2023
2ccefde
Update docs and tests with AVLTreeDigest output.
kkrik-es May 23, 2023
3801358
Update flaky test.
kkrik-es May 23, 2023
52ae1ff
Remove dead code, esp around tracking of incoming data.
kkrik-es May 24, 2023
6c05469
Update docs/changelog/96086.yaml
kkrik-es May 24, 2023
082ac3c
Delete docs/changelog/96086.yaml
kkrik-es May 24, 2023
5352c96
Remove explicit compression calls.
kkrik-es May 24, 2023
90b21bc
Merge remote-tracking branch 'upstream/fix/95903' into fix/95903
kkrik-es May 24, 2023
0003908
Revert "Remove explicit compression calls."
kkrik-es May 24, 2023
91fd594
Remove explicit compression calls to MedianAbsoluteDeviation input.
kkrik-es May 24, 2023
4ff173a
Add unittests for AVL and merging digest accuracy.
kkrik-es May 25, 2023
281bfc7
Fix spotless violations.
kkrik-es May 25, 2023
e382efa
Delete redundant tests and benchmarks.
kkrik-es May 29, 2023
60ec86f
Fix spotless violation.
kkrik-es May 29, 2023
fc512a7
Use the old implementation of AVLTreeDigest.
kkrik-es May 29, 2023
8bd54c8
Merge branch 'main' into fix/95903
kkrik-es May 29, 2023
e53bcc8
Update docs with latest percentile results.
kkrik-es May 29, 2023
6b29dc7
Update docs with latest percentile results.
kkrik-es May 29, 2023
b66664c
Merge branch 'main' into fix/95903
kkrik-es May 29, 2023
fe3facd
Remove repeated compression calls.
kkrik-es May 29, 2023
7389bdc
Update more percentile results.
kkrik-es May 29, 2023
0ff89ae
Use approximate percentile values in integration tests.
kkrik-es May 30, 2023
497202b
Fix expected percentile value in test.
kkrik-es May 30, 2023
093abb7
Revert in-place node updates in AVL tree.
kkrik-es Jun 1, 2023
b0c7870
Add SortingDigest and HybridDigest.
kkrik-es Jun 2, 2023
40f1861
Remove deps to the 3.2 library.
kkrik-es Jun 2, 2023
248560d
Remove unused licenses for tdigest.
kkrik-es Jun 2, 2023
2336b11
Revert changes for SortingDigest and HybridDigest.
kkrik-es Jun 6, 2023
ec87dea
Remove unused Histogram classes and unit tests.
kkrik-es Jun 6, 2023
5e06d79
Remove Comparison class, not used.
kkrik-es Jun 6, 2023
3e12dd4
Merge branch 'main' into fix/95903
kkrik-es Jun 6, 2023
7bfb729
Revert "Revert changes for SortingDigest and HybridDigest."
kkrik-es Jun 6, 2023
0842b27
Use HybridDigest as default tdigest implementation
kkrik-es Jun 8, 2023
063ef27
Small fixes.
kkrik-es Jun 8, 2023
c028d9c
Merge branch 'main' into fix/95903
kkrik-es Jun 8, 2023
cb171ab
Add javadoc and tests.
kkrik-es Jun 8, 2023
dc739af
Add javadoc and tests.
kkrik-es Jun 9, 2023
17b03cf
Remove special logic for singletons in the boundaries.
kkrik-es Jun 10, 2023
079a8c3
Revert changes to expected values in tests.
kkrik-es Jun 10, 2023
47414c7
Revert changes to expected values in tests.
kkrik-es Jun 10, 2023
b8f7c00
Merge branch 'main' into fix/95903
kkrik-es Jun 10, 2023
7718dbb
Tentatively restore percentile rank expected results.
kkrik-es Jun 10, 2023
4df40da
Use cdf version from 3.2
kkrik-es Jun 12, 2023
77c13eb
Revert "Tentatively restore percentile rank expected results."
kkrik-es Jun 12, 2023
4724905
Revert remaining changes compared to main.
kkrik-es Jun 12, 2023
64950c4
Revert excluded V7 compat tests.
kkrik-es Jun 12, 2023
7251150
Exclude V7 compat tests still failing.
kkrik-es Jun 12, 2023
4a78005
Exclude V7 compat tests still failing.
kkrik-es Jun 12, 2023
a847290
Remove ClusterSettings tentatively.
kkrik-es Jun 12, 2023
53f6c66
Initial import for TDigest forking.
kkrik-es May 9, 2023
317a4ff
Fix MedianTest.
kkrik-es May 10, 2023
a7b0b37
Fix Dist.
kkrik-es May 11, 2023
7b4ae66
Fix AVLTreeDigest.quantile to match Dist for uniform centroids.
kkrik-es May 11, 2023
f7321c1
Update docs/changelog/96086.yaml
kkrik-es May 15, 2023
1515ab1
Fix `MergingDigest.quantile` to match `Dist` on uniform distribution.
kkrik-es May 12, 2023
e1afb49
Add merging to TDigestState.hashCode and .equals.
kkrik-es May 16, 2023
e7ca078
Fix style violations for tdigest library.
kkrik-es May 16, 2023
0a8184f
Fix typo.
kkrik-es May 16, 2023
766dcb0
Fix more style violations.
kkrik-es May 16, 2023
6547bef
Fix more style violations.
kkrik-es May 16, 2023
d3b188d
Fix remaining style violations in tdigest library.
kkrik-es May 16, 2023
33d6049
Update results in docs based on the forked tdigest.
kkrik-es May 17, 2023
2dd1b18
Fix YAML tests in aggs module.
kkrik-es May 18, 2023
9da3c8d
Fix YAML tests in x-pack/plugin.
kkrik-es May 18, 2023
b0dbbff
Skip failing V7 compat tests in modules/aggregations.
kkrik-es May 18, 2023
e725a22
Fix TDigest library unittests.
kkrik-es May 22, 2023
4a02706
Remove YAML test versions for older releases.
kkrik-es May 22, 2023
8edf49e
Fix test failures in docs and mixed cluster.
kkrik-es May 22, 2023
2944856
Reduce buffer sizes in MergingDigest to avoid oom.
kkrik-es May 22, 2023
e68f692
Exclude more failing V7 compatibility tests.
kkrik-es May 23, 2023
c2452e9
Update results for JdbcCsvSpecIT tests.
kkrik-es May 23, 2023
b6b0721
Update results for JdbcDocCsvSpecIT tests.
kkrik-es May 23, 2023
6a49a6b
Revert unrelated change.
kkrik-es May 23, 2023
989c3f2
More test fixes.
kkrik-es May 23, 2023
a61a48c
Use version skips instead of blacklisting in mixed cluster tests.
kkrik-es May 23, 2023
0175ff8
Switch TDigestState back to AVLTreeDigest.
kkrik-es May 23, 2023
6babd70
Update docs and tests with AVLTreeDigest output.
kkrik-es May 23, 2023
86cde3c
Update flaky test.
kkrik-es May 23, 2023
32c51ea
Remove dead code, esp around tracking of incoming data.
kkrik-es May 24, 2023
3904a3b
Remove explicit compression calls.
kkrik-es May 24, 2023
73872a5
Update docs/changelog/96086.yaml
kkrik-es May 24, 2023
37499e6
Delete docs/changelog/96086.yaml
kkrik-es May 24, 2023
5dda5bd
Revert "Remove explicit compression calls."
kkrik-es May 24, 2023
0520197
Remove explicit compression calls to MedianAbsoluteDeviation input.
kkrik-es May 24, 2023
d9a4ce5
Add unittests for AVL and merging digest accuracy.
kkrik-es May 25, 2023
cb6564f
Fix spotless violations.
kkrik-es May 25, 2023
3ce5ec2
Delete redundant tests and benchmarks.
kkrik-es May 29, 2023
afc812a
Fix spotless violation.
kkrik-es May 29, 2023
e17d30c
Use the old implementation of AVLTreeDigest.
kkrik-es May 29, 2023
8ce1ac4
Update docs with latest percentile results.
kkrik-es May 29, 2023
44f0eb3
Update docs with latest percentile results.
kkrik-es May 29, 2023
139eaf6
Remove repeated compression calls.
kkrik-es May 29, 2023
0f3e019
Update more percentile results.
kkrik-es May 29, 2023
9222e81
Use approximate percentile values in integration tests.
kkrik-es May 30, 2023
5b62172
Fix expected percentile value in test.
kkrik-es May 30, 2023
244c8c0
Revert in-place node updates in AVL tree.
kkrik-es Jun 1, 2023
75ed609
Add SortingDigest and HybridDigest.
kkrik-es Jun 2, 2023
3e99d90
Remove deps to the 3.2 library.
kkrik-es Jun 2, 2023
d23e0f2
Remove unused licenses for tdigest.
kkrik-es Jun 2, 2023
0e41355
Revert changes for SortingDigest and HybridDigest.
kkrik-es Jun 6, 2023
6930a45
Remove unused Histogram classes and unit tests.
kkrik-es Jun 6, 2023
bb6f968
Remove Comparison class, not used.
kkrik-es Jun 6, 2023
b8e4c79
Revert "Revert changes for SortingDigest and HybridDigest."
kkrik-es Jun 6, 2023
bb2d7bb
Use HybridDigest as default tdigest implementation
kkrik-es Jun 8, 2023
23634d4
Add javadoc and tests.
kkrik-es Jun 8, 2023
c1c9f44
Remove ClusterSettings tentatively.
kkrik-es Jun 12, 2023
97ad6a9
Restore bySize function in TDigest and subclasses.
kkrik-es Jun 13, 2023
b13fccb
Merge branch 'main' into fix/95903
kkrik-es Jun 13, 2023
0831fbc
Merge branch 'fix/95903' into fix/95903-merging
kkrik-es Jun 13, 2023
46460fa
Merge remote-tracking branch 'upstream/fix/95903-merging' into fix/95…
kkrik-es Jun 13, 2023
4735914
Update Dist.cdf to match the rest.
kkrik-es Jun 13, 2023
e902ca3
Merge branch 'main' into fix/95903
kkrik-es Jun 13, 2023
c5be977
Merge branch 'fix/95903-merging' into fix/95903
kkrik-es Jun 13, 2023
5b0f784
Revert outdated test changes.
kkrik-es Jun 13, 2023
a6fdc93
Revert outdated changes.
kkrik-es Jun 13, 2023
519b558
Small fixes.
kkrik-es Jun 13, 2023
3a6cdf2
Update docs/changelog/96794.yaml
kkrik-es Jun 13, 2023
cca7b3c
Make HybridDigest the default implementation.
kkrik-es Jun 13, 2023
c7b80ac
Merge remote-tracking branch 'upstream/fix/95903' into fix/95903
kkrik-es Jun 13, 2023
e8e8043
Update boxplot documentation.
kkrik-es Jun 13, 2023
f058ef6
Restore AVLTreeDigest as the default in TDigestState.
kkrik-es Jun 13, 2023
a359fbd
Use execution_hint in tdigest spec.
kkrik-es Jun 14, 2023
dc76a88
Fix Dist.cdf for empty digest.
kkrik-es Jun 14, 2023
7287c19
Bump up TransportVersion.
kkrik-es Jun 14, 2023
c206032
Merge branch 'main' into fix/95903
kkrik-es Jun 14, 2023
e873fb8
Bump up TransportVersion for real.
kkrik-es Jun 14, 2023
2b38504
HybridDigest uses its final implementation during deserialization.
kkrik-es Jun 14, 2023
79c1372
Restore the right TransportVersion in TDigestState.read
kkrik-es Jun 14, 2023
5c96899
Merge branch 'main' into fix/95903
kkrik-es Jun 15, 2023
de29750
Use TDigestExecutionHint instead of strings.
kkrik-es Jun 15, 2023
c728714
Add link to TDigest javadoc.
kkrik-es Jun 15, 2023
0956241
Spotless fix.
kkrik-es Jun 15, 2023
4da7b48
Merge branch 'main' into fix/95903
kkrik-es Jun 15, 2023
5edb2a3
Small fixes.
kkrik-es Jun 15, 2023
425ce8a
Merge branch 'main' into fix/95903
kkrik-es Jun 15, 2023
fc11be2
Bump up TransportVersion.
kkrik-es Jun 15, 2023
88c080f
Merge branch 'main' into fix/95903
kkrik-es Jun 16, 2023
7929ea2
Bump up the TransportVersion, again.
kkrik-es Jun 16, 2023
ff825d2
Merge branch 'main' into fix/95903
kkrik-es Jun 18, 2023
59a0577
Merge branch 'main' into fix/95903
kkrik-es Jun 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/96794.yaml
@@ -0,0 +1,5 @@
pr: 96794
summary: Make TDigestState configurable
area: Aggregations
type: enhancement
issues: []
29 changes: 29 additions & 0 deletions docs/reference/aggregations/metrics/boxplot-aggregation.asciidoc
Expand Up @@ -166,6 +166,35 @@ GET latency/_search

include::percentile-aggregation.asciidoc[tags=t-digest]

==== Execution hint

The default implementation of TDigest is optimized for performance, scaling to millions or even
billions of sample values while maintaining acceptable accuracy levels (close to 1% relative error
for millions of samples in some cases). There's an option to use an implementation optimized
for accuracy by setting parameter `execution_hint` to value `high_accuracy`:

[source,console]
--------------------------------------------------
GET latency/_search
{
"size": 0,
"aggs": {
"load_time_boxplot": {
"boxplot": {
"field": "load_time",
"execution_hint": "high_accuracy" <1>
}
}
}
}
--------------------------------------------------
// TEST[setup:latency]

<1> Optimize TDigest for accuracy, at the expense of performance

This option can lead to improved accuracy (relative error close to 0.01% for millions of samples in some
cases) but then percentile queries take 2x-10x longer to complete.

==== Missing value

The `missing` parameter defines how documents that are missing a value should be treated.
Expand Down
Expand Up @@ -306,6 +306,38 @@ TDigest roughly 64KB in size. In practice data tends to be more random and
the TDigest will use less memory.
// end::t-digest[]

[[search-aggregations-metrics-percentile-aggregation-execution-hint]]
==== Execution hint

The default implementation of TDigest is optimized for performance, scaling to millions or even
billions of sample values while maintaining acceptable accuracy levels (close to 1% relative error
for millions of samples in some cases). There's an option to use an implementation optimized
for accuracy by setting parameter `execution_hint` to value `high_accuracy`:

[source,console]
--------------------------------------------------
GET latency/_search
{
"size": 0,
"aggs": {
"load_time_outlier": {
"percentiles": {
"field": "load_time",
"tdigest": {
"execution_hint": "high_accuracy" <1>
}
}
}
}
}
--------------------------------------------------
// TEST[setup:latency]

<1> Optimize TDigest for accuracy, at the expense of performance

This option can lead to improved accuracy (relative error close to 0.01% for millions of samples in some
cases) but then percentile queries take 2x-10x longer to complete.

==== HDR Histogram

https://github.com/HdrHistogram/HdrHistogram[HDR Histogram] (High Dynamic Range Histogram) is an alternative implementation
Expand Down
Expand Up @@ -10,9 +10,10 @@ extracted from specific numeric or <<histogram,histogram fields>> in the documen

[NOTE]
==================================================
Please see <<search-aggregations-metrics-percentile-aggregation-approximation>>
and <<search-aggregations-metrics-percentile-aggregation-compression>> for advice
regarding approximation and memory use of the percentile ranks aggregation
Please see <<search-aggregations-metrics-percentile-aggregation-approximation>>,
<<search-aggregations-metrics-percentile-aggregation-compression>> and
<<search-aggregations-metrics-percentile-aggregation-execution-hint>> for advice
regarding approximation, performance and memory use of the percentile ranks aggregation
==================================================

Percentile rank show the percentage of observed values which are below certain
Expand Down
72 changes: 55 additions & 17 deletions libs/tdigest/src/main/java/org/elasticsearch/tdigest/Dist.java
Expand Up @@ -30,30 +30,68 @@
public class Dist {

private static double cdf(final double x, final int length, Function<Integer, Double> elementGetter) {
if (length == 0) {
// no data to examine
return Double.NaN;
}
if (length == 1) {
double value = elementGetter.apply(0);
if (x < value) return 0;
if (x > value) return 1;
return 0.5;
}

if (Double.compare(x, elementGetter.apply(0)) < 0) {
return 0;
}

double n1 = 0.5;
int n2 = 0;
for (int i = 1; i < length; i++) {
double value = elementGetter.apply(i);
int compareResult = Double.compare(value, x);
if (compareResult > 0) {
if (Double.compare(n2, 0) > 0) {
return (n1 + 0.5 * n2) / length;
}
double previousValue = elementGetter.apply(i - 1);
double factor = (x - previousValue) / (value - previousValue);
return (n1 + factor) / length;
if (Double.compare(x, elementGetter.apply(0)) == 0) {
// we have one or more centroids == x, treat them as one
// dw will accumulate the weight of all of the centroids at x
double dw = 0;
for (int i = 0; i < length && Double.compare(elementGetter.apply(i), x) == 0; i++) {
dw += 1;
}
return dw / 2.0 / length;
}

if (x > elementGetter.apply(length - 1)) {
return 1;
}
if (x == elementGetter.apply(length - 1)) {
double dw = 0;
for (int i = length - 1; i >= 0 && Double.compare(elementGetter.apply(i), x) == 0; i--) {
dw += 1;
}
if (compareResult < 0) {
n1++;
} else {
n2++;
return (length - dw / 2.0) / length;
}

// initially, we set left width equal to right width
double left = (elementGetter.apply(1) - elementGetter.apply(0)) / 2;
double weightSoFar = 0;

for (int i = 0; i < length - 1; i++) {
double right = (elementGetter.apply(i + 1) - elementGetter.apply(i)) / 2;
if (x < elementGetter.apply(i) + right) {
double value = (weightSoFar + AbstractTDigest.interpolate(x, elementGetter.apply(i) - left, elementGetter.apply(i) + right))
/ length;
return Math.max(value, 0.0);
}
weightSoFar += 1;
left = right;
}

// for the last element, assume right width is same as left
int lastOffset = length - 1;
double right = (elementGetter.apply(lastOffset) - elementGetter.apply(lastOffset - 1)) / 2;
if (x < elementGetter.apply(lastOffset) + right) {
return (weightSoFar + AbstractTDigest.interpolate(
x,
elementGetter.apply(lastOffset) - right,
elementGetter.apply(lastOffset) + right
)) / length;
}
return (length - 0.5 * n2) / length;
return 1;
}

public static double cdf(final double x, double[] data) {
Expand Down
193 changes: 193 additions & 0 deletions libs/tdigest/src/main/java/org/elasticsearch/tdigest/HybridDigest.java
@@ -0,0 +1,193 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.elasticsearch.tdigest;

import java.util.Collection;
import java.util.List;

/**
* Uses a {@link SortingDigest} implementation under the covers for small sample populations, then switches to {@link MergingDigest}.
* The {@link SortingDigest} is perfectly accurate and the fastest implementation for up to millions of samples, at the cost of increased
* memory footprint as it tracks all samples. Conversely, the {@link MergingDigest} pre-allocates its memory (tens of KBs) and provides
* better performance for hundreds of millions of samples and more, while accuracy stays bounded to 0.1-1% for most cases.
*
* This hybrid approach provides the best of both worlds, i.e. speedy and accurate percentile calculations for small populations with
* bounded memory allocation and acceptable speed and accuracy for larger ones.
*/
public class HybridDigest extends AbstractTDigest {

// See MergingDigest's compression param.
private final double compression;

// Indicates the sample size over which it switches from SortingDigest to MergingDigest.
private final long maxSortingSize;

// This is set to null when the implementation switches to MergingDigest.
private SortingDigest sortingDigest = new SortingDigest();

// This gets initialized when the implementation switches to MergingDigest.
private MergingDigest mergingDigest;

/**
* Creates a hybrid digest that uses a {@link SortingDigest} for up to {@param maxSortingSize} samples,
* then switches to a {@link MergingDigest}.
*
* @param compression The compression factor for the MergingDigest
* @param maxSortingSize The sample size limit for switching from a {@link SortingDigest} to a {@link MergingDigest} implementation
*/
HybridDigest(double compression, long maxSortingSize) {
this.compression = compression;
this.maxSortingSize = maxSortingSize;
}

/**
* Similar to the constructor above. The limit for switching from a {@link SortingDigest} to a {@link MergingDigest} implementation
* is calculated based on the passed compression factor.
*
* @param compression The compression factor for the MergingDigest
*/
HybridDigest(double compression) {
// The default maxSortingSize is calculated so that the SortingDigest will have comparable size with the MergingDigest
// at the point where implementations switch, e.g. for default compression 100 SortingDigest allocates ~16kB and MergingDigest
// allocates ~15kB.
this(compression, Math.round(compression) * 20);
martijnvg marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public void add(double x, int w) {
reserve(w);
if (mergingDigest != null) {
mergingDigest.add(x, w);
} else {
sortingDigest.add(x, w);
}
}

@Override
public void reserve(long size) {
if (mergingDigest != null) {
mergingDigest.reserve(size);
return;
}
// Check if we need to switch implementations.
assert sortingDigest != null;
if (sortingDigest.size() + size >= maxSortingSize) {
mergingDigest = new MergingDigest(compression);
for (double value : sortingDigest.values) {
mergingDigest.add(value);
}
mergingDigest.reserve(size);
// Release the allocated SortingDigest.
sortingDigest = null;
} else {
sortingDigest.reserve(size);
}
}

@Override
public void add(List<? extends TDigest> others) {
if (mergingDigest != null) {
mergingDigest.add(others);
} else {
sortingDigest.add(others);
}
}

@Override
public void compress() {
if (mergingDigest != null) {
mergingDigest.compress();
} else {
sortingDigest.compress();
}
}

@Override
public long size() {
if (mergingDigest != null) {
return mergingDigest.size();
}
return sortingDigest.size();
}

@Override
public double cdf(double x) {
if (mergingDigest != null) {
return mergingDigest.cdf(x);
}
return sortingDigest.cdf(x);
}

@Override
public double quantile(double q) {
if (mergingDigest != null) {
return mergingDigest.quantile(q);
}
return sortingDigest.quantile(q);
}

@Override
public Collection<Centroid> centroids() {
if (mergingDigest != null) {
return mergingDigest.centroids();
}
return sortingDigest.centroids();
}

@Override
public double compression() {
if (mergingDigest != null) {
return mergingDigest.compression();
}
return sortingDigest.compression();
}

@Override
public int centroidCount() {
if (mergingDigest != null) {
return mergingDigest.centroidCount();
}
return sortingDigest.centroidCount();
}

@Override
public double getMin() {
if (mergingDigest != null) {
return mergingDigest.getMin();
}
return sortingDigest.getMin();
}

@Override
public double getMax() {
if (mergingDigest != null) {
return mergingDigest.getMax();
}
return sortingDigest.getMax();
}

@Override
public int byteSize() {
if (mergingDigest != null) {
return mergingDigest.byteSize();
}
return sortingDigest.byteSize();
}
}