-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add special implementation for zip for Utf8View/BinaryView scalars #8963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
70809ca to
d64eaf5
Compare
d64eaf5 to
1e05651
Compare
2006eff to
724157f
Compare
|
I have some decent speed-ups, see benchmarks. It would be great if somone could provide an initial review of this. |
724157f to
ba3c71a
Compare
|
@rluvaton Maybe you could have a look since you were the original author of this optimization? Thank you! |
|
Great job! Will try to review it today |
alamb
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this PR @mkleen -- could you please split out the benchmarks into a separate PR so it it easier to evaluate the performance difference due to this change?
94fd9a6 to
310a81a
Compare
310a81a to
918e2d0
Compare
| truthy: Option<GenericByteViewArray<T>>, | ||
| falsy: Option<GenericByteViewArray<T>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I question this choice of holding a GenericByteViewArray itself here 🤔
Can we decompose it to just the buffers and view u128 perhaps?
| fn get_value_from_scalar(scalar: &dyn Array) -> Option<GenericByteViewArray<T>> { | ||
| if scalar.is_null(0) { | ||
| None | ||
| } else { | ||
| Some(scalar.as_byte_view().clone()) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I would inline this; there's a lot of functions here so we would benefit from removing simple ones like this
|
|
||
| let bytes = Buffer::from(mutable); | ||
|
|
||
| (bytes.into(), buffers, Some(NullBuffer::new_valid(length))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| (bytes.into(), buffers, Some(NullBuffer::new_valid(length))) | |
| (vec![views[0]; length].into(), buffers, None) |
No need for null buffer if all are valid
Also can simplify buffer creation
| // otherwise, we simply use the view. | ||
| let view_falsy = if falsy.total_buffer_bytes_used() > 0 { | ||
| let byte_view_falsy = ByteView::from(falsy.views()[0]); | ||
| let new_index_falsy_buffers = buffers.len() as u32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let new_index_falsy_buffers = buffers.len() as u32; | |
| let new_index_falsy_buffers = buffers.len() as u32 + byte_view_falsy.buffer_index; |
We can't assume falsy only has 1 buffer
| // All values are null | ||
| return Self::get_scalar_buffers_and_nulls_for_all_values_null(number_of_values); | ||
| } | ||
| let view = value.views()[0].to_byte_slice(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this view have the right buffer offset?
| let total_number_of_bytes = true_count * view_truthy.len() | ||
| + (predicate.len() - true_count) * view_falsy.to_byte_slice().len(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
view_truthy and view_falsy are u128s which we can know the constant size for; no need to call len() on their byte slices
| ( | ||
| bytes.into(), | ||
| buffers, | ||
| Some(NullBuffer::new_valid(predicate.len())), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| Some(NullBuffer::new_valid(predicate.len())), | |
| None, |
All values are non-null so we don't need a null buffer
| let mut mutable = MutableBuffer::with_capacity(0); | ||
| mutable.repeat_slice_n_times((0u128).to_byte_slice(), len); | ||
|
|
||
| (mutable.into(), vec![], Some(NullBuffer::new_null(len))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let mut mutable = MutableBuffer::with_capacity(0); | |
| mutable.repeat_slice_n_times((0u128).to_byte_slice(), len); | |
| (mutable.into(), vec![], Some(NullBuffer::new_null(len))) | |
| (vec![0; len].into(), vec![], Some(NullBuffer::new_null(len))) |
At this point it would be better to inline this instead of having this thin wrapper function
| (mutable.into(), vec![], Some(NullBuffer::new_null(len))) | ||
| } | ||
|
|
||
| fn get_scalar_buffers_and_nulls_for_single_non_nullable( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| fn get_scalar_buffers_and_nulls_for_single_non_nullable( | |
| fn get_view_parts_single_value( |
These function names can do with improving, they aren't very readable
| let mut bytes = MutableBuffer::with_capacity(0); | ||
| bytes.repeat_slice_n_times(view, number_of_values); | ||
|
|
||
| let bytes = Buffer::from(bytes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let mut bytes = MutableBuffer::with_capacity(0); | |
| bytes.repeat_slice_n_times(view, number_of_values); | |
| let bytes = Buffer::from(bytes); | |
| let bytes = vec![value.views()[0]; number_of_values]; |
No need to use MutableBuffer since our values (views) are simple u128s
Which issue does this PR close?
zipwith scalar for Utf8View #8724Rationale for this change
It's explained in the issue.
What changes are included in this PR?
This adds a special implementation for Utf8View/BinaryView scalars for zip based on the design from #8653. It also includes tests. Benchmarks are available here:
Are these changes tested?
Yes.
Are there any user-facing changes?
There is a new struct
ByteViewScalarImpl.Benchmarks
System: Apple M1 Max with 10 cores on macOS 26.1