Skip to content

perf: direct linear scan for implicit-whole-table-group aggregates (#633) - #637

Merged
iheitlager merged 2 commits into
mainfrom
perf/633-direct-agg-scan
Aug 29, 2026
Merged

perf: direct linear scan for implicit-whole-table-group aggregates (#633)#637
iheitlager merged 2 commits into
mainfrom
perf/633-direct-agg-scan

Conversation

@iheitlager

Copy link
Copy Markdown
Member

Summary

  • compile_grouped_scan unconditionally opened a Sorter even for aggregates with no GROUP BY key at all (feat: support aggregates with no GROUP BY (implicit whole-table group) #287's implicit whole-table group) — every WHERE-matching row paid a MakeRecord/SorterInsert/SorterSort/SorterData round trip for nothing to sort by.
  • Adds try_compile_direct_agg_scan (src/codegen/select/aggregate.rs): a new fast-path tier between the existing try_compile_index_only_count/try_compile_index_only_sum narrow cases and the compile_grouped_scan fallback, wired into dispatch in src/codegen/select/entry.rs.
  • Fires whenever no aggregate call uses DISTINCT (the only case it declines): a single Rewind/Next scan folds matching rows via AggStep inline, no Sorter opcode at all. Reuses flush_group/AggSlot/compile_limit_setup directly, so HAVING, LIMIT/OFFSET, and feat: support aggregates with no GROUP BY (implicit whole-table group) #287's "zero-row table still flushes one row" behavior are unchanged.

Verified via EXPLAIN on the agg_subquery benchmark query that the outer count(*) scan now compiles with zero Sorter opcodes (the remaining SorterOpen/SorterInsert/SorterSort belongs to the inner, separately-compiled avg(x) subquery — out of scope here).

Test plan

  • cargo test --test codegen_select (40/40, including aggregate_without_group_by_implicit_whole_table_group and having_without_group_by_filters_the_implicit_group)
  • make test (full suite, 0 failures)
  • make lint (clippy + fmt clean)
  • EXPLAIN inspection confirms no Sorter opcode on the direct-agg-scan path
  • Benchmark spot-check (cargo bench --bench engine -- agg_subquery --quick): 50MB fixture ~255ms (down from the issue's reported ~275ms baseline; oracle ~15ms) — modest improvement since the outer count(*) was only part of the gap; the inner avg(x) subquery's own Sorter (unindexed column, separate compile path) is now the dominant remaining cost, tracked separately

spend: matched estimate (medium)

Closes #633

)

compile_grouped_scan unconditionally opened a Sorter even for aggregates
with no GROUP BY key at all (#287's implicit whole-table group), paying a
MakeRecord/SorterInsert/SorterSort/SorterData round trip per WHERE-matching
row for nothing to sort by.

Adds try_compile_direct_agg_scan: a new fast-path tier between the existing
try_compile_index_only_count/sum narrow cases and the compile_grouped_scan
fallback. Fires whenever no aggregate call uses DISTINCT, doing a single
Rewind/Next scan with AggStep folded in inline, reusing flush_group/AggSlot/
compile_limit_setup so HAVING, LIMIT/OFFSET, and #287's zero-row-still-
flushes-one-row behavior are unchanged.

spend: matched estimate (medium)
@iheitlager
iheitlager force-pushed the perf/633-direct-agg-scan branch from 754a5ef to c61b263 Compare August 29, 2026 08:50
No version bump — staying on 0.18.7 per the just-combined 0.18.7/0.18.8
release (#638).
@iheitlager
iheitlager merged commit 71ec693 into main Aug 29, 2026
6 checks passed
@iheitlager
iheitlager deleted the perf/633-direct-agg-scan branch August 29, 2026 08:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: implicit-whole-table-group aggregates buffer through a pointless Sorter

1 participant