Skip to content

Fuse filterless lower aggregate partials - #24

Merged
ila merged 7 commits into
mainfrom
codex/filterless-lower-fusion
Sep 2, 2026
Merged

Fuse filterless lower aggregate partials#24
ila merged 7 commits into
mainfrom
codex/filterless-lower-fusion

Conversation

@ila

@ila ila commented Sep 1, 2026

Copy link
Copy Markdown
Member

Thanks for reviewing.

Summary

This PR fuses the two lower, per-privacy-unit partial aggregates used by dp_filterless into one paired aggregate for every SUM or COUNT component. The paired state returns STRUCT(answer, histogram), so the upper filterless aggregate receives the same two partials as before while the lower plan updates only one aggregate state per component.

This is an internal execution optimization. It does not change the public SQL surface, privacy-budget allocation, sampling rule, clipping-bound selection, noise calibration, group suppression, or released result types.

Why

Filterless needs two views of each contribution:

  • answer: rows selected by the user predicate;
  • histogram: rows selected by the fixed, predicate-independent privacy-unit sample.

Previously, the per-PU aggregation computed those views with two independent aggregate states for each rewritten component:

lower GROUP BY (SQL groups, privacy unit)
  filtered partial aggregate(value)  FILTER active
  histogram partial aggregate(value) FILTER sampled

SUM, COUNT, and each internal AVG component therefore repeated aggregate dispatch, state lookup, value conversion, and accumulation. This overhead grows with query width.

The new lower plan performs both updates in one callback:

lower GROUP BY (SQL groups, privacy unit)
  paired partial aggregate(value, active, sampled)
    -> STRUCT(answer, histogram)

The existing upper filterless_sum and filterless_count aggregates still consume separate answer and histogram expressions. The compiler extracts the two struct fields after per-PU aggregation, so the privacy mechanism above this boundary is unchanged.

Semantics preserved

  • The answer partial is updated only when the encoded filter bit is active.
  • The histogram partial is updated only when the privacy unit belongs to the fixed sample.
  • A row may update both partials, one partial, or neither.
  • NULL SUM inputs update neither numeric partial.
  • COUNT(expr) uses an explicit non-NULL marker, while COUNT(*) counts every qualifying row.
  • AVG remains a rewritten SUM plus COUNT and fuses both internal components independently.
  • Integer SUM uses exact HUGEINT accumulation.
  • DECIMAL SUM preserves its scale and exact DECIMAL(38, scale) accumulation.
  • Floating SUM retains the existing approximate signed-magnitude accumulator.
  • The separate active-row count remains because group participation cannot be inferred from SUM or COUNT(expr) when all values are NULL.
  • Group contribution bounding, private partition selection, nonces, and upper noise generation are unchanged.

Implementation

  • Adds one generic paired-state implementation shared by floating SUM, exact integer/DECIMAL SUM, and COUNT.
  • Registers overloaded internal priv_filterless_sum_pair functions for the supported numeric types and a priv_filterless_count_pair function for count markers.
  • Rewrites each lower component to one paired aggregate and extracts its answer and histogram fields for the existing upper aggregate.
  • Removes the obsolete standalone approximate-SUM lower aggregate.
  • Reuses the existing exact conversion helpers and PAC fixed-point scale/magnitude helpers.

PAC aggregate callbacks themselves are not reused: they own 64-lane, arena-backed PAC state, whereas this lower aggregate owns exactly two scalar partials with different activation predicates. Sharing those callbacks would couple unrelated state layouts instead of removing duplication.

Plan and correctness coverage

The filterless SQL suite verifies:

  • rewritten plans contain paired SUM and COUNT aggregates;
  • SUM, COUNT(*), COUNT(expr), and AVG results;
  • NULL input and NULL predicate behavior;
  • exact BIGINT values above the DOUBLE precision boundary;
  • DECIMAL(38, scale) preservation;
  • fixed sampling with dp_filterless_sample_bits=0 and 6;
  • grouped queries and deterministic C_u > 1 top-k contribution bounding;
  • FK-linked privacy units;
  • serial and parallel aggregate combination.

A real four-thread grouped query with C_u=2 also returned the expected BIGINT, DECIMAL, floating SUM, and COUNT values for both retained groups.

Validation

  • Ninja release build
  • focused filterless suite: 155 assertions passed
  • full extension suite: 3,719 assertions passed
  • PAC C++ test runner passed
  • CI passed format, clang-tidy, Linux, macOS, Windows MSVC, Windows MinGW, and Wasm jobs

Performance

Randomized fresh-process SF30 measurements compare this PR with the checkpoint where exact integer and DECIMAL lower partials were still unfused:

Query shape Median change
One native DECIMAL SUM 0.9% faster
Four native DECIMAL SUMs plus COUNT 7.0% faster
One native BIGINT SUM 0.8% faster
Four native BIGINT SUMs plus COUNT 3.7% faster

The benefit increases with aggregate width because fusion removes one lower state and update path per component. The BIGINT measurements had more run-to-run variance, so the table reports conservative medians rather than individual best runs.

A separate ten-pair comparison of five floating/count aggregates against the earlier specialized fused implementation differed by 0.09%. This confirms that replacing separate specialized pair implementations with the shared generic implementation is performance-neutral while substantially reducing code.

@ila
ila marked this pull request as ready for review September 1, 2026 13:01
@ila ila mentioned this pull request Sep 1, 2026
@ila
ila merged commit f53f772 into main Sep 2, 2026
30 checks passed
@ila
ila deleted the codex/filterless-lower-fusion branch September 2, 2026 17:41
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.

1 participant