Skip to content

perf: compact inputs for grouped aggregate filter - #24869

Merged
kosiew merged 6 commits into
apache:mainfrom
lyne7-sc:perf/grouped-aggregate-filter-compaction
Sep 10, 2026
Merged

kosiew merged 6 commits into
apache:mainfrom
lyne7-sc:perf/grouped-aggregate-filter-compaction

Conversation

@lyne7-sc

@lyne7-sc lyne7-sc commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

  • Closes #.

Rationale for this change

Grouped aggregate FILTER currently scatters evaluated arguments back to their original row positions. The accumulator then receives the full-length arguments and group IDs and scans the filter again. This introduces unnecessary scatter and filtering work.

What changes are included in this PR?

  • For regular Partial and Single aggregation, evaluate aggregate arguments using only rows selected by the filter.
  • Compact the corresponding group IDs before updating each accumulator.
  • Reuse the original group IDs when every row is selected.
  • Use bitmap indices or contiguous ranges depending on filter selectivity when compacting group IDs.
  • Preserve group registration using the complete input batch.
  • Handle sparse groups correctly in accumulator null-state tracking.
  • Preserve row-aligned argument evaluation for skip-partial aggregation.
  • Preserve ordered aggregation and empty-grouping-set behavior.

What is the testing strategy for this PR?

Yes. slts cover independent filters, rejected groups, primitive and boolean group accumulators, ordered-input scenarios, and skip-partial aggregation. Unit tests cover group ID compaction and row-aligned skip-partial state conversion.

Are there any user-facing changes?

No. This is an internal performance optimization.

Benchmark

group                                                                                         main                                    optimize
-----                                                                                         ----                                    --------
grouped_aggregate_filter/one_aggregate/1024_groups/column/100_percent                         1.60   291.8±10.49µs        ? ?/sec     1.00    182.2±1.95µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/column/10_percent                          1.74   343.9±60.21µs        ? ?/sec     1.00    197.9±3.64µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/column/1_percent                           1.56   268.6±33.15µs        ? ?/sec     1.00    172.0±3.28µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/column/50_percent                          1.77  639.4±108.17µs        ? ?/sec     1.00   362.2±15.07µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/column/90_percent                          1.00   766.6±44.60µs        ? ?/sec     1.01   774.5±15.30µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/column/99_percent                          1.13    361.9±9.36µs        ? ?/sec     1.00    320.4±3.95µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/column/unfiltered                          1.05   195.7±10.26µs        ? ?/sec     1.00    185.7±5.89µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/multiply/100_percent                       1.47    305.1±5.93µs        ? ?/sec     1.00    207.1±3.59µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/multiply/10_percent                        1.74   342.4±43.21µs        ? ?/sec     1.00    197.2±0.83µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/multiply/1_percent                         1.54   251.8±11.14µs        ? ?/sec     1.00    163.9±2.20µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/multiply/50_percent                        1.77   631.5±22.55µs        ? ?/sec     1.00    356.8±4.79µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/multiply/90_percent                        1.01   797.8±18.16µs        ? ?/sec     1.00   786.3±31.99µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/multiply/99_percent                        1.00   401.4±15.31µs        ? ?/sec     1.04   416.2±28.65µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/1024_groups/multiply/unfiltered                        1.02   217.0±20.33µs        ? ?/sec     1.00   213.6±11.31µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/column/100_percent                           1.61    266.6±7.40µs        ? ?/sec     1.00    165.7±1.61µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/column/10_percent                            1.52    296.1±7.73µs        ? ?/sec     1.00    195.0±7.79µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/column/1_percent                             1.36    220.2±3.91µs        ? ?/sec     1.00    161.5±8.14µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/column/50_percent                            1.62   573.8±13.79µs        ? ?/sec     1.00    353.1±6.08µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/column/90_percent                            1.10   705.5±10.46µs        ? ?/sec     1.00   638.7±12.83µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/column/99_percent                            1.33   348.3±47.72µs        ? ?/sec     1.00   261.5±25.44µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/column/unfiltered                            1.00    163.5±5.24µs        ? ?/sec     1.08   177.4±12.99µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/multiply/100_percent                         2.15  396.8±182.01µs        ? ?/sec     1.00    184.3±2.48µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/multiply/10_percent                          1.64    299.1±8.26µs        ? ?/sec     1.00    182.9±1.63µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/multiply/1_percent                           1.51    224.2±4.13µs        ? ?/sec     1.00    148.5±1.32µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/multiply/50_percent                          1.65   581.6±17.48µs        ? ?/sec     1.00    352.3±8.53µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/multiply/90_percent                          1.28  804.6±120.15µs        ? ?/sec     1.00   626.6±31.91µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/multiply/99_percent                          1.35   382.3±42.57µs        ? ?/sec     1.00   283.2±14.58µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/16_groups/multiply/unfiltered                          1.02   188.6±19.17µs        ? ?/sec     1.00    184.1±3.40µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/column/100_percent                        1.16  1341.0±53.20µs        ? ?/sec     1.00  1152.0±66.17µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/column/10_percent                         1.18  1421.0±52.66µs        ? ?/sec     1.00  1205.4±79.31µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/column/1_percent                          1.14  1333.0±48.42µs        ? ?/sec     1.00  1171.5±57.48µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/column/50_percent                         1.31  1689.3±44.50µs        ? ?/sec     1.00  1294.0±41.36µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/column/90_percent                         1.12  1865.2±90.42µs        ? ?/sec     1.00  1663.3±49.81µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/column/99_percent                         1.12  1484.9±154.95µs        ? ?/sec    1.00  1325.3±33.43µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/column/unfiltered                         1.08  1260.3±101.90µs        ? ?/sec    1.00  1165.3±19.06µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/multiply/100_percent                      1.29  1435.1±158.66µs        ? ?/sec    1.00  1110.8±72.64µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/multiply/10_percent                       1.24  1420.1±92.72µs        ? ?/sec     1.00  1145.4±28.61µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/multiply/1_percent                        1.17  1310.0±62.02µs        ? ?/sec     1.00  1115.0±45.38µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/multiply/50_percent                       1.29  1691.4±86.65µs        ? ?/sec     1.00  1306.5±63.15µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/multiply/90_percent                       1.10  1850.0±76.61µs        ? ?/sec     1.00  1674.2±55.65µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/multiply/99_percent                       1.17  1453.6±42.35µs        ? ?/sec     1.00  1244.7±53.70µs        ? ?/sec
grouped_aggregate_filter/one_aggregate/65536_groups/multiply/unfiltered                       1.05  1260.2±30.72µs        ? ?/sec     1.00  1203.8±22.23µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/1024_groups/multiply/100_percent     1.82   441.4±14.01µs        ? ?/sec     1.00    242.2±4.97µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/1024_groups/multiply/10_percent      1.90    490.2±9.58µs        ? ?/sec     1.00    258.6±7.60µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/1024_groups/multiply/1_percent       1.78   350.7±11.14µs        ? ?/sec     1.00   196.8±15.83µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/1024_groups/multiply/50_percent      1.91  1077.6±21.84µs        ? ?/sec     1.00   563.5±27.23µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/1024_groups/multiply/90_percent      1.13  1458.9±80.33µs        ? ?/sec     1.00  1290.9±37.66µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/1024_groups/multiply/99_percent      1.20   637.5±84.74µs        ? ?/sec     1.00   530.8±15.89µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/1024_groups/multiply/unfiltered      1.01   260.0±20.62µs        ? ?/sec     1.00    257.7±5.63µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/16_groups/multiply/100_percent       1.83   442.6±25.65µs        ? ?/sec     1.00   242.0±18.15µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/16_groups/multiply/10_percent        2.06   491.8±20.88µs        ? ?/sec     1.00    238.4±7.67µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/16_groups/multiply/1_percent         2.04   335.2±13.63µs        ? ?/sec     1.00    164.4±3.26µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/16_groups/multiply/50_percent        1.87  1065.6±20.79µs        ? ?/sec     1.00   569.3±17.13µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/16_groups/multiply/90_percent        1.09  1460.0±145.69µs        ? ?/sec    1.00  1338.8±247.67µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/16_groups/multiply/99_percent        1.53  655.3±122.20µs        ? ?/sec     1.00   428.4±80.90µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/16_groups/multiply/unfiltered        1.00   246.5±15.46µs        ? ?/sec     1.04   256.1±49.50µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/65536_groups/multiply/100_percent    1.22  1479.8±45.71µs        ? ?/sec     1.00  1214.9±43.68µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/65536_groups/multiply/10_percent     1.29  1582.4±189.79µs        ? ?/sec    1.00  1222.4±47.32µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/65536_groups/multiply/1_percent      1.26  1378.2±89.04µs        ? ?/sec     1.00  1097.3±34.69µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/65536_groups/multiply/50_percent     1.62      2.5±0.48ms        ? ?/sec     1.00  1519.1±44.74µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/65536_groups/multiply/90_percent     1.12      2.5±0.10ms        ? ?/sec     1.00      2.2±0.04ms        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/65536_groups/multiply/99_percent     1.27  1884.7±402.52µs        ? ?/sec    1.00  1484.2±45.19µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_distinct_filters/65536_groups/multiply/unfiltered     1.07  1304.4±101.10µs        ? ?/sec    1.00  1217.9±53.10µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/1024_groups/multiply/100_percent        1.96   469.2±45.79µs        ? ?/sec     1.00    239.7±4.70µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/1024_groups/multiply/10_percent         2.40   566.0±94.16µs        ? ?/sec     1.00    236.3±5.20µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/1024_groups/multiply/1_percent          2.02   370.4±44.87µs        ? ?/sec     1.00    183.3±3.44µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/1024_groups/multiply/50_percent         2.07  1103.2±99.60µs        ? ?/sec     1.00   532.8±20.23µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/1024_groups/multiply/90_percent         1.21  1411.1±119.95µs        ? ?/sec    1.00  1165.6±38.46µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/1024_groups/multiply/99_percent         1.36  720.9±194.27µs        ? ?/sec     1.00   528.6±21.47µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/1024_groups/multiply/unfiltered         1.03    245.7±3.38µs        ? ?/sec     1.00    237.9±5.10µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/16_groups/multiply/100_percent          1.85   424.4±20.65µs        ? ?/sec     1.00   229.6±12.86µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/16_groups/multiply/10_percent           2.19   495.7±75.88µs        ? ?/sec     1.00    226.7±2.49µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/16_groups/multiply/1_percent            2.10   340.5±45.25µs        ? ?/sec     1.00    161.8±2.39µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/16_groups/multiply/50_percent           2.00  1038.7±143.30µs        ? ?/sec    1.00    519.7±9.86µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/16_groups/multiply/90_percent           1.33  1290.4±48.84µs        ? ?/sec     1.00   972.3±29.13µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/16_groups/multiply/99_percent           1.80  665.6±231.71µs        ? ?/sec     1.00   369.5±50.72µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/16_groups/multiply/unfiltered           1.00   238.8±24.69µs        ? ?/sec     1.00   239.1±19.73µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/65536_groups/multiply/100_percent       1.24  1643.7±92.99µs        ? ?/sec     1.00  1325.0±155.28µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/65536_groups/multiply/10_percent        1.46  1765.9±118.19µs        ? ?/sec    1.00  1211.7±56.11µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/65536_groups/multiply/1_percent         1.36  1529.3±67.92µs        ? ?/sec     1.00  1127.5±49.69µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/65536_groups/multiply/50_percent        1.62      2.4±0.26ms        ? ?/sec     1.00  1507.6±57.98µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/65536_groups/multiply/90_percent        1.11      2.5±0.28ms        ? ?/sec     1.00      2.3±0.07ms        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/65536_groups/multiply/99_percent        1.10  1699.1±153.04µs        ? ?/sec    1.00  1550.3±158.97µs        ? ?/sec
grouped_aggregate_filter/two_aggregates_shared_filter/65536_groups/multiply/unfiltered        1.19  1476.5±124.38µs        ? ?/sec    1.00  1237.4±47.13µs        ? ?/sec

@github-actions github-actions Bot added sqllogictest SQL Logic Tests (.slt) functions Changes to functions implementation physical-plan Changes to the physical-plan crate labels Sep 2, 2026
@codecov-commenter

codecov-commenter commented Sep 2, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.17073% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.74%. Comparing base (a5c809f) to head (4b5d115).

Files with missing lines Patch % Lines
...plan/src/aggregates/aggregate_hash_table/common.rs 92.89% 1 Missing and 11 partials ⚠️
...c/aggregates/aggregate_hash_table/partial_table.rs 83.33% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #24869    +/-   ##
========================================
  Coverage   81.74%   81.74%            
========================================
  Files        1128     1128            
  Lines      416644   416803   +159     
  Branches   416644   416803   +159     
========================================
+ Hits       340592   340736   +144     
  Misses      55995    55995            
- Partials    20057    20072    +15     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@kosiew kosiew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lyne7-sc, thanks for working on this. The changes look good overall. I have one non-blocking suggestion around SQL-level coverage for NULL filter predicates.


# Each aggregate must compact group indices using its own FILTER. Cover both
# primitive and boolean group accumulators, including groups rejected by both.
query IIB

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a SQL-level test where the FILTER predicate contains NULL values, ideally with a fallible aggregate argument? The helper unit test covers NULLs in the bitmap, but the new true_count -> filter_record_batch path is not covered end to end. I think a test here would be useful to verify that NULL is treated as not selected and that the compacted group IDs stay aligned with the aggregate arguments.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! I've added an SLT covering NULL filter predicates with a fallible aggregate argument and interleaved groups to verify that compacted group IDs stay aligned.

@kosiew

kosiew commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

🚀
@lyne7-sc
Thank you for your contribution.

@kosiew
kosiew added this pull request to the merge queue Sep 9, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to no response for status checks Sep 9, 2026
@kosiew
kosiew added this pull request to the merge queue Sep 10, 2026
Merged via the queue into apache:main with commit 4a1fe81 Sep 10, 2026
41 checks passed
@lyne7-sc
lyne7-sc deleted the perf/grouped-aggregate-filter-compaction branch September 13, 2026 05:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

functions Changes to functions implementation physical-plan Changes to the physical-plan crate sqllogictest SQL Logic Tests (.slt) v56.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants