Skip to content

perf: optimize spark_array_compact (no-op fast path for null-free arrays)#4934

Open
andygrove wants to merge 3 commits into
apache:mainfrom
andygrove:perf-optimize-array-expr
Open

perf: optimize spark_array_compact (no-op fast path for null-free arrays)#4934
andygrove wants to merge 3 commits into
apache:mainfrom
andygrove:perf-optimize-array-expr

Conversation

@andygrove

Copy link
Copy Markdown
Member

Which issue does this PR close?

N/A

Rationale for this change

Optimize existing expression.

What changes are included in this PR?

array_compact only removes null elements. When the values buffer contains no null elements there is nothing to remove, so the output is bit-identical to the input. This adds a fast path to compact_list that returns the input array unchanged in that case, reusing its buffers zero-copy and skipping the per-element MutableArrayData::extend loop.

The null-containing path is left byte-for-byte unchanged, so arrays with sparse or dense element nulls take exactly the same code as before and do not regress. This deliberately avoids the per-row run-batching approach that regressed the dense-null shape in a previous attempt (#4886).

Also adds Rust unit tests (the expression previously had only SQL file / Scala coverage) and a criterion benchmark covering no-null, null-row, sparse-null, and dense-null shapes.

How are these changes tested?

New Rust unit tests assert the fast path is bit-identical to the input (including null and empty rows) and that the null-removal path is unchanged; existing SQL file tests and CometArrayExpressionSuite continue to cover end-to-end behavior.

Benchmark (criterion), baseline main vs this branch, 8192-row list columns:

array_compact: no nulls short:              316 µs -> 287 ns   (~99.9% faster)
array_compact: no nulls long:              2.46 ms -> 254 ns   (~99.99% faster)
array_compact: no element nulls, null rows: 296 µs -> 258 ns   (~99.9% faster)
array_compact: sparse nulls:                489 µs -> 488 µs   (within noise)
array_compact: dense nulls:                 258 µs -> 257 µs   (within noise)

The null-free shapes hit the zero-copy fast path; the sparse/dense-null shapes run the unchanged loop and are within noise across repeated samples.

…ree arrays)

array_compact only removes null elements. When the values buffer has no null
elements there is nothing to remove, so the result is bit-identical to the
input. Add a fast path that returns the input unchanged in that case, reusing
its buffers zero-copy and skipping the per-element MutableArrayData::extend
loop. The null-containing path is left byte-for-byte unchanged so shapes with
sparse or dense element nulls do not regress.

Add Rust unit tests (the expression previously had only SQL/Scala coverage) and
a criterion benchmark covering no-null, null-row, sparse-null, and dense-null
shapes.

@mbutrovich mbutrovich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

First pass, thanks @andygrove!

Comment thread native/spark-expr/src/array_funcs/array_compact.rs Outdated
Comment thread native/spark-expr/src/array_funcs/array_compact.rs Outdated
…hild

logical_null_count() has O(1) overrides on Dictionary/Run/Null arrays that avoid materializing a fresh null bitmap, while logical_nulls() would allocate on exactly this path for those types. Adds null_array_values_child_all_rows_empty covering the List<Null> shape produced by make_array(NULL) so a future refactor that dropped logical_null_count() would fail.

@mbutrovich mbutrovich left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This LGTM after revision, thanks @andygrove!

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