Skip to content

perf(array): accumulate nested builder validity without a null buffer [builders-child-stack] - #8966

Closed
robert3005 wants to merge 2 commits into
claude/builders-canonical-children-9ze0t6from
claude/builders-lazy-validity-9ze0t6
Closed

perf(array): accumulate nested builder validity without a null buffer [builders-child-stack]#8966
robert3005 wants to merge 2 commits into
claude/builders-canonical-children-9ze0t6from
claude/builders-lazy-validity-9ze0t6

Conversation

@robert3005

@robert3005 robert3005 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Stacked on #8964 — review that first.

After the parent merges the arrays are lazy but validity is not. With this change the validity is lazy as well

@claude claude Bot changed the title perf(array): accumulate nested builder validity without a null buffer perf(array): accumulate nested builder validity without a null buffer [builders-child-stack] Jul 25, 2026
@robert3005
robert3005 force-pushed the claude/builders-min-chunk-len-9ze0t6 branch from cc36904 to 6f3ef13 Compare July 25, 2026 21:07
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch 2 times, most recently from 85f8dcd to b67473e Compare July 29, 2026 07:35
@robert3005
robert3005 force-pushed the claude/builders-min-chunk-len-9ze0t6 branch from 6f3ef13 to 20dd597 Compare July 29, 2026 07:35
@codspeed-hq

codspeed-hq Bot commented Jul 29, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 1842 untouched benchmarks
⏩ 55 skipped benchmarks1


Comparing claude/builders-lazy-validity-9ze0t6 (dd4420b) with claude/builders-canonical-children-9ze0t6 (c9bacae)

Open in CodSpeed

Footnotes

  1. 55 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.

@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from b67473e to 8561525 Compare July 29, 2026 13:48
@robert3005
robert3005 changed the base branch from claude/builders-min-chunk-len-9ze0t6 to claude/builders-canonical-children-9ze0t6 July 29, 2026 13:50
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from 8561525 to a8ebf30 Compare July 29, 2026 14:19
@robert3005
robert3005 force-pushed the claude/builders-canonical-children-9ze0t6 branch 2 times, most recently from df75205 to c30fa2a Compare July 31, 2026 13:35
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from a8ebf30 to 9f2a3d8 Compare July 31, 2026 13:35
@robert3005
robert3005 force-pushed the claude/builders-canonical-children-9ze0t6 branch from c30fa2a to e8186e0 Compare July 31, 2026 15:29
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from 9f2a3d8 to 7b61176 Compare July 31, 2026 15:29
@robert3005

Copy link
Copy Markdown
Contributor Author

Do we still need LazyBitBufferBuilder?

@robert3005
robert3005 force-pushed the claude/builders-canonical-children-9ze0t6 branch from e8186e0 to fe7132d Compare July 31, 2026 16:54
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from 7b61176 to 32c0dbd Compare July 31, 2026 16:54
@robert3005
robert3005 force-pushed the claude/builders-canonical-children-9ze0t6 branch from fe7132d to 9b0fd4b Compare July 31, 2026 17:22
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from 32c0dbd to 9c58b3c Compare July 31, 2026 17:22
@robert3005

Copy link
Copy Markdown
Contributor Author

On LazyBitBufferBuilder: yes, it is still carrying its weight, in two roles.

  1. It is ValidityBuilder::pending — the bits that arrive one row at a time from append_scalar / append_n_nulls. Its laziness (no buffer at all until the first null) is exactly what makes an all-valid builder cost nothing, so ValidityBuilder would have to reimplement it to drop it.
  2. The flat builders use it directly: BoolBuilder, PrimitiveBuilder, DecimalBuilder, VarBinViewBuilder, plus varbinview::compute::zip. Those need bit-level operations a run cannot express — set_bit at an arbitrary index (PrimitiveBuilder's uninit ranges) and append_buffer.

What this PR does remove is the nested builders' use of it: ListBuilder, ListViewBuilder, StructBuilder and FixedSizeListBuilder all hold a ValidityBuilder now, and nothing outside ValidityBuilder builds a bit buffer for a nested builder.

I did not migrate the flat builders on purpose: a flat builder copies its values into one contiguous buffer, so there is no chunk boundary for validity runs to line up with, and keeping runs there would hand downstream a chunked bool array for an otherwise flat array. Say the word if you would rather have one accumulator everywhere and I will do it.

@robert3005 robert3005 added the changelog/performance A performance improvement label Jul 31, 2026
@robert3005
robert3005 force-pushed the claude/builders-canonical-children-9ze0t6 branch from 9b0fd4b to bc03a46 Compare July 31, 2026 18:38
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from 9c58b3c to 4e607da Compare July 31, 2026 18:38
@robert3005
robert3005 force-pushed the claude/builders-canonical-children-9ze0t6 branch from bc03a46 to 79dc1ec Compare July 31, 2026 18:43
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from 4e607da to 6541973 Compare July 31, 2026 18:43
@robert3005
robert3005 force-pushed the claude/builders-canonical-children-9ze0t6 branch from 79dc1ec to c9bacae Compare July 31, 2026 19:20
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from 6541973 to dd4420b Compare July 31, 2026 19:20
@robert3005
robert3005 force-pushed the claude/builders-canonical-children-9ze0t6 branch from c9bacae to edb04e4 Compare August 5, 2026 12:51
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from 5e51643 to 421b6d4 Compare August 5, 2026 12:51
claude and others added 2 commits August 7, 2026 11:39
A nested builder learns about validity from two sources: one row at a time
as scalars are appended, and a whole array's worth at a time as arrays are.
Only the first needs a null buffer, but `LazyBitBufferBuilder` treated both
the same, so every appended array had its validity executed into a `Mask`
and its bits copied.

`ValidityBuilder` keeps a whole array's validity as a run and concatenates
the runs at the end, the way `Validity::concat` already does for
`StructArray::try_concat`. `AllValid` and `AllInvalid` runs cost nothing,
array-backed runs are bool arrays that are already built, and a builder that
only ever saw uniform validity still answers from its nullability rather
than producing a bool array. However few values a run covers, it is kept as
it arrived, so a builder's validity is split on exactly the boundaries its
children are.

`StructBuilder`, `ListBuilder`, `ListViewBuilder` and `FixedSizeListBuilder`
use it; the leaf builders keep `LazyBitBufferBuilder`.

Signed-off-by: Claude <noreply@anthropic.com>
Signed-off-by: Robert Kruszewski <robert@spiraldb.com>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Robert Kruszewski <github@robertk.io>
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from 421b6d4 to b24a656 Compare August 7, 2026 10:40
@robert3005
robert3005 force-pushed the claude/builders-canonical-children-9ze0t6 branch from edb04e4 to 76b988c Compare August 7, 2026 10:40
@robert3005

Copy link
Copy Markdown
Contributor Author

Collapsed into #9064 along with #8964 and #9131.

@robert3005 robert3005 closed this Aug 7, 2026
@robert3005
robert3005 deleted the claude/builders-lazy-validity-9ze0t6 branch August 7, 2026 11:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog/performance A performance improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants