Skip to content

Zstd: append values in bulk and harden the frame metadata reads - #9099

Open
robert3005 wants to merge 1 commit into
rk/dict-gather-into-builderfrom
rk/zstd-append-to-builder
Open

Zstd: append values in bulk and harden the frame metadata reads#9099
robert3005 wants to merge 1 commit into
rk/dict-gather-into-builderfrom
rk/zstd-append-to-builder

Conversation

@robert3005

@robert3005 robert3005 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Write zstd values directly into the builder without additional copy

Comment thread encodings/zstd/src/array.rs Outdated
row = valid_index + 1;
// Null rows carry an empty view, so scatter the stored values into their rows.
let mut views = BufferMut::<BinaryView>::zeroed(slice.n_rows);
for (view, index) in valid_views.into_iter().zip_eq(valid_indices) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should use for_each_valid_index if possible

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The scatter now matches on mask.bit_buffer() and only the AllOr::Some arm walks bits, via for_each_set_index with a running valid-row counter. That drops the mask.indices() materialization — the views were its only consumer — and the zip_eq with it. The all-false case is an early mask.all_false() return instead of an indices() match arm.

Comment thread encodings/zstd/src/array.rs Outdated
Comment on lines 293 to 298

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this shouldn't be necessary

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed. Right — after match_each_varbin_builder! (all four widths) and VarBinViewBuilder, there is nothing left for a Utf8/Binary dtype, and canonicalizing would only decompress to a VarBinView for VarBinView::append_to_builder to reject the same remainder. It is now a vortex_bail!, matching what VarBinView already does, with a comment saying why there is no fallback.

Applied the same removal to FSST (#9097) and OnPair (#9100), which had the identical block. Dict keeps its fallback since its dtype is not restricted to varbin.

@robert3005 robert3005 added the changelog/chore A trivial change label Jul 31, 2026
@robert3005
robert3005 marked this pull request as ready for review July 31, 2026 13:53
`append_to_builder` walked the decompressed frames one value at a time,
appending each through a single-value call and interleaving null runs by hand.
It now derives the value byte range from the slice's own metadata and hands the
whole region to `append_value_slices`, which sizes the offsets, byte storage and
validity once — one offset store plus one `memcpy` per value.

Adds a `VarBinViewBuilder` path as well: the frames already hold the values
contiguously, so the views can reference them in place instead of going through
the canonical array and rewriting every view a second time to rebase its buffer
index. `reconstruct_views` therefore takes the buffer index the views should
start at.

Frame metadata comes straight off disk, so the arithmetic it drives is now
checked and surfaced as errors rather than panics or wrapped lengths: value
counts, frame sizes, length prefixes and the offsets they walk to. The missing
`n_values` fallback no longer reads a byte count as a value count for
variable-width values, where that mis-attributes values to frames — it is
accepted only for the single-frame case that is still recoverable. Decompression
also writes through `WriteBuf` into uninitialized spare capacity rather than a
`&mut [u8]` over memory nothing has written yet.

Signed-off-by: Robert Kruszewski <robert@spiraldb.com>
@robert3005
robert3005 force-pushed the rk/zstd-append-to-builder branch from 31c8739 to 1c85ef5 Compare July 31, 2026 18:11
@codspeed-hq

codspeed-hq Bot commented Jul 31, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 13.18%

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

⚡ 1 improved benchmark
✅ 1884 untouched benchmarks
⏩ 12 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation take_map[(0.1, 0.5)] 1,109.4 µs 980.2 µs +13.18%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing rk/zstd-append-to-builder (1c85ef5) with rk/dict-gather-into-builder (c82aa58)

Open in CodSpeed

Footnotes

  1. 12 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

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

Labels

changelog/chore A trivial change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant