-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Optimize Nullstate / accumulators
#19625
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
|
run benchmark tpch |
|
run benchmarks |
|
🤖 |
|
🤖: Benchmark completed Details
|
|
🤖 |
|
🤖: Benchmark completed Details
|
|
Looks like it is a nice win. |
|
run benchmarks |
|
🤖 |
|
🤖: Benchmark completed Details
|
|
run benchmarks |
|
🤖 |
|
Benchmark script failed with exit code 101. Last 10 lines of output: Click to expand |
|
run benchmarks |
|
🤖 |
|
🤖: Benchmark completed Details
|
|
run benchmarks |
|
🤖 |
fb8f6ac to
05414e8
Compare
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.
this is looking pretty cool
| /// If `seen_values[i]` is false, have not seen any values that | ||
| /// pass the filter yet for group `i` | ||
| seen_values: BooleanBufferBuilder, | ||
| /// If true, all groups seen so far have seen at least one non-null value |
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 encode this as an state enum so it is clearer how things are related to BooleamBufferBuilder
Something like
enum SeenValues {
/// All groups seen so far have seen at least one non-null value
All {
num_values: usize,
}
// some groups have not yet seen a non-null value
Some {
values: BooleanBufferBuilder,
}
}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.
done
|
run benchmarks |
|
🤖 |
|
🤖: Benchmark completed Details
|
|
run benchmark aggregate |
|
🤖 Hi @Dandandan, thanks for the request (#19625 (comment)).
Please choose one or more of these with |
|
run benchmark aggregate_query_sql aggregate_vectorized |
|
🤖 |
|
🤖: Benchmark completed Details
|
|
🤖 |
|
Benchmark script failed with exit code 101. Last 10 lines of output: Click to expand |
|
run benchmark tpch_mem |
|
🤖 |
|
🤖: Benchmark completed Details
|
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 @Dandandan -- this looks good to me. I have some comment / structure comments but I don't think anything is needed prior to merge (we could do it as follow on PRs or never)
| // "not seen" valid) | ||
| let seen_values = | ||
| initialize_builder(&mut self.seen_values, total_num_groups, false); | ||
| if let SeenValues::All { num_values } = &mut self.seen_values |
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.
One possibility is to make this another another function to reduce some duplication and have a place it could be explained
Maybe something like
if let Some(num_values) = self.all_values_mut() && opt_filter.is_none && values.null_count == 0 {
...
}?
Though maybe an extra level of indirection would make it harder to follow.
| // "not seen" valid) | ||
| let seen_values = | ||
| initialize_builder(&mut self.seen_values, total_num_groups, false); | ||
| if let SeenValues::All { num_values } = &mut self.seen_values |
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.
| if let SeenValues::All { num_values } = &mut self.seen_values | |
| // skip null handling if no nulls in input or accumulator | |
| if let SeenValues::All { num_values } = &mut self.seen_values |
| // "not seen" valid) | ||
| let seen_values = | ||
| initialize_builder(&mut self.seen_values, total_num_groups, false); | ||
| if let SeenValues::All { num_values } = &mut self.seen_values |
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.
| if let SeenValues::All { num_values } = &mut self.seen_values | |
| // skip null handling if no nulls in input or accumulator | |
| if let SeenValues::All { num_values } = &mut self.seen_values |
| SeenValues::Some { .. } => { | ||
| let mut old_values = match std::mem::replace( | ||
| &mut self.seen_values, | ||
| SeenValues::All { num_values: 0 }, |
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.
It might be nicer if you implemented SeenValues::new() or maybe even SeenValues::default
SO this couls look like
let mut old_values = match std::mem::take(&mut self.seen_values);| total_num_groups, | ||
| |group_index, new_value| { | ||
| let value = &mut self.values[group_index]; | ||
| // SAFETY: group_index is guaranteed to be in bounds |
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 recommend adding safety notes to the docs of GroupsAccumulator in https://github.com/apache/datafusion/blob/36ec9f1de0aeabca60b8f7ebe07d650b8ef03506/datafusion/expr-common/src/groups_accumulator.rs#L114-L113
That explains that all group indexes are guaranteed to be <= total_num_groups and that can be relied on for safety
|
run benchmark tpch_mem |
|
🤖 |
|
(will see if we can see a reproducable speedup) |
|
🤖: Benchmark completed Details
|
Which issue does this PR close?
Rationale for this change
Speedup accumulator code (sum, avg, count) by specializing on non-null cases.
What changes are included in this PR?
Nullstateto non-null values.Are these changes tested?
Are there any user-facing changes?