Uh oh!
There was an error while loading. Please reload this page.
Intermediate result blocked approach to aggregation memory management - #15591
Intermediate result blocked approach to aggregation memory management#15591Rachelint wants to merge 102 commits into
Conversation
Dandandan
commented
Apr 8, 2025
Hi @Rachelint I think I have a alternative proposal that seems relatively easy to implement. |
Really thanks. This design in pr indeed still introduces quite a few code changes... I tried to not modify anythings about
But I found this way will introduce too many extra cost... Maybe we place the |
cc37eba to
f690940Compare95c6a36 to
a4c6f42Compare2100a5b to
0ee951cCompareHas finished development(and test) of all needed common structs!
|
c51d409 to
2863809CompareRachelint
commented
Apr 21, 2025
It is very close, just need to add more tests! |
31d660d to
2b8dd1eCompareariel-miculas
commented
Jun 18, 2026
I think it's a good idea, this is important work and it would be easier to review if split into smaller PRs. |
2010YOUY01
commented
Jun 18, 2026
I think the steps are
The performance seems to be a nearly solved issue, the PoC already showed high cardinality cases are faster (with several micro optimizations left on the table), low cardinality is slightly slower but @alamb's suggestion in #22712 (comment) is doable I think, to bring back the performance. I suggest not trying to parallelize steps 1 and 2, as they will likely conflict with each other. Step 3 should be highly parallelizable. As for the refactoring progress, I'd estimate it's about 50% complete. I haven't seen any major technical blockers so far—just need some time to better structure the implementation. |
Make sense.
Yes, and actually I think it make few difference to performance after experiment before (some steps are improved like removing slice of record batch, removing Vec resizing, and some steps are regressed like we need to perform 2 index op, and finally near to no difference will be made), and just a better memory management approach. |
ariel-miculas
commented
Jun 19, 2026
I disagree, since the memory management is directly tied to performance via the spilling mechanism when running with memory limits configured. See #22526 (comment)
So I believe the new "blocked" approach will have significant performance improvements in production-like workloads. |
2010YOUY01
commented
Jun 20, 2026
I agree we could proceed first without worrying too much about the benchmark numbers. This is like a tradeoff between micro-optimizations and algorithmic improvements to memory efficiency. I think completely giving up 10%-ish performance for architectural win is already a good idea. But realistically, I also believe it should be possible to avoid the regressions entirely with some low-level optimizations, but we'd better discuss those opportunities later. |
Good point, no difference to performance is maybe just for benchmark. |
adriangb
commented
Jun 20, 2026
We can run benchmarks with memory limits to force spilling if that helps |
Thank you for opening this pull request! Reviewer note: cargo-semver-checks reported the current version number is not SemVer-compatible with the changes in this pull request (compared against the base branch). Details |
Rachelint
commented
Jun 23, 2026
@adriangb hello, is it possible to authorize me to trigger benchmark through bot? |
alamb
commented
Jun 23, 2026
adriangb
commented
Jun 23, 2026
Rachelint
commented
Jun 24, 2026
Rachelint
commented
Jun 24, 2026
run benchmarks clickbench_partitioned |
adriangbot
commented
Jun 24, 2026
🤖 Benchmark running (GKE) | trigger CPU Details (lscpu)Comparing intermeidate-result-blocked-approach (5869167) to a27f030 (merge-base) diff using: clickbench_partitioned File an issue against this benchmark runner |
adriangbot
commented
Jun 24, 2026
🤖 Benchmark completed (GKE) | trigger Instance: CPU Details (lscpu)DetailsResource Usageclickbench_partitioned — base (merge-base)
clickbench_partitioned — branch
File an issue against this benchmark runner |
| /// - [`EmitTo::NextBlock`]: pops a single block. | ||
| pub fn emit(&mut self, emit_to: EmitTo) -> Result<Vec<T>> { | ||
| match emit_to { | ||
| EmitTo::All => self.inner.pop_block().ok_or_else(|| { |
There was a problem hiding this comment.
this doesn't seem to match the above description:
[
EmitTo::All]: drains every block via repeatedpop_blockand
concatenates the results into a singleVec<T>.
Which issue does this PR close?
Rationale for this change
As mentioned in #7065 , we use a single
Vecto manageaggregation intermediate resultsboth inGroupAccumulatorandGroupValues.It is simple but not efficient enough in high-cardinality aggregation, because when
Vecis not large enough, we need to allocate a newVecand copy all data from the old one.So this pr introduces a
blocked approachto manage theaggregation intermediate results. We will never resize theVecin the approach, and instead we split the data to blocks, when the capacity is not enough, we just allocate a new block. Detail can see #7065What changes are included in this PR?
PrimitiveGroupsAccumulatorandGroupValuesPrimitiveas the exampleAre these changes tested?
Test by exist tests. And new unit tests, new fuzzy tests.
Are there any user-facing changes?
Two functions are added to
GroupValuesandGroupAccumulatortrait.But as you can see, there are default implementations for them, and users can choose to really support the blocked approach when wanting a better performance for their
udafs.