Skip to content

Canonicalize chunked nested types through the builder [builders-child-stack] - #8967

Open
robert3005 wants to merge 1 commit into
claude/constant-fast-paths-9ze0t6from
claude/chunked-canonical-via-builder-9ze0t6
Open

Canonicalize chunked nested types through the builder [builders-child-stack]#8967
robert3005 wants to merge 1 commit into
claude/constant-fast-paths-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#8964#9064#9046 — review those first. This is the payoff for the rest of the stack.

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, and both helpers are deleted. Nothing has to be configured for this to work: since #8964 a nested builder keeps every appended chunk however short, which is the behaviour the swizzles hand-rolled. An earlier revision of this PR called builder.set_min_chunk_len(0) here to opt out of a length threshold that no longer exists.

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.

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, and no observable difference: the generic builder path preserves every chunk boundary, which is exactly what the swizzles did.

Checks

  • cargo nextest run --workspace — 7168 passed, 560 skipped
  • cargo clippy --all-targets --all-features — clean
  • cargo +nightly fmt --all --check — clean

Generated by Claude Code

Stacked PR Chain: builders-child-stack

PR Title Merges Into
#9046 perf(array): stop flattening appended list views in ListViewBuilder [builders-child-stack] N/A
#8964 feat(array): builders no longer canonicalize their children [builders-child-stack] #9046
#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 31.44%

❌ 9 regressed benchmarks
✅ 1833 untouched benchmarks
⏩ 55 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.7 µs 38.3 µs -38.04%
Simulation canonicalize[256, 32] 23.8 µs 37.8 µs -37.06%
Simulation canonicalize[16, 32] 24.7 µs 38 µs -35.06%
Simulation canonicalize[1024, 8] 18.9 µs 28.6 µs -33.86%
Simulation canonicalize[256, 8] 18.9 µs 28.2 µs -32.82%
Simulation canonicalize[1024, 2] 18.1 µs 25.8 µs -30%
Simulation canonicalize[16, 8] 20.4 µs 28.1 µs -27.44%
Simulation canonicalize[256, 2] 18.2 µs 24.8 µs -26.51%
Simulation canonicalize[16, 2] 23.4 µs 29.4 µs -20.34%

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 (9c8574c) with claude/constant-fast-paths-9ze0t6 (681bd37)

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.

@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
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch 2 times, most recently from afed52f to 79f350a Compare July 29, 2026 07:35
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch 2 times, most recently from 1145029 to 8f39b06 Compare July 29, 2026 14:19
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch from 8f39b06 to 825ab2e Compare July 31, 2026 13:35
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch from 825ab2e to ce2eb1d Compare July 31, 2026 15:29
Canonical::FixedSizeList(swizzle_fixed_size_list_chunks(
DType::List(elem_dtype, _) => {
let owned_chunks: Vec<ArrayRef> = array.iter_chunks().cloned().collect();
Canonical::List(swizzle_list_chunks(

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 is unnecessary as well

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 — swizzle_list_chunks is gone too, so Variant is the only dtype left with a hand-written pack (there is no variant builder). The generic builder path produces the same array: append_listview_array / extend_from_list hand each chunk’s elements to the ChildBuilder, which keeps them as chunks of the combined elements. Added a case::list to canonicalize_reuses_chunk_children covering it.

One consequence worth flagging: swizzle_list_chunks was the last canonicalization path that allocated through the session allocator (ctx.allocator().allocate_typed for offsets/sizes). builder_with_capacity_in still ignores the allocator it is handed, so chunked primitives, structs and FSLs already went around it — lists were the odd one out. I removed list_canonicalize_uses_memory_session_allocator with the swizzle rather than keep a bespoke path alive for it; restoring the property means teaching the builders to allocate through a HostAllocatorRef. Happy to file that as a follow-up if you want it tracked.

@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch from ce2eb1d to cefbc8e Compare July 31, 2026 16:54
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch from cefbc8e to 0d4eb9a Compare July 31, 2026 17:22
@robert3005 robert3005 changed the title refactor(array): canonicalize chunked structs and FSLs through the builder [builders-child-stack] Canonicalize chunked nested types through the builder [builders-child-stack] Jul 31, 2026
@robert3005 robert3005 added the changelog/chore A trivial change label Jul 31, 2026
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch from 0d4eb9a to 1e8346a Compare July 31, 2026 18:39
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch from 1e8346a to 0431fa7 Compare July 31, 2026 18:43
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch from 0431fa7 to a9f7d31 Compare July 31, 2026 19:20
`pack_struct_chunks`, `swizzle_fixed_size_list_chunks` and
`swizzle_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 all three are
dead weight - the generic builder path produces the same swizzled array,
with each chunk's children kept as chunks of the combined child.

Only `Variant` still needs a hand-written pack, because there is no variant
builder.

This drops the one canonicalization path that allocated its buffers through
the session allocator: `swizzle_list_chunks` allocated its offsets and sizes
with `ctx.allocator()`, whereas `builder_with_capacity_in` still ignores the
allocator it is handed, so chunked primitives, structs and FSLs already went
around it. `list_canonicalize_uses_memory_session_allocator` guarded that
one path and goes with it; restoring the property means teaching the
builders to allocate through a `HostAllocatorRef`, not keeping a bespoke
list swizzle alive.

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>
@robert3005
robert3005 force-pushed the claude/chunked-canonical-via-builder-9ze0t6 branch from a9f7d31 to 9c8574c Compare July 31, 2026 22:45
@robert3005
robert3005 changed the base branch from claude/builders-lazy-validity-9ze0t6 to claude/constant-fast-paths-9ze0t6 July 31, 2026 22:47
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.

2 participants