-
Notifications
You must be signed in to change notification settings - Fork 2k
Improve speed of median by implementing special GroupsAccumulator
#13681
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 1 commit
cff822e
7f10006
6f172ef
cacc693
11e6753
955036f
17bd90b
28d8716
1244df4
c812350
fdc9b33
e2f384f
4b8a4ad
5603bc0
7e6a73a
1c7b57a
5eb7711
5a52e7c
6f56a63
e963d50
5fd9d8e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
state.
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,8 +22,9 @@ use std::sync::Arc; | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| use arrow::array::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| downcast_integer, ArrowNumericType, BooleanArray, GenericListBuilder, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| GenericListViewArray, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| GenericListViewArray, ListArray, ListBuilder, PrimitiveArray, PrimitiveBuilder, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| use arrow::buffer::{OffsetBuffer, ScalarBuffer}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| use arrow::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| array::{ArrayRef, AsArray}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| datatypes::{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -235,21 +236,23 @@ impl<T: ArrowNumericType> Accumulator for MedianAccumulator<T> { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The median accumulator accumulates the raw input values | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// as `ScalarValue`s | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The median groups accumulator accumulates the raw input values | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// For calculating the accurate medians of groups, we need to store all values | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// of groups before final evaluation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// And values in each group will be stored in a `Vec<T>`, so the total group values | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// will be actually organized as a `Vec<Vec<T>>`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// In partial aggregation stage, the `values` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The intermediate state is represented as a List of scalar values updated by | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// `merge_batch` and a `Vec` of `ArrayRef` that are converted to scalar values | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// in the final evaluation step so that we avoid expensive conversions and | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| /// allocations during `update_batch`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #[derive(Debug)] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| struct MedianGroupAccumulator<T: ArrowNumericType + Send> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| struct MedianGroupsAccumulator<T: ArrowNumericType + Send> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| data_type: DataType, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| group_values: Vec<Vec<T::Native>>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
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. Just wonder -- using P.S. asking just because when I was doing +- same for count distinct (PR), the performance for GroupsAccumulator with
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 think among other things, the intermediate state management (creating ListArrays directly rather than from ScalarValue) probably helps a lot: There is also an extra allocation per group when using the groups accumulator adapter thingie That being said, it is a fair question how much better the existing MedianAccumulator could be if it built the ListArrays as does this PR directly 🤔
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. @korowa I think what mentioned by @alamb is a important point about the improvement. Following are some other points for me:
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.
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. There was some improvements, but overall results for clickbench q9 (I was mostly looking at this query) were like x2.63 for GroupsAccumulator, and x2.30 for the regular Accumulator -- so it would be like 13-15% overall difference, which is not as massive as this PR results. However, maybe things has changed in GroupsAccumulator implementation, and now even plain UPD: and, yes, maybe producing state, as pointed out by @alamb above, was (at least partially) the cause of non-significant improvement -- in count distinct it was implemented via
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. Seem really worth seeking the reason more deeply. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| null_state: NullState, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl<T: ArrowNumericType + Send> MedianGroupAccumulator<T> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl<T: ArrowNumericType + Send> MedianGroupsAccumulator<T> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn new(data_type: DataType) -> Self { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Self { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| data_type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -259,7 +262,7 @@ impl<T: ArrowNumericType + Send> MedianGroupAccumulator<T> { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl<T: ArrowNumericType + Send> GroupsAccumulator for MedianGroupAccumulator<T> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| impl<T: ArrowNumericType + Send> GroupsAccumulator for MedianGroupsAccumulator<T> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn update_batch( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| &mut self, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| values: &[ArrayRef], | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -295,7 +298,7 @@ impl<T: ArrowNumericType + Send> GroupsAccumulator for MedianGroupAccumulator<T> | |||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> Result<()> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert_eq!(values.len(), 1, "one argument to merge_batch"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The merged values should be organized like as a `ListArray` like: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The merged values should be organized like as a `non-nullable ListArray` like: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ```text | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // group 0: [1, 2, 3] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -306,26 +309,55 @@ impl<T: ArrowNumericType + Send> GroupsAccumulator for MedianGroupAccumulator<T> | |||||||||||||||||||||||||||||||||||||||||||||||||||
| // ``` | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let input_group_values = values[0].as_list::<i32>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert!(input_group_values.null_count() == 0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Ensure group values big enough | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.group_values.resize(total_num_groups, Vec::new()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Adds the counts with the partial counts | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Extend values to related groups | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| group_indices | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| .iter() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| .zip(input_group_values.iter()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| .for_each(|(&group_index, values_opt)| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Some(values) = values_opt { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let values = values.as_primitive::<T>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.group_values[group_index].extend(values.values().iter()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let values = values_opt.unwrap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let values = values.as_primitive::<T>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.group_values[group_index].extend(values.values().iter()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn evaluate(&mut self, emit_to: EmitTo) -> Result<ArrayRef> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| todo!() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn state(&mut self, emit_to: EmitTo) -> Result<Vec<ArrayRef>> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let emit_group_values = emit_to.take_needed(&mut self.group_values); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Build offsets | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut offsets = Vec::with_capacity(self.group_values.len() + 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| offsets.push(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let mut cur_len = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| for group_value in &emit_group_values { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| cur_len += group_value.len() as i32; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
alamb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| offsets.push(cur_len); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let offsets = OffsetBuffer::new(ScalarBuffer::from(offsets)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
alamb marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Build inner array | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let flatten_group_values = | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| emit_group_values.into_iter().flatten().collect::<Vec<_>>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let group_values_array = | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| PrimitiveArray::<T>::new(ScalarBuffer::from(flatten_group_values), None); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Build the result list array | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| let result_list_array = ListArray::new( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Arc::new(Field::new_list_field(self.data_type.clone(), false)), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| offsets, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Arc::new(group_values_array), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| None, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Ok(vec![Arc::new(result_list_array)]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn state(&mut self, emit_to: EmitTo) -> Result<Vec<ArrayRef>> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn evaluate(&mut self, emit_to: EmitTo) -> Result<ArrayRef> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| todo!() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
Given it is important to track the median values for each group separately I don't really see a way around Vec/Vec -- I think it is the simplest version and will have pretty reasonable performance
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.
Yes, I tried not to use
Vec<Vec<T>>for avoiding copying fromVec<Vec<T>>to theresult Vec<T>, but it is hard to do that.