perf: use columnar state for ordered ARRAY_AGG and STRING_AGG - #24392
perf: use columnar state for ordered ARRAY_AGG and STRING_AGG#24392lyne7-sc wants to merge 6 commits into
ARRAY_AGG and STRING_AGG#24392Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #24392 +/- ##
==========================================
+ Coverage 81.14% 81.19% +0.04%
==========================================
Files 1110 1110
Lines 386137 389248 +3111
Branches 386137 389248 +3111
==========================================
+ Hits 313347 316047 +2700
- Misses 54324 54544 +220
- Partials 18466 18657 +191 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
jayzhan211
left a comment
There was a problem hiding this comment.
I think overall looks good to me
| let values = if values.data_type() == &self.value_type { | ||
| Arc::clone(values) | ||
| } else if self.value_type.contains(values.data_type()) { | ||
| cast(values.as_ref(), &self.value_type)? |
There was a problem hiding this comment.
I'm not sure about this casting — should it happen inside the accumulator, or should the types already be correct before they enter it, like at the logical layer, so we don't need to cast again here?
There was a problem hiding this comment.
I think this could ideally be handled at a common physical-expression boundary rather than by each accumulator.
However, self.value_type comes from the declared aggregate argument type, while the concrete ArrayRef is produced later during physical expression evaluation. It seems that exact type equality is not currently validated at that boundary, as also noted in #24022. #24029 handles such differences by normalizing the final array in ScalarValue::new_list.
Since this columnar implementation bypasses ScalarValue::new_list and interleave expects consistent source types, normalizing compatible inputs here seems like a reasonable way to preserve the existing behavior.
The accumulator-side nested-nullability case is exercised by ordered_aggregate_nested_nullability_mismatch_issue_24022. The test expects the result to use the accumulator's declared nullable type even when the concrete input field is non-nullable.
| .as_deref() | ||
| .unwrap_or(ordering_values); | ||
| // Detach the stored payload from potentially oversized backing buffers. | ||
| let values = make_array(copy_array_data(&values.to_data())); |
There was a problem hiding this comment.
If the array isn't filtered much — barely anything changes, or only 1-2 items get removed — would copying the whole array every time still cause high overhead?
There was a problem hiding this comment.
Yes, I had the same concern. When I tried removing this copy locally and ran does_not_over_account_memory_ordered, the accumulator's reported size increased from 2295 to 7243 bytes.
This makes me concerned that removing it could introduce a memory regression in cases where retained ArrayRefs keep much larger backing buffers alive, especially given the related memory explosion reported in #20788 with the previous ordered ARRAY_AGG implementation.
On the performance side, I also compared the two variants using the benchmark, and the difference was relatively small for this workload.
It may still be worth exploring a conditional policy, although there does not seem to be a simple way to determine whether an arbitrary nested arrow array is already compact.
Which issue does this PR close?
Rationale for this change
Ordered
ARRAY_AGGandSTRING_AGGcurrently have high memory and sorting overhead for large inputs.This is relevant to #20788, where removing
ORDER BYfrom severalARRAY_AGGexpressions avoids the reported memory explosion. This PR improves the orderedARRAY_AGGandSTRING_AGGaccumulators.What changes are included in this PR?
This PR changes the ordered aggregate accumulator to:
RowConverter.interleaveor a zero-copy slice.The accumulator is shared by ordered
ARRAY_AGGandSTRING_AGG.Are these changes tested?
Yes. Unit and SQL logic tests cover ordering, partial-state merging, NULL handling, and type preservation.
Are there any user-facing changes?
No. This is an internal performance optimization
Benchmarks
Benchmark coverage is added in #24391.
ARRAY_AGGARRAY_AGGexpressionsSTRING_AGG