Skip to content

t-digest: allocate storage on first use and grow it; add ensureCapacity() - #801

Open
leerho wants to merge 1 commit into
mainfrom
tdigest-lazy-allocation
Open

leerho wants to merge 1 commit into
mainfrom
tdigest-lazy-allocation

Conversation

@leerho

@leerho leerho commented Nov 12, 2026

Copy link
Copy Markdown
Member

This applies the lazy allocation convention agreed on dev@ (vote, proposal) to TDigestDouble. It is the Java counterpart of apache/datasketches-cpp#561. The KLL part is #800; REQ will follow.

Behavior

Before this change, a new TDigestDouble allocated all of its storage in the constructor: centroid arrays for 2k + 10 centroids and a buffer four times that size (about 10 KB at k = 100).

Now storage is allocated on first use and grows as values arrive, following the C++ change:

  • Buffer: allocated on the first update. It grows geometrically (8, 16, 32, …) up to its full size.
  • Centroid storage: allocated at the first compression or merge. It grows as needed, up to its full size.
  • Small t-Digests: one that stays small never allocates the full storage.
  • Heapified t-Digests: sized to the image.
  • ensureCapacity(): a new public method that allocates the full storage up front. It does nothing for storage already at full size, and it doesn't change the contents.

Compression happens at exactly the same points as before, so results and the serialized form don't change. The generated cross-language files are byte-identical to those from main.

The class javadoc now says that an OutOfMemoryError from growing the storage comes from update() or merge(), not the constructor, and that the t-Digest is then unchanged. toString() shows both the full and the allocated capacities.

Fix: merge order

merge() used to widen min and max before allocating its work arrays. An OutOfMemoryError there would leave a stale min or max behind, for example in an empty t-Digest. It now allocates everything first. The C++ tests found the same problem in apache/datasketches-cpp#561.

Tests

The new TDigestLazyAllocationTest checks:

  • An empty t-Digest holds no storage, including after:
    • ignored NaN and infinite updates;
    • merging an empty t-Digest into it;
    • serialization and heapifying its image.
  • The buffer grows 8 → 16 → … → full, and the centroid storage is allocated only when compressing.
  • A small t-Digest never allocates the full storage.
  • After ensureCapacity(), the storage never grows through 100,000 updates, and the image is byte-identical to one built without it.
  • Calling ensureCapacity() after updates keeps the contents, buffered values included, and later results match.
  • Merging into a new t-Digest gives the same image as merging into one with ensureCapacity(), and doesn't allocate a buffer.
  • A heapified t-Digest grows from its image size and gives the same results.

The full suite passes (2,411 tests), and so do the t-digest cross-language checks against the local C++, Go and Rust files. javac -Xlint:all -Xdoclint:all,-missing reports nothing in the t-digest package.

🤖 Generated with Claude Code

…ty()

A new TDigestDouble no longer allocates its centroid and buffer storage
in the constructor. The buffer is allocated on the first update and
grows geometrically (8, 16, ...) up to its full size; the centroid
storage is allocated at the first compression or merge and grows as
needed up to its full size. A heapified t-Digest is sized to its image.
Compression happens at the same points as before, so results and the
serialized form are unchanged. ensureCapacity() allocates the full
storage up front.

merge() now allocates everything before it widens min and max, so an
OutOfMemoryError leaves the t-Digest unchanged.

This follows the lazy allocation convention agreed on dev@.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant