Repository navigation
perf: Optimize appending out-of-line views in concat, BatchCoalescer
#11770
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
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 |
|---|---|---|
|
|
@@ -224,25 +224,39 @@ impl<T: ByteViewType + ?Sized> GenericByteViewBuilder<T> { | |
| /// Appends an array to the builder. | ||
| /// This will flush any in-progress block and append the data buffers | ||
| /// and add the (adapted) views. | ||
| /// | ||
| /// # Panics | ||
| /// | ||
| /// Panics if the builder would hold more than `u32::MAX` data buffers | ||
| pub fn append_array(&mut self, array: &GenericByteViewArray<T>) { | ||
| self.flush_in_progress(); | ||
| // keep original views if this array is the first to be added or if there are no data buffers (all inline views) | ||
| let keep_views = self.completed.is_empty() || array.data_buffers().is_empty(); | ||
| let starting_buffer = self.completed.len() as u32; | ||
|
|
||
| self.completed.extend(array.data_buffers().iter().cloned()); | ||
| assert!( | ||
| u32::try_from(self.completed.len()).is_ok(), | ||
| "Too many blocks" | ||
| ); | ||
|
Comment on lines
+227
to
+241
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. before this assertion what happened if the builder overflowed?
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. On an overflow, the behavior before this PR was to panic in debug mode. In release mode, the buffer index would wrap and point at the wrong buffer. |
||
|
|
||
| if keep_views { | ||
| self.views_buffer.extend_from_slice(array.views()); | ||
| } else { | ||
| self.views_buffer.extend(array.views().iter().map(|v| { | ||
| let mut byte_view = ByteView::from(*v); | ||
| if byte_view.length > MAX_INLINE_VIEW_LEN { | ||
| // Small views (<=12 bytes) are inlined, so only need to update large views | ||
| byte_view.buffer_index += starting_buffer; | ||
| } | ||
|
|
||
| byte_view.as_u128() | ||
| // 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 { | ||
| starting_buffer | ||
| } else { | ||
| 0 | ||
| }; | ||
| // The buffer index is bits 64 to 95 of a view. Adding | ||
| // `starting_buffer` gives the string's buffer index in the | ||
| // builder, which fits in 32 bits, so the sum never carries | ||
| // into bit 96. | ||
| v + ((offset as u128) << 64) | ||
| })); | ||
| } | ||
|
|
||
|
|
||
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.
🤔 this makes sense to do but it is a breaking change right? code the previously worked (produced incorrect results) ran but now this will cause a panic.
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 don't think this is a breaking change. Code that produces incorrect results should not be considered "code that previously worked" -- if that was the definition of a breaking change, many straightforward bugfixes would not be allowed in minor releases.
Additional considerations:
buffer_indexpoints at one buffer but the view'slengthandoffsetwere validated against a different buffer. Reader methods likeGenericByteViewArray::value(k)do an unchecked read somewhere off into heap memory, resulting in undefined behavior.u32::MAXdata buffers, which is extremely unlikely to occur in practice.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.
good point.