[refine](function) enforce final on CRTP aggregate function derived classes to enable compiler devirtualization#62433
Conversation
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
run buildall |
|
run cloud_p0 |
|
/review |
There was a problem hiding this comment.
Findings:
be/src/exprs/aggregate/aggregate_function.h: the newstatic_assertis described as a correctness requirement, butIAggregateFunctionHelperstill calls virtual methods such asadd()andget_return_type(). That means subclasses likeAggregateStateMergeandAggregateFunctionForEachV2already dispatch correctly today; the new opt-out marker preserves those non-final hierarchies instead of proving the claimed invariant. As written, this change enforces a style/perf preference while documenting it as a correctness guarantee, which is misleading and likely to cause churn the next time a legitimate helper hierarchy is added.
Critical checkpoints:
- Goal / correctness / tests: The goal is to guard CRTP aggregate helpers, but the current code does not actually establish the claimed correctness property. No test was added, so there is no proof that the asserted invariant matches runtime behavior.
- Minimality / focus: The refactor stays focused in aggregate helpers, but it broadens the contract beyond what the helper actually requires.
- Concurrency: No new concurrency or lock-order changes in this PR.
- Lifecycle / static initialization: No special lifecycle or static initialization risk observed in the touched code.
- Configuration: No configuration changes.
- Compatibility: No FE/BE protocol or storage-format compatibility changes.
- Parallel code paths: The touched helper is shared by many aggregate implementations; because the new rule is global, its stated invariant needs to be exact. It is not exact today because the allowed non-final hierarchies still rely on virtual dispatch.
- Conditional checks: The new conditional (
is_final || opt-out marker) lacks a precise justification matching the actual dispatch semantics. - Test coverage: No unit or regression coverage was added.
- Observability: Not applicable.
- Transaction / persistence / data writes: Not applicable.
- FE/BE variable passing: Not applicable.
- Performance: Marking leaves
finalmay help optimization, but that is a performance hint, not the correctness guard claimed in the new comment and PR description. - Other issues: None beyond the mismatch above.
Summary opinion: the cleanup/refactor direction is reasonable, but I do not think the new static_assert should be merged in its current form because its documented invariant is stronger than what the helper actually guarantees.
final on CRTP aggregate function derived classes to enable compiler devirtualization
|
run buildall |
|
run beut |
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
|
PR approved by at least one committer and no changes requested. |
|
PR approved by anyone and no changes requested. |
477982f to
b16f8ef
Compare
|
/review |
There was a problem hiding this comment.
No blocking issues found in this PR after reviewing the aggregate-function helper refactor and the affected inheritance chains.
Critical checkpoint conclusions:
- Goal / correctness / proof: The goal is to enforce
finalon CRTP aggregate-function implementations so the hot helper paths (add_batch,add_batch_single_place, etc.) can devirtualizeadd()when the concrete type is known. The code change accomplishes that for the direct CRTP users it updates, and the opt-out marker cleanly preserves the intentional inheritance-based adaptors (AggregateFunctionForEach,AggregateStateUnion). There is no dedicated test, but this change is compile-time enforcement rather than runtime logic. - Scope / minimality: The change is focused and reasonably small for the breadth of aggregate implementations it touches. The only structural refactors (
TopNBase,PercentileApproxBase) are narrowly scoped to make the concrete leaf classes usable as the CRTPDerivedtype. - Concurrency: I did not find new concurrency behavior here. The modified code stays in aggregate-function type plumbing and per-row/per-batch execution helpers; no new shared mutable state, locks, or lock-order concerns were introduced.
- Lifecycle / static initialization: No special lifecycle or static-initialization-order risk was introduced by this PR. The new marker base is inert, and the constructor-body
static_assertis a compile-time check only. - Configuration: No new configuration items are added.
- Compatibility: No FE/BE protocol, symbol-compatibility, or storage-format compatibility change is introduced.
- Parallel code paths: The refactor updates the relevant direct CRTP aggregate implementations in this area, and the explicit opt-out for the known inheritance hierarchies makes the remaining behavior intentional rather than accidental.
- Special conditional checks: The only new condition is the
static_assertpredicate. The surrounding comment clearly explains whyfinalis required for optimization and why the non-final marker exists. - Test coverage: No new test coverage was added. For this compile-time-only refactor that is acceptable, although a full BE build is still the main verification signal.
- Test results / outputs: No test result files were changed.
- Observability: No new runtime path or observability requirement is introduced.
- Transactions / persistence / writes: Not applicable; this PR does not touch transaction, persistence, or data-write semantics.
- FE-BE variable passing: Not applicable.
- Performance: The intended optimization direction is sound. Using the final concrete type as the CRTP
Derivedparameter in the updated classes should let the compiler devirtualize hotadd()calls more reliably. - Other issues: I did not identify any blocking correctness, safety, compatibility, or performance regression in the changed code.
Residual risk:
- I did not run a BE build in this review session, so compile verification remains the main follow-up check for the helper-wide
static_assertenforcement.
|
run buildall |
BE UT Coverage ReportIncrement line coverage Increment coverage report
|
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
|
run external |
|
PR approved by at least one committer and no changes requested. |
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
…ization PRs to branch-4.1 - Add 'final' to AggregateFunctionRegrSimple to satisfy the static_assert introduced by apache#62433 - Add change/change_if_less/change_if_greater methods to SingleValueDataComplexType to match the interface expected by aggregate_function_min_max_impl.h introduced by apache#61179
### What problem does this PR solve? ## Picked PRs - [#58490](#58490) Support max/min agg functions for type_array (prerequisite for #61179) - [#61179](#61179) Reduce the compilation time of aggregate functions - [#61227](#61227) Reduce template instantiation in scan_operator - [#61349](#61349) Reduce hash join build template instantiations - [#61515](#61515) Reduce template instantiations in datetime floor/ceil functions - [#61606](#61606) Optimize the compilation time of aggregate_function_orthogonal_bitmap.cpp - [#61276](#61276) Optimize the compilation time of cast - [#61232](#61232) Reduce window function template instantiations - [#62047](#62047) Reduce compile time of aggregate_function_reader_replace.cpp - [#62433](#62433) Enforce `final` on CRTP aggregate function derived classes to enable compiler devirtualization ### Release note None ### Check List (For Author) - Test <!-- At least one of them must be included. --> - [ ] Regression test - [ ] Unit Test - [ ] Manual test (add detailed scripts or steps below) - [ ] No need to test or manual test. Explain why: - [ ] This is a refactor/code format and no logic has been changed. - [ ] Previous test can cover this change. - [ ] No code files have been changed. - [ ] Other reason <!-- Add your reason? --> - Behavior changed: - [ ] No. - [ ] Yes. <!-- Explain the behavior change --> - Does this need documentation? - [ ] No. - [ ] Yes. <!-- Add document PR link here. eg: apache/doris-website#1214 --> ### Check List (For Reviewer who merge this PR) - [ ] Confirm the release note - [ ] Confirm test cases - [ ] Confirm document - [ ] Add branch pick label <!-- Add branch pick label that this PR should merge into --> --------- Co-authored-by: admiring_xm <86338598+wumeibanfa@users.noreply.github.com>
What problem does this PR solve?
Issue Number: N/A
Problem Summary:
IAggregateFunctionHelper<Derived>implements hot aggregate paths such asadd_batch()andadd_batch_single_place()by callingassert_cast<const Derived*>(this)->add(...). Becauseadd()is declaredvirtualinIAggregateFunction, the compiler cannot devirtualize this call unless it can prove thatDerivedhas no further overrides — which requiresDerivedto be markedfinal.This PR enforces this via a
static_assertin theIAggregateFunctionHelperconstructor. Note thatfinalhere is a performance hint only — correctness is not affected, sinceadd()remains virtual and subclasses always dispatch correctly through the vtable regardless. MarkingDerivedasfinalallows the compiler to devirtualize (and potentially inline) theadd()call inside hot aggregation paths.Changes:
static_assertin theIAggregateFunctionHelperconstructor assertingstd::is_final_v<Derived>, so that missingfinalis caught at compile time.AggregateFunctionNonFinalBaseas an opt-out marker for classes that intentionally form inheritance hierarchies (e.g.AggregateStateUnion → AggregateStateMerge,AggregateFunctionForEach → AggregateFunctionForEachV2).finalacrossbe/src/exprs/aggregate/.AggregateFunctionTopNBaseto accept aDerivedtemplate parameter so thatAggregateFunctionTopNandAggregateFunctionTopNArraycan each be markedfinal.AggregateFunctionPercentileApproxintoAggregateFunctionPercentileApproxBase<Derived>so the four concrete specializations can each be markedfinal.Release note
None
Check List (For Author)
Test
Behavior changed:
Does this need documentation?
Check List (For Reviewer who merge this PR)