perf(encoding): compact mini-block chunk index for cached page state#7651
Open
Ali2Arslan wants to merge 4 commits into
Open
perf(encoding): compact mini-block chunk index for cached page state#7651Ali2Arslan wants to merge 4 commits into
Ali2Arslan wants to merge 4 commits into
Conversation
The mini-block scheduler cached each page as a `Vec<ChunkMeta>` plus a repetition index of per-chunk blocks (~48 bytes/chunk), most of which is derivable from a few cumulative quantities. Replace both with a compact `MiniBlockChunkIndex` that stores only the non-redundant data and derives the rest: prefix-sum byte offsets (narrowed to `u32` when the page fits) and a row mapping chosen per page shape -- `UniformFlat` (arithmetic, no allocation) for fixed-width/bitpacked pages, `Flat` for non-uniform flat pages (RLE/FSST), and `Nested` (row prefix sums + a trailer bitmap) when a repetition index is present. Row->chunk lookups stay O(1)/O(log n) and the scheduler's multi-range merge now runs in place. The `DeepSizeOf` impl accounts for every retained buffer so cache weighing reflects the smaller footprint. Adds unit tests for page-shape detection, byte-range/row/item axes, uniform-vs-flat scheduler parity, and per-variant size bounds. Co-authored-by: Cursor <cursoragent@cursor.com>
Trim the docs and comments added in the previous commit to the project convention: explain why not what, drop pure narration, and keep each to 2-3 lines. Comment/doc only; no behavior change. Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Ali2Arslan
commented
Jul 7, 2026
| impl DeepSizeOf for MiniBlockCacheableState { | ||
| fn deep_size_of_children(&self, context: &mut Context) -> usize { | ||
| self.rep_index.deep_size_of_children(context) | ||
| self.chunk_index.deep_size_of_children(context) |
Contributor
Author
There was a problem hiding this comment.
this also fixes the under-reporting (wasn't accounting for chunk_meta before)
Ali2Arslan
commented
Jul 7, 2026
| // are _adjacent_ (i.e. don't merge "take first row of chunk 0" and "take third row of chunk 0" into "take 2 | ||
| // rows of chunk 0 starting at 0") | ||
| if user_ranges.len() > 1 { | ||
| // TODO: Could probably optimize this allocation away |
Contributor
Author
There was a problem hiding this comment.
bit of a drive-by, but addressed this as well
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The mini-block structural scheduler caches per-page state that is consulted on
every read. Today that state is a
Vec<ChunkMeta>(num_values, size, offset perchunk) plus a repetition index of per-chunk blocks (first_row,
starts_including_trailer, has_preamble, has_trailer) — ~48 bytes/chunk, almost
all of it derivable from a handful of cumulative quantities.
This PR replaces both with a compact
MiniBlockChunkIndex(newprimitive/chunk_index.rssubmodule) that stores only the non-redundant dataand derives the rest:
byte_starts: PrefixSums— cumulative chunk byte sizes, stored asu32when the page's data buffer fits in 4 GiB and
u64otherwise.rows: RowMappingselected by page shape:UniformFlat— fixed-width / bitpacked pages where every non-last chunkholds the same number of values; row↔chunk is pure arithmetic with no
heap allocation.
Flat— non-uniform flat pages (RLE / FSST); a single cumulative valuearray.
Nested— repetition present; cumulative row starts + ahas_trailerbitmap (preamble derived from the previous chunk's trailer) + leaf item
counts.
byte_range/items_in_chunk/find_chunk/first_row/rows_in_chunk/has_preamble/has_trailerpreserve the exact semantics the scheduler reliedon (
find_chunkstill returns the first of duplicated start rows). Row→chunklookups remain O(1) (uniform flat) or O(log n) (binary search), and the
multi-range instruction merge in
schedule_instructionsnow runs in placeinstead of allocating a second
Vec.MiniBlockChunkIndeximplementsDeepSizeOf(composing the innerPrefixSums/ItemCounts/RowMapping), so cache weighing reflects the smallerfootprint.
This is an idiomatic port of an internal change to current upstream: it uses
lance's
DeepSizeOftrait (rather than a bespoke size method), threads theexisting
initializeIO structure, and drops fork-only test scaffolding thathas no counterpart here.
Test plan
cargo test -p lance-encoding --lib— 422 passed, 0 failed, 5 ignored(covers flat, non-uniform-flat, and nested/list miniblock round trips,
e.g.
test_sparse_large_string_list::...MINIBLOCK,test_sparse_boolean_list_uses_miniblock,test_nested_strings).test_flat_detectioncases),nested row/item axes (
test_nested_detection_and_axes), uniform-vs-Flatscheduler/lookup parity (
test_uniform_flat_matches_prefix_sum_flat),per-variant heap-size bounds vs the legacy layout
(
test_deep_size_per_variant_below_legacy), and aparse_nested_reporacle test against the previous per-block decode.
cargo fmt -p lance-encodingcargo clippy -p lance-encoding --tests --benches -- -D warningsMade with Cursor