Skip to content

refactor(array): canonicalize chunked structs and FSLs through the builder [builders-child-stack]#8967

Open
robert3005 wants to merge 1 commit into
claude/builders-lazy-validity-9ze0t6from
claude/chunked-canonical-via-builder-9ze0t6
Open

refactor(array): canonicalize chunked structs and FSLs through the builder [builders-child-stack]#8967
robert3005 wants to merge 1 commit into
claude/builders-lazy-validity-9ze0t6from
claude/chunked-canonical-via-builder-9ze0t6

Conversation

@robert3005

@robert3005 robert3005 commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Stacked on #8966#8965#8964 — review those first. This is the payoff for the three below it.

ChunkedArray's _canonicalize carried four hand-written special cases plus a generic append_to_builder fallback. Two of them existed purely because append_to_builder used to decode a builder's children; their doc comments say so:

  • pack_struct_chunks — "a single StructArray, where the data for each field is a ChunkedArray"
  • swizzle_fixed_size_list_chunks — "reuse the chunks' elements children directly as the chunks of a combined elements array without copying"

That is what ChildBuilder does now, so both are dead weight — an extra path to keep correct for a result the generic path already produces.

What changes are included in this PR?

DType::Struct and DType::FixedSizeList fall through to the builder, which is told set_min_chunk_len(0): every chunk is worth keeping however short, since reusing the chunks' children is the entire point of canonicalizing a ChunkedArray rather than concatenating it. Both helpers are deleted (57 insertions, 60 deletions, one file).

I verified the shapes match before deleting anything. A tree_display probe over chunked struct / list / FSL inputs produces byte-identical trees on both paths for chunks of 200 rows — struct field is vortex.chunked(i32, len=400) with two chunks, FSL elements likewise. Two new rstest cases lock that in using chunks of one row, far below the threshold a nested builder applies on its own; dropping the set_min_chunk_len(0) line fails both.

What is not included

ListArray keeps swizzle_list_chunks. The builder produces the same shape — is_zero_copy_to_list: true and all — but the swizzle also allocates its offsets and sizes through ctx.allocator(), and builders are not allocator-aware: builder_with_capacity_in takes a HostAllocatorRef and discards it (let _allocator = allocator;). list_canonicalize_uses_memory_session_allocator catches exactly this. Making it work means threading an allocator through BufferMut in vortex-buffer, which has none today — a bigger and separate piece of work than this stack, so the list swizzle stays until someone does it.

pack_variant_chunks stays too: builder_with_capacity is unimplemented!() for DType::Variant.

StructArray::try_concat is now unused in-tree. It is pub, and a useful helper in its own right, so I left it — removing public API seemed like a separate call for a reviewer to make rather than something to slip into this PR.

What APIs are changed? Are there any user-facing changes?

No API changes. The observable difference is confined to chunked arrays with chunks shorter than 64 rows, and there is none: set_min_chunk_len(0) reproduces the swizzles' behaviour of preserving every boundary.

Checks

  • cargo test -p vortex-array — 3177 lib tests
  • cargo test --lib --tests green for vortex-arrow, vortex-layout, vortex-scan, vortex-file, vortex-datafusion, vortex-btrblocks, vortex-compressor, vortex-row, vortex, vortex-ipc, vortex-json, vortex-tensor
  • cargo +nightly fmt --all, cargo clippy -p vortex-array --all-targets --all-features

Generated by Claude Code

Stacked PR Chain: builders-child-stack

PR Title Merges Into
#8964 feat(array): builders no longer canonicalize their children [builders-child-stack] N/A
#8965 feat(array): let callers choose a nested builder's chunk threshold [builders-child-stack] #8964
#8966 perf(array): accumulate nested builder validity without a null buffer [builders-child-stack] #8965
#8967 refactor(array): canonicalize chunked structs and FSLs through the builder [builders-child-stack] #8966

Generated by Claude Code

@codspeed-hq

codspeed-hq Bot commented Jul 25, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 30.96%

❌ 9 regressed benchmarks
✅ 1840 untouched benchmarks
⏩ 46 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Simulation canonicalize[1024, 32] 23.1 µs 38.2 µs -39.43%
Simulation canonicalize[256, 32] 23.3 µs 37.3 µs -37.64%
Simulation canonicalize[16, 32] 24.3 µs 37.4 µs -35.05%
Simulation canonicalize[1024, 8] 19.6 µs 28.5 µs -31.29%
Simulation canonicalize[256, 8] 19.6 µs 28.2 µs -30.54%
Simulation canonicalize[1024, 2] 18.2 µs 25.5 µs -28.72%
Simulation canonicalize[16, 8] 20.1 µs 28 µs -28.13%
Simulation canonicalize[256, 2] 18.3 µs 24.6 µs -25.67%
Simulation canonicalize[16, 2] 23.3 µs 29.2 µs -20.06%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing claude/chunked-canonical-via-builder-9ze0t6 (afed52f) with claude/builders-lazy-validity-9ze0t6 (dc71bbf)2

Open in CodSpeed

Footnotes

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

  2. No successful run was found on claude/builders-lazy-validity-9ze0t6 (85f8dcd) during the generation of this report, so b4fb298 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@claude claude Bot changed the title refactor(array): canonicalize chunked structs and FSLs through the builder refactor(array): canonicalize chunked structs and FSLs through the builder [builders-child-stack] Jul 25, 2026
…ilder

`pack_struct_chunks` and `swizzle_fixed_size_list_chunks` existed because
`append_to_builder` used to decode a builder's children: without them,
canonicalizing a `ChunkedArray` would concatenate every chunk's children
instead of reusing them. Their doc comments describe what the builders now
do on their own, so both are dead weight.

Route both dtypes through the generic builder path, telling it to keep every
chunk boundary. `ListArray` keeps its swizzle for now — that one also
allocates its offsets and sizes through the session allocator, which the
builders cannot yet do.

Signed-off-by: Claude <noreply@anthropic.com>
@robert3005
robert3005 force-pushed the claude/builders-lazy-validity-9ze0t6 branch from dc71bbf to 85f8dcd Compare July 25, 2026 21:07
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch from 5dd13d4 to afed52f Compare July 25, 2026 21:07
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.

2 participants