Repository navigation
perf: Optimize MutableBuffer::extend for exact-size iterators
#11656
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?
Changes from all commits
cfab89f
e2eecf1
9b988c5
2edf0bb
5352e8a
be88f77
fd8425c
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 |
|---|---|---|
|
|
@@ -20,7 +20,7 @@ | |
| //! Provides utilities for creating, manipulating, and converting Arrow arrays | ||
| //! made of primitive types, strings, and nested types. | ||
|
|
||
| use super::{ArrayData, ArrayDataBuilder, ByteView, data::new_buffers}; | ||
| use super::{ArrayData, ArrayDataBuilder, MAX_INLINE_VIEW_LEN, data::new_buffers}; | ||
| use crate::bit_mask::set_bits; | ||
| use arrow_buffer::buffer::{BooleanBuffer, NullBuffer}; | ||
| use arrow_buffer::{ArrowNativeType, Buffer, IntervalMonthDayNano, MutableBuffer, bit_util, i256}; | ||
|
|
@@ -226,13 +226,19 @@ fn build_extend_view(array: &ArrayData, buffer_offset: u32) -> Extend<'_> { | |
| mutable | ||
| .buffer1 | ||
| .extend(views[start..start + len].iter().map(|v| { | ||
|
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. Do you know where the extra time goes in
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. Yeah, this is a good call out; the branchless view copy is indeed the culprit here. I had Claude explore a bunch of alternatives and it seems that rather than unpacking and repacking the view, we can use this trick instead: That results in turning the x86 regression into a nice win (~20%) on both x86 and ARM.
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.
The codegen agrees with that. I built movq xmm1, qword ptr [r8 + 8] # high 64 bits of the view
...
add r11d, dword ptr [r8 + 8] # new buffer index
pand xmm1, xmm0 # clear the old buffer index
movq rbx, xmm1
or rbx, r11 # merge in the new oneAt 2edf0bb, the offset is added straight into the high word from memory. There is no SSE round trip, and LLVM unrolls the loop by two: cmp r15d, 13
mov r12d, 0
cmovae r12d, r11d # buffer_offset, or 0 for an inline view
add r12, qword ptr [r8 + r14 - 8] # high 64 bits + offsetOn aarch64 the
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. Thanks for checking this! |
||
| let len = *v as u32; | ||
| if len <= 12 { | ||
| return *v; // Stored inline | ||
| } | ||
| let mut view = ByteView::from(*v); | ||
| view.buffer_index += buffer_offset; | ||
| view.into() | ||
| // A view of a string of 12 bytes or fewer stores the | ||
| // string inline and has no buffer index, so adding 0 | ||
| // leaves it unchanged. | ||
| let offset = if *v as u32 > MAX_INLINE_VIEW_LEN { | ||
| buffer_offset | ||
| } else { | ||
| 0 | ||
| }; | ||
| // The buffer index is bits 64 to 95 of a view. Adding | ||
| // `buffer_offset` gives the string's buffer index in the | ||
| // output, which fits in 32 bits, so the sum never carries | ||
| // into bit 96. | ||
| v + ((offset as u128) << 64) | ||
| })); | ||
| Ok(()) | ||
| }, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.