Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,17 @@ All significant changes to this project will be documented in this file.
### New features

* Add KLL sketches behind the `kll` feature, with rank, quantile, PMF, and CDF queries, merging, totally ordered custom item types, a `KllFloat` adapter for non-NaN floating-point values, and serialization.
* Implement `FromIterator<TDigestMut>` for batch construction, and add `TDigestMut::quantiles` and `TDigest::quantiles` for querying several ranks in one centroid scan.

### Improvements

* Improve truncated-input diagnostics across sketch deserializers.
* T-Digest batch construction from owned partial sketches avoids recompressing intermediate results, and batch quantile queries reuse one traversal for ranks supplied in nondecreasing order.

### Bug fixes

* T-Digest deserialization now rejects unknown or conflicting flags, reversed extrema, out-of-range values, unsorted centroids, and non-empty images without stored values.
* T-Digest merging now uses the smaller `k` when sketches have different compression parameters, preserving the size bound of the coarser input.

## v0.5.0

Expand Down
36 changes: 34 additions & 2 deletions benchmarks/tdigest/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fn small_partials(bencher: Bencher) {
let partials = partial_digests(64, SMALL_ROWS_PER_PARTIAL)
.into_iter()
.map(|mut digest| {
black_box(digest.rank(0.0));
black_box(digest.quantile(0.5));
digest
})
.collect::<Vec<_>>();
Expand All @@ -86,7 +86,7 @@ fn partials(bencher: Bencher) {
let partials = partial_digests_with(DEFAULT_DIGEST_K, 64, ROWS_PER_PARTIAL)
.into_iter()
.map(|mut digest| {
black_box(digest.rank(0.0));
black_box(digest.quantile(0.5));
digest
})
.collect::<Vec<_>>();
Expand All @@ -102,6 +102,38 @@ fn partials(bencher: Bencher) {
});
}

#[divan::bench]
fn partials_from_iter(bencher: Bencher) {
let partials = partial_digests_with(DEFAULT_DIGEST_K, 64, ROWS_PER_PARTIAL)
.into_iter()
.map(|mut digest| {
black_box(digest.quantile(0.5));
digest
})
.collect::<Vec<_>>();

bencher
.counter(ItemsCount::new(64 * ROWS_PER_PARTIAL))
// Construct fresh owned inputs outside the measurement, as a real caller transfers
// ownership rather than cloning solely for `FromIterator`.
.with_inputs(|| partials.clone())
.bench_local_values(|partials| black_box(partials).into_iter().collect::<TDigestMut>());
}

#[divan::bench]
fn uncompressed_partials_from_iter(bencher: Bencher) {
let values = values(64 * ROWS_PER_PARTIAL);
let partials = values
.chunks_exact(ROWS_PER_PARTIAL)
.map(build_mut_digest)
.collect::<Vec<_>>();

bencher
.counter(ItemsCount::new(values.len()))
.with_inputs(|| partials.clone())
.bench_local_values(|partials| black_box(partials).into_iter().collect::<TDigestMut>());
}

#[divan::bench(args = [SMALL_ROWS_PER_PARTIAL, ROWS_PER_PARTIAL])]
fn serialized_partials(bencher: Bencher, rows_per_partial: usize) {
let partials = serialized_partial_digests(64, rows_per_partial);
Expand Down
21 changes: 21 additions & 0 deletions benchmarks/tdigest/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,24 @@ fn quantiles_2_sequential(bencher: Bencher) {
]
});
}

#[divan::bench]
fn quantiles_6_sequential(bencher: Bencher) {
let digest = prepared_digest();
let ranks = [0.25, 0.5, 0.75, 0.9, 0.95, 0.99];

bencher
.bench_local(|| black_box(ranks).map(|rank| black_box(&digest).quantile(black_box(rank))));
}

#[divan::bench(args = [2, 6, 100])]
fn quantiles_batch(bencher: Bencher, num_ranks: usize) {
let digest = prepared_digest();
let ranks = (1..=num_ranks)
.map(|rank| rank as f64 / (num_ranks + 1) as f64)
.collect::<Vec<_>>();

bencher
.counter(ItemsCount::new(num_ranks))
.bench_local(|| black_box(&digest).quantiles(black_box(&ranks)));
}
Loading