perf: add fast path for uniform fill values in array_resize#20617
perf: add fast path for uniform fill values in array_resize#20617alamb merged 11 commits intoapache:mainfrom
array_resize#20617Conversation
neilconway
left a comment
There was a problem hiding this comment.
Nice work!
Can you check that the SLT tests cover the new fast-path?
|
|
||
| // Fast path: at least one row needs to grow and all rows share | ||
| // the same fill value. | ||
| if is_uniform_fill { |
There was a problem hiding this comment.
The naming is a bit confusing: is_uniform_fill is false if the fill value is uniform but there are no rows that need to grow. How about use_batch_fill or use_bulk_fill or similar?
There was a problem hiding this comment.
I think use_bulk_fill is pretty clear, I renamed it to that, thanks
| if extra > max_extra { | ||
| max_extra = extra; | ||
| } |
There was a problem hiding this comment.
| if extra > max_extra { | |
| max_extra = extra; | |
| } | |
| max_extra = max_extra.max(extra); |
| if start + count > offset_window[1] { | ||
| let extra_count = (start + count - offset_window[1]).to_usize().unwrap(); | ||
| let end = offset_window[1]; | ||
| mutable.extend(0, (start).to_usize().unwrap(), (end).to_usize().unwrap()); |
There was a problem hiding this comment.
(Minor) Why (start)? Parens unnecessary, here and below.
|
|
||
| let mut null_builder = NullBufferBuilder::new(array.len()); | ||
|
|
||
| for (row_index, offset_window) in array.offsets().windows(2).enumerate() { |
There was a problem hiding this comment.
The fast path and slow path are doing very similar work and there's a bunch of duplicate code. I wonder if it would be possible to refactor them -- the key difference is just how the fill itself is done, no?
There was a problem hiding this comment.
yes, the main difference is the fill behavior, and the surrounding control flow is very similar. I’ll take a look to see what can be cleanly refactored there.
There was a problem hiding this comment.
I refactored it to pull the shared loop out of the fast and slow paths.
| let default_element = fill_scalar.to_array_of_size(max_extra)?; | ||
| let default_value_data = default_element.to_data(); | ||
|
|
||
| let capacity = Capacities::Array(original_data.len() + default_value_data.len()); |
There was a problem hiding this comment.
Is this right? default_value_data.len() is the per-row growth, I think we want the total estimated output size.
There was a problem hiding this comment.
thanks for the reminder. I added capacity calculation in the pre-scan and use for the allocation.
|
Thanks for the review @neilconway The existing SLTs already hit the fast path, but I added another array test case to cover the multi-row case more explicitly. |
|
@lyne7-sc Can you resolve the merge conflicts? |
Sure, the conflicts have been resolved. |
|
Hi @alamb — gentle ping, this PR has been open for a while. I’ve rebased and resolved conflicts, would appreciate a review when you have time. |
alamb
left a comment
There was a problem hiding this comment.
Thanks @lyne7-sc and @neilconway -- this looks good to me
| "array_resize: failed to convert size to i64" | ||
| ) | ||
| })?; | ||
| let extra_count = (start + count - offset_window[1]).to_usize().unwrap(); |
There was a problem hiding this comment.
why was this changed to unwrap (which will panic) from a internal error?
There was a problem hiding this comment.
Maybe because the calls below to unwrap() would have panic'd anyways
There was a problem hiding this comment.
Yes, this would panic in the same cases as the unwrap() calls below.
There was a problem hiding this comment.
a more robust option might be to add an overflow check here. Do you think that would be worth adding?
There was a problem hiding this comment.
Let's add the check as a follow on PR
|
I merged thsi PR up from main to resolve some conflicts |
…#20617) ## Which issue does this PR close? <!-- We generally require a GitHub issue to be filed for all bug fixes and enhancements and this helps us generate change logs for our releases. You can link an issue to this PR using the GitHub syntax. For example `Closes apache#123` indicates that this PR will close issue apache#123. --> - Closes #. ## Rationale for this change <!-- Why are you proposing this change? If this is already explained clearly in the issue then this section is not needed. Explaining clearly why changes are proposed helps reviewers understand your changes and offer better suggestions for fixes. --> `array_resize` currently does extra per-row work when resizing list arrays. This pr optimizes the common fast path where the fill value is uniform. ## What changes are included in this PR? - Add a fast path in `array_resize` for uniform fill values. - Precompute the maximum required growth and reuse a single fill buffer in the uniform-fill path. ### Benchmarks ``` group main optimized ----- ---- --------- array_resize_i64/grow_default_null_fill_10_to_500 11.24 3.2±0.07ms ? ?/sec 1.00 283.9±20.10µs ? ?/sec array_resize_i64/grow_uniform_fill_10_to_500 4.15 1648.0±38.12µs ? ?/sec 1.00 397.2±20.46µs ? ?/sec array_resize_i64/grow_variable_fill_10_to_500 1.00 1667.7±50.31µs ? ?/sec 1.02 1692.8±61.54µs ? ?/sec array_resize_i64/mixed_grow_shrink_1000x_100 4.83 373.0±8.91µs ? ?/sec 1.00 77.1±5.90µs ? ?/sec array_resize_i64/shrink_uniform_fill_500_to_10 1.00 8.1±0.51µs ? ?/sec 1.06 8.5±0.50µs ? ?/sec ``` <!-- There is no need to duplicate the description in the issue here but it is sometimes worth providing a summary of the individual changes in this PR. --> ## Are these changes tested? Yes <!-- We typically require tests for all PRs in order to: 1. Prevent the code from being accidentally broken by subsequent changes 2. Serve as another way to document the expected behavior of the code If tests are not included in your PR, please explain why (for example, are they covered by existing tests)? --> ## Are there any user-facing changes? No <!-- If there are user-facing changes then we may require documentation to be updated before approving the PR. --> <!-- If there are any breaking changes to public APIs, please add the `api change` label. --> --------- Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
Which issue does this PR close?
Rationale for this change
array_resizecurrently does extra per-row work when resizing list arrays. This pr optimizes the common fast path where the fill value is uniform.What changes are included in this PR?
array_resizefor uniform fill values.Benchmarks
Are these changes tested?
Yes
Are there any user-facing changes?
No