-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Speed up collect_bool and remove unsafe, optimize take_bits, take_native for null values
#8849
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
Changes from all commits
e3807c7
b0086f4
de5a584
dcb4f96
6a39497
507a9fb
b1a87ea
f9004a2
964a076
f93da94
973121c
07e7a53
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -422,9 +422,10 @@ fn take_native<T: ArrowNativeType, I: ArrowPrimitiveType>( | |
| .enumerate() | ||
| .map(|(idx, index)| match values.get(index.as_usize()) { | ||
| Some(v) => *v, | ||
| None => match n.is_null(idx) { | ||
| true => T::default(), | ||
| false => panic!("Out-of-bounds index {index:?}"), | ||
| // SAFETY: idx<indices.len() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had to read this several times to convince myself it is correct -- namely that I found this whole method actually pretty confusing as there are multiple things called values and indices (and I also double checked that there is a test for out of bounds indexes here: |
||
| None => match unsafe { n.inner().value_unchecked(idx) } { | ||
| false => T::default(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Dandandan do you know why this even bothers to look at the null buffer again? I realize you didn't change this code, but it seems to me like checking It seems like all this is doing is re-checking that so, TLDR I suggest we remove this clause entirely (could be a follow on PR)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can't remove it - it checks the indices value is null as well to make sure out of bounds on a non-null value leads to a panic. So currently:
We could consider out of bounds either always panics or always gives a default (0) value but the current API (and tests) requires it to be this way
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Logic looks correct to me, but is not really intuitive. In our microbenchmarks the current code might faster because it avoids a branch on the validity bit in the happy case, but I'm not sure that will still be faster on larger inputs, or if a larger amount of indices is null. I would find the following more intuitive, and hopefully not much slower (slice, range and zip iteration should all be TrustedLen): indices
.values()
.iter()
.zip((0..n.len()).map(move |i| unsafe { n.inner().value_unchecked(i) }))
.map(|(index, valid)| if valid {
values[index.as_usize()]
} else {
T::default()
})
.collect()
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I made a PR with this change: |
||
| true => panic!("Out-of-bounds index {index:?}"), | ||
| }, | ||
| }) | ||
| .collect(), | ||
|
|
@@ -448,8 +449,10 @@ fn take_bits<I: ArrowPrimitiveType>( | |
| let mut output_buffer = MutableBuffer::new_null(len); | ||
| let output_slice = output_buffer.as_slice_mut(); | ||
| nulls.valid_indices().for_each(|idx| { | ||
| if values.value(indices.value(idx).as_usize()) { | ||
| bit_util::set_bit(output_slice, idx); | ||
| // SAFETY: idx is a valid index in indices.nulls() --> idx<indices.len() | ||
| if values.value(unsafe { indices.value_unchecked(idx).as_usize() }) { | ||
| // SAFETY: MutableBuffer was created with space for indices.len() bit, and idx < indices.len() | ||
| unsafe { bit_util::set_bit_raw(output_slice.as_mut_ptr(), idx) }; | ||
| } | ||
| }); | ||
| BooleanBuffer::new(output_buffer.into(), 0, 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.
Maybe we could make the
nega generic argument on MutableBuffer::collect_bool and then avoid the duplication (as a follow on PR)Or maybe make a
collect_boolfunction inbit_utilthat returns a Vec and have the mutable buffer and this one call it 🤔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.
That makes sense, I was thinking about it as well.