Skip to content

fix: skip SingleDistinctToGroupBy rewrite when single_distinct_prefer_native is set (closes #25932) - #26000

Open
mohitgurav20 wants to merge 41 commits into
apache:mainfrom
mohitgurav20:fix/single-distinct-groupby-memory-usage
Open

mohitgurav20 wants to merge 41 commits into
apache:mainfrom
mohitgurav20:fix/single-distinct-groupby-memory-usage

Conversation

@mohitgurav20

@mohitgurav20 mohitgurav20 commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Closes #25932.

Rationale for this change

In queries with a single distinct aggregation, SingleDistinctToGroupBy rewrites the plan into a two-phase GROUP BY. While this enables partition parallelism, it materializes intermediate (group, distinct_value) pairs, increasing peak memory when there are few groups with many distinct values each (~538–599 MiB vs ~306 MiB in unconstrained memory runs on #25932).

PR #25988 previously introduced datafusion.optimizer.single_distinct_prefer_native for COUNT(DISTINCT int). Per review feedback from @mkleen and @jayzhan211, this PR consolidates both into datafusion.optimizer.single_distinct_prefer_native rather than adding a second knob.

At target_partitions = 1, native GroupsAccumulator execution avoids the two-phase grouping overhead and reduces peak memory for queries with few large groups on fixed-width types (e.g. sum(DISTINCT int) and count(DISTINCT int)). When target_partitions > 1, queries continue using the two-phase rewrite to maintain partition fan-out and support spilling under memory limits.

Additionally, queries with unsupported distinct aggregates alongside non-distinct aggregates (such as SELECT g, count(*), stddev(DISTINCT x) ... GROUP BY g) now rewrite correctly at default settings instead of erroring at runtime as on main.

What changes are included in this PR?

  1. Consolidated Configuration Knob:

    • Extended datafusion.optimizer.single_distinct_prefer_native: bool (default false) to govern native single distinct execution.
    • Documented trade-offs: lowers peak memory with few large groups; under memory limits or with many small groups, queries may require more memory or need spilling.
  2. Opt-in Native Execution Gating:

    • When single_distinct_prefer_native = true and target_partitions == 1, skips the two-phase rewrite if all distinct aggregate arguments are fixed-width primitive types (integers, dates, decimals) supported natively.
    • Excludes floating-point types (f32, f64) to preserve IEEE 754 -0.0 == 0.0 semantics.
    • Excludes Duration types (since avg lacks a native distinct accumulator for durations).
    • Retains the two-phase rewrite when target_partitions > 1, non-distinct companion aggregates are present, or distinct handling is unsupported (approx_median, stddev).
  3. Documentation & Schema Synchronization:

    • Synchronized docs/source/user-guide/configs.md and information_schema.slt.

Are these changes tested?

Yes:

  • Unit tests in single_distinct_to_groupby.rs covering flag states, partition gating (target_partitions == 1 vs > 1), float argument exclusions, and unsupported aggregate fallbacks.
  • Sqllogictests in single_distinct_to_groupby.slt covering plans, execution, duration distinct handling, and stddev(DISTINCT x) default execution.
  • All optimizer and sqllogictests pass cleanly.

Are there any user-facing changes?

Yes:

  • Expands datafusion.optimizer.single_distinct_prefer_native to cover other fixed-width distinct aggregates like sum(DISTINCT int).
  • Queries like SELECT g, count(*), stddev(DISTINCT x) ... GROUP BY g that previously errored on main now succeed at default settings.

@github-actions github-actions Bot added the optimizer Optimizer rules label Oct 3, 2026
@mohitgurav20
mohitgurav20 force-pushed the fix/single-distinct-groupby-memory-usage branch from 527a544 to 9e8dfe6 Compare October 4, 2026 16:36
@github-actions github-actions Bot added the core Core DataFusion crate label Oct 4, 2026

@mkleen mkleen 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.

@mohitgurav20 Thanks for working on this.
Can you make sure all sql-logic tests pass, or correct them if invalid.
You can run them locally with:

cargo test --profile=ci --test sqllogictests

@github-actions github-actions Bot added the sqllogictest SQL Logic Tests (.slt) label Oct 9, 2026
@codecov-commenter

codecov-commenter commented Oct 10, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.53541% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.94%. Comparing base (3ee3e39) to head (38468c5).
⚠️ Report is 29 commits behind head on main.

Files with missing lines Patch % Lines
...fusion/optimizer/src/single_distinct_to_groupby.rs 87.50% 3 Missing and 41 partials ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #26000    +/-   ##
========================================
  Coverage   82.93%   82.94%            
========================================
  Files        1154     1154            
  Lines      458002   458300   +298     
  Branches   458002   458300   +298     
========================================
+ Hits       379856   380130   +274     
+ Misses      55123    55104    -19     
- Partials    23023    23066    +43     

☔ 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.

@mohitgurav20
mohitgurav20 force-pushed the fix/single-distinct-groupby-memory-usage branch from 63e9873 to 0c76607 Compare October 10, 2026 19:06
@mohitgurav20
mohitgurav20 requested a review from mkleen October 11, 2026 05:11
@mohitgurav20

Copy link
Copy Markdown
Contributor Author

@mkleen sir i have completed the work and addressed ur suggestion pls feel free to review and would like to work if any changes needed.

@mkleen

mkleen commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

run benchmarks

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6114216591-3306-6nkh2 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing fix/single-distinct-groupby-memory-usage (0923424) to 334f293 (merge-base) diff

Run configuration
run benchmark clickbench_partitioned

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6114216591-3307-dzrvx 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing fix/single-distinct-groupby-memory-usage (0923424) to 334f293 (merge-base) diff

Run configuration
run benchmark tpcds

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6114216591-3308-fcbjc 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing fix/single-distinct-groupby-memory-usage (0923424) to 334f293 (merge-base) diff

Run configuration
run benchmark tpch

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing fix/single-distinct-groupby-memory-usage (0923424) to 334f293 (merge-base) diff

Run configuration
run benchmark tpch
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark tpch_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃     HEAD ┃ fix_single-distinct-groupby-memory-usage ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │ 40.29 ms │                                 40.32 ms │     no change │
│ QQuery 2  │ 18.13 ms │                                 18.16 ms │     no change │
│ QQuery 3  │ 28.23 ms │                                 28.28 ms │     no change │
│ QQuery 4  │ 17.14 ms │                                 17.06 ms │     no change │
│ QQuery 5  │ 35.12 ms │                                 35.57 ms │     no change │
│ QQuery 6  │ 13.35 ms │                                 13.56 ms │     no change │
│ QQuery 7  │ 42.15 ms │                                 41.29 ms │     no change │
│ QQuery 8  │ 40.83 ms │                                 41.29 ms │     no change │
│ QQuery 9  │ 50.50 ms │                                 50.92 ms │     no change │
│ QQuery 10 │ 40.28 ms │                                 40.46 ms │     no change │
│ QQuery 11 │ 13.26 ms │                                 13.27 ms │     no change │
│ QQuery 12 │ 18.03 ms │                                 18.43 ms │     no change │
│ QQuery 13 │ 39.16 ms │                                 38.90 ms │     no change │
│ QQuery 14 │ 24.47 ms │                                 24.44 ms │     no change │
│ QQuery 15 │ 30.29 ms │                                 30.73 ms │     no change │
│ QQuery 16 │ 13.63 ms │                                 11.66 ms │ +1.17x faster │
│ QQuery 17 │ 73.99 ms │                                 73.56 ms │     no change │
│ QQuery 18 │ 45.29 ms │                                 45.11 ms │     no change │
│ QQuery 19 │ 31.51 ms │                                 31.65 ms │     no change │
│ QQuery 20 │ 31.36 ms │                                 31.67 ms │     no change │
│ QQuery 21 │ 54.39 ms │                                 54.99 ms │     no change │
│ QQuery 22 │ 13.54 ms │                                 13.63 ms │     no change │
└───────────┴──────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 714.94ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 714.93ms │
│ Average Time (HEAD)                                     │  32.50ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │  32.50ms │
│ Queries Faster                                          │        1 │
│ Queries Slower                                          │        0 │
│ Queries with No Change                                  │       21 │
│ Queries with Failure                                    │        0 │
└─────────────────────────────────────────────────────────┴──────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark tpch_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃                           HEAD ┃ fix_single-distinct-groupby-memory-usage ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │ 40.29 / 40.97 ±1.06 / 43.07 ms │           40.32 / 41.00 ±0.96 / 42.90 ms │     no change │
│ QQuery 2  │ 18.13 / 18.38 ±0.33 / 18.98 ms │           18.16 / 18.50 ±0.29 / 18.98 ms │     no change │
│ QQuery 3  │ 28.23 / 28.63 ±0.30 / 29.12 ms │           28.28 / 28.61 ±0.23 / 28.91 ms │     no change │
│ QQuery 4  │ 17.14 / 17.56 ±0.60 / 18.74 ms │           17.06 / 17.30 ±0.19 / 17.62 ms │     no change │
│ QQuery 5  │ 35.12 / 36.10 ±1.42 / 38.90 ms │           35.57 / 35.67 ±0.07 / 35.80 ms │     no change │
│ QQuery 6  │ 13.35 / 13.81 ±0.56 / 14.90 ms │           13.56 / 14.42 ±0.87 / 15.93 ms │     no change │
│ QQuery 7  │ 42.15 / 42.74 ±0.69 / 43.78 ms │           41.29 / 43.01 ±1.29 / 44.83 ms │     no change │
│ QQuery 8  │ 40.83 / 42.21 ±2.10 / 46.38 ms │           41.29 / 42.71 ±2.33 / 47.35 ms │     no change │
│ QQuery 9  │ 50.50 / 51.06 ±0.54 / 51.88 ms │           50.92 / 51.07 ±0.17 / 51.38 ms │     no change │
│ QQuery 10 │ 40.28 / 42.76 ±3.92 / 50.57 ms │           40.46 / 40.79 ±0.21 / 41.11 ms │     no change │
│ QQuery 11 │ 13.26 / 13.65 ±0.36 / 14.27 ms │           13.27 / 13.77 ±0.65 / 15.05 ms │     no change │
│ QQuery 12 │ 18.03 / 18.38 ±0.34 / 19.02 ms │           18.43 / 18.66 ±0.17 / 18.90 ms │     no change │
│ QQuery 13 │ 39.16 / 39.35 ±0.10 / 39.43 ms │           38.90 / 39.73 ±1.25 / 42.18 ms │     no change │
│ QQuery 14 │ 24.47 / 24.61 ±0.08 / 24.69 ms │           24.44 / 24.54 ±0.09 / 24.69 ms │     no change │
│ QQuery 15 │ 30.29 / 30.71 ±0.32 / 31.22 ms │           30.73 / 31.08 ±0.35 / 31.72 ms │     no change │
│ QQuery 16 │ 13.63 / 13.87 ±0.15 / 14.10 ms │           11.66 / 12.02 ±0.22 / 12.31 ms │ +1.15x faster │
│ QQuery 17 │ 73.99 / 74.37 ±0.23 / 74.61 ms │           73.56 / 74.20 ±0.38 / 74.60 ms │     no change │
│ QQuery 18 │ 45.29 / 45.54 ±0.21 / 45.89 ms │           45.11 / 46.05 ±0.96 / 47.78 ms │     no change │
│ QQuery 19 │ 31.51 / 31.91 ±0.34 / 32.51 ms │           31.65 / 33.01 ±1.31 / 35.23 ms │     no change │
│ QQuery 20 │ 31.36 / 31.78 ±0.46 / 32.62 ms │           31.67 / 31.92 ±0.26 / 32.39 ms │     no change │
│ QQuery 21 │ 54.39 / 55.74 ±1.66 / 58.89 ms │           54.99 / 56.49 ±0.98 / 57.55 ms │     no change │
│ QQuery 22 │ 13.54 / 13.71 ±0.13 / 13.91 ms │           13.63 / 13.91 ±0.22 / 14.16 ms │     no change │
└───────────┴────────────────────────────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 727.85ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 728.44ms │
│ Average Time (HEAD)                                     │  33.08ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │  33.11ms │
│ Queries Faster                                          │        1 │
│ Queries Slower                                          │        0 │
│ Queries with No Change                                  │       21 │
│ Queries with Failure                                    │        0 │
└─────────────────────────────────────────────────────────┴──────────┘

Resource Usage

tpch — base (merge-base)

Metric Value
Wall time 5.0s
Peak memory 1.1 GiB
Avg memory 473.9 MiB
CPU user 19.8s
CPU sys 1.7s
Peak spill 0 B

tpch — branch

Metric Value
Wall time 5.0s
Peak memory 1.1 GiB
Avg memory 478.5 MiB
CPU user 19.9s
CPU sys 1.6s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing fix/single-distinct-groupby-memory-usage (0923424) to 334f293 (merge-base) diff

Run configuration
run benchmark tpcds
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark tpcds_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃       HEAD ┃ fix_single-distinct-groupby-memory-usage ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │    5.42 ms │                                  5.48 ms │     no change │
│ QQuery 2  │   79.52 ms │                                 79.63 ms │     no change │
│ QQuery 3  │   28.07 ms │                                 28.11 ms │     no change │
│ QQuery 4  │  428.64 ms │                                427.69 ms │     no change │
│ QQuery 5  │   49.27 ms │                                 49.56 ms │     no change │
│ QQuery 6  │   34.13 ms │                                 34.40 ms │     no change │
│ QQuery 7  │   71.83 ms │                                 71.95 ms │     no change │
│ QQuery 8  │   35.53 ms │                                 35.81 ms │     no change │
│ QQuery 9  │   50.74 ms │                                 50.77 ms │     no change │
│ QQuery 10 │   61.74 ms │                                 61.42 ms │     no change │
│ QQuery 11 │  261.04 ms │                                264.22 ms │     no change │
│ QQuery 12 │   28.34 ms │                                 28.31 ms │     no change │
│ QQuery 13 │  111.88 ms │                                111.92 ms │     no change │
│ QQuery 14 │  382.80 ms │                                380.70 ms │     no change │
│ QQuery 15 │   51.27 ms │                                 52.04 ms │     no change │
│ QQuery 16 │    6.20 ms │                                  5.51 ms │ +1.12x faster │
│ QQuery 17 │   78.98 ms │                                 77.83 ms │     no change │
│ QQuery 18 │  102.77 ms │                                103.49 ms │     no change │
│ QQuery 19 │   40.90 ms │                                 40.78 ms │     no change │
│ QQuery 20 │   35.30 ms │                                 35.07 ms │     no change │
│ QQuery 21 │   16.52 ms │                                 16.40 ms │     no change │
│ QQuery 22 │   62.15 ms │                                 62.80 ms │     no change │
│ QQuery 23 │  299.29 ms │                                299.83 ms │     no change │
│ QQuery 24 │  189.60 ms │                                189.43 ms │     no change │
│ QQuery 25 │  107.82 ms │                                106.95 ms │     no change │
│ QQuery 26 │   45.49 ms │                                 45.27 ms │     no change │
│ QQuery 27 │    5.79 ms │                                  5.81 ms │     no change │
│ QQuery 28 │   55.00 ms │                                 60.08 ms │  1.09x slower │
│ QQuery 29 │   94.14 ms │                                 95.50 ms │     no change │
│ QQuery 30 │   32.64 ms │                                 31.94 ms │     no change │
│ QQuery 31 │  105.84 ms │                                107.09 ms │     no change │
│ QQuery 32 │   20.02 ms │                                 19.43 ms │     no change │
│ QQuery 33 │   35.91 ms │                                 36.15 ms │     no change │
│ QQuery 34 │    9.69 ms │                                  9.76 ms │     no change │
│ QQuery 35 │   70.62 ms │                                 70.50 ms │     no change │
│ QQuery 36 │    5.51 ms │                                  5.43 ms │     no change │
│ QQuery 37 │    6.57 ms │                                  6.52 ms │     no change │
│ QQuery 38 │   60.02 ms │                                 59.40 ms │     no change │
│ QQuery 39 │   71.53 ms │                                 71.64 ms │     no change │
│ QQuery 40 │   23.38 ms │                                 22.17 ms │ +1.05x faster │
│ QQuery 41 │   10.97 ms │                                 11.04 ms │     no change │
│ QQuery 42 │   22.91 ms │                                 22.91 ms │     no change │
│ QQuery 43 │    4.63 ms │                                  4.69 ms │     no change │
│ QQuery 44 │    8.50 ms │                                  8.59 ms │     no change │
│ QQuery 45 │   35.60 ms │                                 35.13 ms │     no change │
│ QQuery 46 │   11.45 ms │                                 11.43 ms │     no change │
│ QQuery 47 │  214.57 ms │                                215.34 ms │     no change │
│ QQuery 48 │   90.01 ms │                                 89.35 ms │     no change │
│ QQuery 49 │   68.33 ms │                                 68.31 ms │     no change │
│ QQuery 50 │   58.02 ms │                                 57.54 ms │     no change │
│ QQuery 51 │   92.75 ms │                                 91.56 ms │     no change │
│ QQuery 52 │   23.21 ms │                                 23.49 ms │     no change │
│ QQuery 53 │   28.69 ms │                                 28.84 ms │     no change │
│ QQuery 54 │   52.63 ms │                                 52.30 ms │     no change │
│ QQuery 55 │   22.69 ms │                                 22.85 ms │     no change │
│ QQuery 56 │   36.60 ms │                                 37.39 ms │     no change │
│ QQuery 57 │  165.20 ms │                                164.97 ms │     no change │
│ QQuery 58 │  106.04 ms │                                105.01 ms │     no change │
│ QQuery 59 │  114.61 ms │                                113.98 ms │     no change │
│ QQuery 60 │   37.64 ms │                                 37.96 ms │     no change │
│ QQuery 61 │   10.79 ms │                                 10.84 ms │     no change │
│ QQuery 62 │   42.55 ms │                                 42.82 ms │     no change │
│ QQuery 63 │   28.77 ms │                                 28.52 ms │     no change │
│ QQuery 64 │  356.97 ms │                                359.36 ms │     no change │
│ QQuery 65 │  126.65 ms │                                130.29 ms │     no change │
│ QQuery 66 │   74.08 ms │                                 74.02 ms │     no change │
│ QQuery 67 │  241.97 ms │                                244.49 ms │     no change │
│ QQuery 68 │   11.64 ms │                                 11.58 ms │     no change │
│ QQuery 69 │   56.34 ms │                                 55.62 ms │     no change │
│ QQuery 70 │  102.86 ms │                                102.13 ms │     no change │
│ QQuery 71 │   34.36 ms │                                 34.06 ms │     no change │
│ QQuery 72 │ 1561.27 ms │                               1516.53 ms │     no change │
│ QQuery 73 │    9.63 ms │                                  9.60 ms │     no change │
│ QQuery 74 │  154.31 ms │                                150.74 ms │     no change │
│ QQuery 75 │  138.10 ms │                                139.11 ms │     no change │
│ QQuery 76 │   33.01 ms │                                 34.15 ms │     no change │
│ QQuery 77 │   59.29 ms │                                 59.03 ms │     no change │
│ QQuery 78 │  158.53 ms │                                157.71 ms │     no change │
│ QQuery 79 │   65.74 ms │                                 65.46 ms │     no change │
│ QQuery 80 │   93.20 ms │                                 93.85 ms │     no change │
│ QQuery 81 │   25.50 ms │                                 25.44 ms │     no change │
│ QQuery 82 │   16.23 ms │                                 16.15 ms │     no change │
│ QQuery 83 │   32.69 ms │                                 32.19 ms │     no change │
│ QQuery 84 │   29.18 ms │                                 29.26 ms │     no change │
│ QQuery 85 │   97.46 ms │                                 97.39 ms │     no change │
│ QQuery 86 │   24.72 ms │                                 24.81 ms │     no change │
│ QQuery 87 │   59.74 ms │                                 59.54 ms │     no change │
│ QQuery 88 │   59.18 ms │                                 58.74 ms │     no change │
│ QQuery 89 │   34.50 ms │                                 34.39 ms │     no change │
│ QQuery 90 │   16.12 ms │                                 15.94 ms │     no change │
│ QQuery 91 │   40.03 ms │                                 40.47 ms │     no change │
│ QQuery 92 │   28.54 ms │                                 29.21 ms │     no change │
│ QQuery 93 │   48.37 ms │                                 48.81 ms │     no change │
│ QQuery 94 │   37.39 ms │                                 36.85 ms │     no change │
│ QQuery 95 │   78.35 ms │                                 78.76 ms │     no change │
│ QQuery 96 │   23.04 ms │                                 23.29 ms │     no change │
│ QQuery 97 │   49.44 ms │                                 51.90 ms │     no change │
│ QQuery 98 │   41.95 ms │                                 41.46 ms │     no change │
│ QQuery 99 │   63.69 ms │                                 63.86 ms │     no change │
└───────────┴────────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 8633.14ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 8597.84ms │
│ Average Time (HEAD)                                     │   87.20ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │   86.85ms │
│ Queries Faster                                          │         2 │
│ Queries Slower                                          │         1 │
│ Queries with No Change                                  │        96 │
│ Queries with Failure                                    │         0 │
└─────────────────────────────────────────────────────────┴───────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark tpcds_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃                                  HEAD ┃ fix_single-distinct-groupby-memory-usage ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │           5.42 / 6.02 ±1.02 / 8.03 ms │              5.48 / 6.06 ±1.02 / 8.11 ms │     no change │
│ QQuery 2  │        79.52 / 79.71 ±0.21 / 80.11 ms │           79.63 / 80.03 ±0.34 / 80.43 ms │     no change │
│ QQuery 3  │        28.07 / 28.39 ±0.26 / 28.86 ms │           28.11 / 28.37 ±0.19 / 28.66 ms │     no change │
│ QQuery 4  │     428.64 / 431.52 ±1.89 / 434.03 ms │        427.69 / 433.05 ±4.37 / 439.07 ms │     no change │
│ QQuery 5  │        49.27 / 49.96 ±0.57 / 50.80 ms │           49.56 / 50.02 ±0.37 / 50.58 ms │     no change │
│ QQuery 6  │        34.13 / 35.29 ±0.87 / 36.77 ms │           34.40 / 36.33 ±2.59 / 41.42 ms │     no change │
│ QQuery 7  │        71.83 / 72.61 ±0.57 / 73.12 ms │           71.95 / 73.18 ±1.02 / 74.37 ms │     no change │
│ QQuery 8  │        35.53 / 35.89 ±0.29 / 36.35 ms │           35.81 / 36.36 ±0.46 / 37.17 ms │     no change │
│ QQuery 9  │        50.74 / 52.83 ±1.23 / 54.18 ms │           50.77 / 53.35 ±2.21 / 56.93 ms │     no change │
│ QQuery 10 │        61.74 / 63.30 ±2.27 / 67.81 ms │           61.42 / 63.43 ±2.99 / 69.39 ms │     no change │
│ QQuery 11 │     261.04 / 265.30 ±3.46 / 271.25 ms │        264.22 / 268.31 ±2.64 / 272.49 ms │     no change │
│ QQuery 12 │        28.34 / 28.94 ±0.40 / 29.43 ms │           28.31 / 28.70 ±0.30 / 29.12 ms │     no change │
│ QQuery 13 │     111.88 / 112.90 ±0.96 / 114.71 ms │        111.92 / 114.06 ±1.42 / 116.11 ms │     no change │
│ QQuery 14 │     382.80 / 389.27 ±4.06 / 394.08 ms │        380.70 / 384.02 ±2.43 / 387.26 ms │     no change │
│ QQuery 15 │        51.27 / 53.59 ±2.80 / 59.08 ms │           52.04 / 53.58 ±1.45 / 56.30 ms │     no change │
│ QQuery 16 │           6.20 / 6.31 ±0.18 / 6.66 ms │              5.51 / 5.68 ±0.20 / 6.07 ms │ +1.11x faster │
│ QQuery 17 │        78.98 / 79.41 ±0.40 / 79.98 ms │           77.83 / 78.07 ±0.16 / 78.27 ms │     no change │
│ QQuery 18 │     102.77 / 105.49 ±2.54 / 110.29 ms │        103.49 / 104.86 ±1.32 / 107.35 ms │     no change │
│ QQuery 19 │        40.90 / 41.33 ±0.37 / 41.84 ms │           40.78 / 41.04 ±0.18 / 41.23 ms │     no change │
│ QQuery 20 │        35.30 / 35.94 ±0.67 / 36.87 ms │           35.07 / 35.18 ±0.11 / 35.36 ms │     no change │
│ QQuery 21 │        16.52 / 17.33 ±1.26 / 19.83 ms │           16.40 / 17.31 ±1.04 / 19.27 ms │     no change │
│ QQuery 22 │        62.15 / 62.84 ±0.39 / 63.30 ms │           62.80 / 63.15 ±0.30 / 63.62 ms │     no change │
│ QQuery 23 │     299.29 / 304.57 ±4.68 / 311.02 ms │        299.83 / 302.09 ±2.39 / 306.19 ms │     no change │
│ QQuery 24 │     189.60 / 194.38 ±5.91 / 205.90 ms │        189.43 / 193.47 ±4.36 / 201.89 ms │     no change │
│ QQuery 25 │     107.82 / 109.31 ±1.21 / 110.79 ms │        106.95 / 108.29 ±1.29 / 110.07 ms │     no change │
│ QQuery 26 │        45.49 / 47.45 ±2.63 / 52.65 ms │           45.27 / 47.49 ±2.88 / 53.11 ms │     no change │
│ QQuery 27 │           5.79 / 6.03 ±0.25 / 6.51 ms │              5.81 / 6.06 ±0.26 / 6.49 ms │     no change │
│ QQuery 28 │        55.00 / 57.61 ±2.18 / 60.97 ms │           60.08 / 60.53 ±0.36 / 60.97 ms │  1.05x slower │
│ QQuery 29 │        94.14 / 95.80 ±1.33 / 98.00 ms │           95.50 / 96.48 ±1.40 / 99.15 ms │     no change │
│ QQuery 30 │        32.64 / 34.26 ±2.09 / 38.34 ms │           31.94 / 33.76 ±2.70 / 39.12 ms │     no change │
│ QQuery 31 │     105.84 / 107.20 ±1.27 / 109.59 ms │        107.09 / 107.88 ±0.86 / 109.14 ms │     no change │
│ QQuery 32 │        20.02 / 20.14 ±0.16 / 20.45 ms │           19.43 / 19.87 ±0.50 / 20.78 ms │     no change │
│ QQuery 33 │        35.91 / 36.70 ±0.47 / 37.18 ms │           36.15 / 36.68 ±0.39 / 37.15 ms │     no change │
│ QQuery 34 │          9.69 / 9.94 ±0.21 / 10.32 ms │            9.76 / 10.02 ±0.19 / 10.23 ms │     no change │
│ QQuery 35 │        70.62 / 72.70 ±2.08 / 76.37 ms │           70.50 / 72.80 ±2.31 / 76.99 ms │     no change │
│ QQuery 36 │           5.51 / 5.67 ±0.28 / 6.22 ms │              5.43 / 5.57 ±0.24 / 6.05 ms │     no change │
│ QQuery 37 │           6.57 / 6.72 ±0.10 / 6.88 ms │              6.52 / 6.61 ±0.06 / 6.67 ms │     no change │
│ QQuery 38 │        60.02 / 60.64 ±0.75 / 62.10 ms │           59.40 / 59.95 ±0.45 / 60.65 ms │     no change │
│ QQuery 39 │        71.53 / 73.96 ±3.90 / 81.70 ms │           71.64 / 73.02 ±1.54 / 76.04 ms │     no change │
│ QQuery 40 │        23.38 / 24.36 ±0.62 / 25.12 ms │           22.17 / 23.58 ±1.07 / 24.68 ms │     no change │
│ QQuery 41 │        10.97 / 11.21 ±0.28 / 11.74 ms │           11.04 / 11.14 ±0.09 / 11.29 ms │     no change │
│ QQuery 42 │        22.91 / 23.54 ±0.51 / 24.47 ms │           22.91 / 23.56 ±0.53 / 24.36 ms │     no change │
│ QQuery 43 │           4.63 / 4.72 ±0.17 / 5.06 ms │              4.69 / 4.83 ±0.21 / 5.25 ms │     no change │
│ QQuery 44 │           8.50 / 8.62 ±0.10 / 8.78 ms │              8.59 / 8.64 ±0.04 / 8.71 ms │     no change │
│ QQuery 45 │        35.60 / 35.91 ±0.27 / 36.32 ms │           35.13 / 35.57 ±0.23 / 35.77 ms │     no change │
│ QQuery 46 │        11.45 / 12.12 ±1.13 / 14.36 ms │           11.43 / 12.68 ±1.66 / 15.95 ms │     no change │
│ QQuery 47 │     214.57 / 219.61 ±4.51 / 227.51 ms │        215.34 / 218.62 ±2.41 / 221.02 ms │     no change │
│ QQuery 48 │        90.01 / 91.07 ±1.56 / 94.14 ms │           89.35 / 91.15 ±2.26 / 95.60 ms │     no change │
│ QQuery 49 │        68.33 / 69.39 ±1.32 / 71.89 ms │           68.31 / 69.26 ±1.05 / 71.22 ms │     no change │
│ QQuery 50 │        58.02 / 58.86 ±0.89 / 60.45 ms │           57.54 / 58.80 ±1.33 / 61.32 ms │     no change │
│ QQuery 51 │        92.75 / 95.52 ±2.45 / 99.76 ms │          91.56 / 95.77 ±6.09 / 107.89 ms │     no change │
│ QQuery 52 │        23.21 / 23.69 ±0.43 / 24.28 ms │           23.49 / 23.88 ±0.34 / 24.49 ms │     no change │
│ QQuery 53 │        28.69 / 28.88 ±0.15 / 29.07 ms │           28.84 / 29.00 ±0.10 / 29.15 ms │     no change │
│ QQuery 54 │        52.63 / 53.24 ±0.56 / 54.23 ms │           52.30 / 53.60 ±1.39 / 56.23 ms │     no change │
│ QQuery 55 │        22.69 / 22.99 ±0.19 / 23.26 ms │           22.85 / 23.14 ±0.29 / 23.55 ms │     no change │
│ QQuery 56 │        36.60 / 37.36 ±0.43 / 37.91 ms │           37.39 / 39.23 ±2.19 / 43.41 ms │     no change │
│ QQuery 57 │     165.20 / 166.11 ±0.81 / 167.52 ms │        164.97 / 166.52 ±1.65 / 169.49 ms │     no change │
│ QQuery 58 │     106.04 / 107.96 ±2.61 / 112.99 ms │        105.01 / 107.60 ±2.45 / 111.84 ms │     no change │
│ QQuery 59 │     114.61 / 115.43 ±0.64 / 116.45 ms │        113.98 / 115.16 ±1.42 / 117.94 ms │     no change │
│ QQuery 60 │        37.64 / 38.27 ±0.81 / 39.86 ms │           37.96 / 38.52 ±0.41 / 39.23 ms │     no change │
│ QQuery 61 │        10.79 / 10.94 ±0.23 / 11.39 ms │           10.84 / 10.96 ±0.16 / 11.27 ms │     no change │
│ QQuery 62 │        42.55 / 42.88 ±0.20 / 43.08 ms │           42.82 / 43.35 ±0.41 / 43.82 ms │     no change │
│ QQuery 63 │        28.77 / 29.47 ±0.97 / 31.38 ms │           28.52 / 30.05 ±1.69 / 33.27 ms │     no change │
│ QQuery 64 │     356.97 / 359.47 ±1.79 / 361.47 ms │        359.36 / 362.27 ±4.01 / 370.21 ms │     no change │
│ QQuery 65 │     126.65 / 130.47 ±4.64 / 139.13 ms │        130.29 / 132.53 ±2.36 / 135.85 ms │     no change │
│ QQuery 66 │        74.08 / 74.74 ±0.45 / 75.20 ms │           74.02 / 74.63 ±0.52 / 75.46 ms │     no change │
│ QQuery 67 │     241.97 / 247.66 ±5.87 / 258.83 ms │        244.49 / 247.19 ±2.95 / 250.89 ms │     no change │
│ QQuery 68 │        11.64 / 11.83 ±0.16 / 12.13 ms │           11.58 / 11.81 ±0.19 / 12.14 ms │     no change │
│ QQuery 69 │        56.34 / 58.39 ±2.05 / 61.81 ms │           55.62 / 56.24 ±0.44 / 56.82 ms │     no change │
│ QQuery 70 │     102.86 / 104.05 ±0.80 / 104.89 ms │        102.13 / 106.12 ±5.47 / 116.84 ms │     no change │
│ QQuery 71 │        34.36 / 34.97 ±0.53 / 35.82 ms │           34.06 / 34.95 ±0.87 / 36.45 ms │     no change │
│ QQuery 72 │ 1561.27 / 1609.86 ±39.36 / 1666.54 ms │    1516.53 / 1654.61 ±69.41 / 1698.50 ms │     no change │
│ QQuery 73 │          9.63 / 9.90 ±0.35 / 10.58 ms │            9.60 / 10.20 ±0.39 / 10.69 ms │     no change │
│ QQuery 74 │     154.31 / 155.01 ±0.88 / 156.73 ms │        150.74 / 155.23 ±3.87 / 162.21 ms │     no change │
│ QQuery 75 │     138.10 / 140.89 ±2.66 / 145.94 ms │        139.11 / 141.09 ±3.07 / 147.15 ms │     no change │
│ QQuery 76 │        33.01 / 33.44 ±0.39 / 34.11 ms │           34.15 / 35.61 ±2.04 / 39.59 ms │  1.06x slower │
│ QQuery 77 │        59.29 / 60.08 ±0.82 / 61.56 ms │           59.03 / 59.30 ±0.33 / 59.91 ms │     no change │
│ QQuery 78 │     158.53 / 161.03 ±2.33 / 165.37 ms │        157.71 / 163.53 ±6.19 / 175.49 ms │     no change │
│ QQuery 79 │        65.74 / 66.55 ±0.62 / 67.63 ms │           65.46 / 67.98 ±4.35 / 76.67 ms │     no change │
│ QQuery 80 │        93.20 / 95.03 ±2.23 / 99.30 ms │          93.85 / 96.27 ±2.81 / 101.72 ms │     no change │
│ QQuery 81 │        25.50 / 25.69 ±0.17 / 25.90 ms │           25.44 / 25.66 ±0.30 / 26.21 ms │     no change │
│ QQuery 82 │        16.23 / 16.50 ±0.18 / 16.69 ms │           16.15 / 16.41 ±0.27 / 16.93 ms │     no change │
│ QQuery 83 │        32.69 / 33.35 ±1.06 / 35.46 ms │           32.19 / 32.59 ±0.27 / 33.04 ms │     no change │
│ QQuery 84 │        29.18 / 29.73 ±0.51 / 30.61 ms │           29.26 / 31.05 ±2.12 / 35.05 ms │     no change │
│ QQuery 85 │       97.46 / 99.94 ±2.39 / 104.27 ms │          97.39 / 99.88 ±2.00 / 102.10 ms │     no change │
│ QQuery 86 │        24.72 / 24.94 ±0.18 / 25.15 ms │           24.81 / 25.32 ±0.32 / 25.75 ms │     no change │
│ QQuery 87 │        59.74 / 60.27 ±0.33 / 60.62 ms │           59.54 / 60.22 ±0.66 / 61.25 ms │     no change │
│ QQuery 88 │        59.18 / 60.22 ±1.03 / 62.13 ms │           58.74 / 60.76 ±2.70 / 66.09 ms │     no change │
│ QQuery 89 │        34.50 / 35.14 ±0.43 / 35.62 ms │           34.39 / 35.25 ±1.05 / 37.29 ms │     no change │
│ QQuery 90 │        16.12 / 16.33 ±0.19 / 16.70 ms │           15.94 / 16.18 ±0.27 / 16.70 ms │     no change │
│ QQuery 91 │        40.03 / 40.55 ±0.47 / 41.40 ms │           40.47 / 40.72 ±0.14 / 40.85 ms │     no change │
│ QQuery 92 │        28.54 / 29.80 ±1.37 / 32.33 ms │           29.21 / 29.79 ±0.35 / 30.15 ms │     no change │
│ QQuery 93 │        48.37 / 49.60 ±1.42 / 52.30 ms │           48.81 / 50.73 ±2.10 / 54.81 ms │     no change │
│ QQuery 94 │        37.39 / 38.46 ±0.76 / 39.39 ms │           36.85 / 37.37 ±0.50 / 38.25 ms │     no change │
│ QQuery 95 │        78.35 / 79.47 ±0.63 / 80.10 ms │           78.76 / 80.48 ±1.70 / 83.22 ms │     no change │
│ QQuery 96 │        23.04 / 23.34 ±0.29 / 23.87 ms │           23.29 / 23.62 ±0.33 / 24.18 ms │     no change │
│ QQuery 97 │        49.44 / 50.89 ±1.22 / 53.13 ms │           51.90 / 53.35 ±1.52 / 56.20 ms │     no change │
│ QQuery 98 │        41.95 / 42.75 ±0.71 / 43.76 ms │           41.46 / 42.26 ±0.64 / 43.17 ms │     no change │
│ QQuery 99 │        63.69 / 64.16 ±0.40 / 64.81 ms │           63.86 / 64.81 ±0.99 / 66.67 ms │     no change │
└───────────┴───────────────────────────────────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 8803.90ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 8865.30ms │
│ Average Time (HEAD)                                     │   88.93ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │   89.55ms │
│ Queries Faster                                          │         1 │
│ Queries Slower                                          │         2 │
│ Queries with No Change                                  │        96 │
│ Queries with Failure                                    │         0 │
└─────────────────────────────────────────────────────────┴───────────┘

Resource Usage

tpcds — base (merge-base)

Metric Value
Wall time 45.0s
Peak memory 2.2 GiB
Avg memory 1.5 GiB
CPU user 190.5s
CPU sys 5.2s
Peak spill 0 B

tpcds — branch

Metric Value
Wall time 45.0s
Peak memory 2.1 GiB
Avg memory 1.4 GiB
CPU user 187.3s
CPU sys 5.4s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing fix/single-distinct-groupby-memory-usage (0923424) to 334f293 (merge-base) diff

Run configuration
run benchmark clickbench_partitioned
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark clickbench_partitioned.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ Query     ┃       HEAD ┃ fix_single-distinct-groupby-memory-usage ┃       Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│ QQuery 0  │    1.21 ms │                                  1.24 ms │    no change │
│ QQuery 1  │   11.53 ms │                                 11.81 ms │    no change │
│ QQuery 2  │   36.03 ms │                                 36.11 ms │    no change │
│ QQuery 3  │   32.65 ms │                                 31.71 ms │    no change │
│ QQuery 4  │  242.63 ms │                                632.82 ms │ 2.61x slower │
│ QQuery 5  │  287.61 ms │                                277.62 ms │    no change │
│ QQuery 6  │    1.29 ms │                                  1.23 ms │    no change │
│ QQuery 7  │   13.36 ms │                                 12.91 ms │    no change │
│ QQuery 8  │  349.51 ms │                                376.25 ms │ 1.08x slower │
│ QQuery 9  │  475.90 ms │                                481.50 ms │    no change │
│ QQuery 10 │   65.62 ms │                                 96.97 ms │ 1.48x slower │
│ QQuery 11 │   76.46 ms │                                102.84 ms │ 1.34x slower │
│ QQuery 12 │  272.83 ms │                                266.34 ms │    no change │
│ QQuery 13 │  380.95 ms │                                392.40 ms │    no change │
│ QQuery 14 │  294.61 ms │                                285.95 ms │    no change │
│ QQuery 15 │  294.13 ms │                                300.89 ms │    no change │
│ QQuery 16 │  632.64 ms │                                625.71 ms │    no change │
│ QQuery 17 │  653.41 ms │                                639.50 ms │    no change │
│ QQuery 18 │ 1309.92 ms │                               1333.86 ms │    no change │
│ QQuery 19 │   29.19 ms │                                 27.93 ms │    no change │
│ QQuery 20 │  532.89 ms │                                527.14 ms │    no change │
│ QQuery 21 │  535.80 ms │                                528.26 ms │    no change │
│ QQuery 22 │ 1025.70 ms │                               1020.79 ms │    no change │
│ QQuery 23 │ 3176.32 ms │                               3262.30 ms │    no change │
│ QQuery 24 │   42.25 ms │                                 44.83 ms │ 1.06x slower │
│ QQuery 25 │  108.71 ms │                                107.64 ms │    no change │
│ QQuery 26 │   42.42 ms │                                 42.10 ms │    no change │
│ QQuery 27 │  528.52 ms │                                523.45 ms │    no change │
│ QQuery 28 │ 2938.42 ms │                               2931.53 ms │    no change │
│ QQuery 29 │   39.47 ms │                                 39.54 ms │    no change │
│ QQuery 30 │  323.15 ms │                                318.40 ms │    no change │
│ QQuery 31 │  289.84 ms │                                299.37 ms │    no change │
│ QQuery 32 │ 1165.77 ms │                               1156.05 ms │    no change │
│ QQuery 33 │ 1643.33 ms │                               1644.54 ms │    no change │
│ QQuery 34 │ 1599.86 ms │                               1732.76 ms │ 1.08x slower │
│ QQuery 35 │  311.22 ms │                                308.49 ms │    no change │
│ QQuery 36 │   67.88 ms │                                 66.64 ms │    no change │
│ QQuery 37 │   35.94 ms │                                 35.54 ms │    no change │
│ QQuery 38 │   40.92 ms │                                 44.41 ms │ 1.09x slower │
│ QQuery 39 │  142.77 ms │                                148.70 ms │    no change │
│ QQuery 40 │   14.09 ms │                                 14.70 ms │    no change │
│ QQuery 41 │   13.81 ms │                                 14.57 ms │ 1.06x slower │
│ QQuery 42 │   13.79 ms │                                 13.79 ms │    no change │
└───────────┴────────────┴──────────────────────────────────────────┴──────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 20094.34ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 20761.13ms │
│ Average Time (HEAD)                                     │   467.31ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │   482.82ms │
│ Queries Faster                                          │          0 │
│ Queries Slower                                          │          8 │
│ Queries with No Change                                  │         35 │
│ Queries with Failure                                    │          0 │
└─────────────────────────────────────────────────────────┴────────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark clickbench_partitioned.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃                                   HEAD ┃ fix_single-distinct-groupby-memory-usage ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 0  │           1.21 / 4.43 ±6.29 / 17.01 ms │             1.24 / 4.58 ±6.47 / 17.51 ms │     no change │
│ QQuery 1  │         11.53 / 11.89 ±0.19 / 12.10 ms │           11.81 / 12.12 ±0.16 / 12.26 ms │     no change │
│ QQuery 2  │         36.03 / 36.38 ±0.28 / 36.82 ms │           36.11 / 36.38 ±0.26 / 36.85 ms │     no change │
│ QQuery 3  │         32.65 / 33.14 ±0.59 / 34.04 ms │           31.71 / 32.12 ±0.48 / 33.02 ms │     no change │
│ QQuery 4  │      242.63 / 248.76 ±4.65 / 254.88 ms │        632.82 / 640.21 ±6.94 / 653.00 ms │  2.57x slower │
│ QQuery 5  │      287.61 / 290.33 ±1.85 / 293.06 ms │        277.62 / 281.84 ±2.32 / 284.76 ms │     no change │
│ QQuery 6  │            1.29 / 1.44 ±0.23 / 1.89 ms │              1.23 / 1.40 ±0.26 / 1.90 ms │     no change │
│ QQuery 7  │         13.36 / 13.65 ±0.22 / 13.94 ms │           12.91 / 13.16 ±0.14 / 13.35 ms │     no change │
│ QQuery 8  │      349.51 / 355.51 ±3.20 / 358.77 ms │       376.25 / 402.63 ±13.83 / 416.80 ms │  1.13x slower │
│ QQuery 9  │      475.90 / 482.99 ±5.10 / 490.11 ms │       481.50 / 500.32 ±15.59 / 522.19 ms │     no change │
│ QQuery 10 │         65.62 / 66.63 ±1.00 / 68.16 ms │         96.97 / 106.35 ±5.24 / 110.67 ms │  1.60x slower │
│ QQuery 11 │         76.46 / 76.75 ±0.23 / 77.09 ms │        102.84 / 106.76 ±2.42 / 110.23 ms │  1.39x slower │
│ QQuery 12 │      272.83 / 280.35 ±5.87 / 290.56 ms │       266.34 / 279.84 ±15.02 / 308.77 ms │     no change │
│ QQuery 13 │     380.95 / 397.57 ±19.27 / 433.55 ms │        392.40 / 402.56 ±9.28 / 416.14 ms │     no change │
│ QQuery 14 │      294.61 / 301.79 ±7.45 / 311.21 ms │        285.95 / 296.12 ±8.23 / 308.16 ms │     no change │
│ QQuery 15 │      294.13 / 305.86 ±9.51 / 322.73 ms │       300.89 / 314.38 ±14.12 / 341.32 ms │     no change │
│ QQuery 16 │     632.64 / 651.45 ±16.62 / 679.17 ms │        625.71 / 639.86 ±7.29 / 645.09 ms │     no change │
│ QQuery 17 │     653.41 / 667.01 ±10.17 / 680.65 ms │       639.50 / 666.96 ±16.17 / 686.63 ms │     no change │
│ QQuery 18 │  1309.92 / 1340.50 ±21.83 / 1374.01 ms │    1333.86 / 1347.92 ±14.13 / 1368.19 ms │     no change │
│ QQuery 19 │         29.19 / 36.96 ±9.98 / 54.17 ms │           27.93 / 29.85 ±3.32 / 36.46 ms │ +1.24x faster │
│ QQuery 20 │     532.89 / 542.48 ±10.77 / 563.55 ms │       527.14 / 538.96 ±12.15 / 561.71 ms │     no change │
│ QQuery 21 │      535.80 / 539.31 ±3.49 / 545.50 ms │        528.26 / 536.10 ±8.35 / 549.51 ms │     no change │
│ QQuery 22 │  1025.70 / 1055.17 ±21.43 / 1082.55 ms │    1020.79 / 1077.26 ±45.78 / 1142.31 ms │     no change │
│ QQuery 23 │ 3176.32 / 3424.21 ±141.60 / 3592.26 ms │    3262.30 / 3330.05 ±63.05 / 3447.61 ms │     no change │
│ QQuery 24 │     42.25 / 186.85 ±141.79 / 358.50 ms │       44.83 / 164.26 ±131.18 / 398.39 ms │ +1.14x faster │
│ QQuery 25 │      108.71 / 110.60 ±1.98 / 113.99 ms │        107.64 / 109.55 ±1.38 / 110.85 ms │     no change │
│ QQuery 26 │         42.42 / 43.49 ±0.84 / 44.67 ms │           42.10 / 43.28 ±0.97 / 44.64 ms │     no change │
│ QQuery 27 │      528.52 / 536.13 ±7.33 / 549.30 ms │        523.45 / 527.87 ±3.95 / 533.59 ms │     no change │
│ QQuery 28 │  2938.42 / 2980.13 ±33.70 / 3036.62 ms │    2931.53 / 2976.17 ±25.72 / 2999.24 ms │     no change │
│ QQuery 29 │        39.47 / 53.11 ±16.19 / 75.46 ms │           39.54 / 40.01 ±0.32 / 40.42 ms │ +1.33x faster │
│ QQuery 30 │      323.15 / 332.83 ±8.61 / 346.93 ms │        318.40 / 327.50 ±5.39 / 334.77 ms │     no change │
│ QQuery 31 │      289.84 / 296.37 ±4.24 / 301.76 ms │        299.37 / 300.87 ±1.99 / 304.76 ms │     no change │
│ QQuery 32 │  1165.77 / 1240.12 ±68.48 / 1356.63 ms │    1156.05 / 1231.14 ±67.76 / 1341.70 ms │     no change │
│ QQuery 33 │  1643.33 / 1669.20 ±19.52 / 1689.05 ms │    1644.54 / 1701.23 ±55.20 / 1783.59 ms │     no change │
│ QQuery 34 │  1599.86 / 1685.77 ±46.07 / 1720.38 ms │    1732.76 / 1790.47 ±61.66 / 1897.45 ms │  1.06x slower │
│ QQuery 35 │    311.22 / 396.36 ±141.72 / 678.99 ms │      308.49 / 458.84 ±268.06 / 994.42 ms │  1.16x slower │
│ QQuery 36 │         67.88 / 73.03 ±6.84 / 86.30 ms │           66.64 / 72.60 ±4.78 / 81.05 ms │     no change │
│ QQuery 37 │         35.94 / 36.72 ±0.60 / 37.69 ms │           35.54 / 41.81 ±9.56 / 60.47 ms │  1.14x slower │
│ QQuery 38 │        40.92 / 55.00 ±20.38 / 95.36 ms │          44.41 / 55.05 ±10.54 / 73.37 ms │     no change │
│ QQuery 39 │      142.77 / 154.62 ±7.58 / 166.27 ms │        148.70 / 155.60 ±4.28 / 161.45 ms │     no change │
│ QQuery 40 │         14.09 / 14.62 ±0.41 / 15.36 ms │           14.70 / 16.96 ±3.76 / 24.46 ms │  1.16x slower │
│ QQuery 41 │         13.81 / 18.06 ±8.21 / 34.47 ms │           14.57 / 19.11 ±6.80 / 32.39 ms │  1.06x slower │
│ QQuery 42 │         13.79 / 21.24 ±6.35 / 28.43 ms │           13.79 / 14.04 ±0.21 / 14.33 ms │ +1.51x faster │
└───────────┴────────────────────────────────────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 21068.81ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 21644.10ms │
│ Average Time (HEAD)                                     │   489.97ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │   503.35ms │
│ Queries Faster                                          │          4 │
│ Queries Slower                                          │          9 │
│ Queries with No Change                                  │         30 │
│ Queries with Failure                                    │          0 │
└─────────────────────────────────────────────────────────┴────────────┘

Resource Usage

clickbench_partitioned — base (merge-base)

Metric Value
Wall time 110.0s
Peak memory 18.6 GiB
Avg memory 5.9 GiB
CPU user 1030.1s
CPU sys 108.4s
Peak spill 0 B

clickbench_partitioned — branch

Metric Value
Wall time 110.0s
Peak memory 17.7 GiB
Avg memory 6.2 GiB
CPU user 1025.3s
CPU sys 110.6s
Peak spill 0 B

File an issue against this benchmark runner

@mkleen

mkleen commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

@mohitgurav20 There are serious performance regressions in the clickhouse benchmark. See #26000 (comment) for more details.

@mohitgurav20

Copy link
Copy Markdown
Contributor Author

Thanks for running the benchmark and flagging this @mkleen.

I analyzed the benchmark run in detail:
Every single regressed query (Q4, Q8, Q10, Q11, Q34, Q35, Q37, Q40, Q41) is a COUNT(DISTINCT "UserID") query where "UserID" is Int64.

Because count.groups_accumulator_supported_for_types(&[Int64], true) returns Some(true), the unconditional rewrite_pays check prevented SingleDistinctToGroupBy from firing on lone distinct queries like SELECT COUNT(DISTINCT UserID) FROM hits.

Without the two-phase rewrite to GROUP BY, the engine cannot leverage RepartitionExec: partitioning=Hash(...) to parallelize deduplication across partitions, and falls back to evaluating distincts serially through the accumulator. That explains the 2.57x regression on Q4 (248 ms -> 640 ms) and the slowdowns on Q10 and Q11.

This confirms that the two-phase rewrite is a critical parallelization optimization for lone distinct queries, even when a specialized GroupsAccumulator exists.

To address this cleanly:
Lone distinct queries (where all aggregates in the plan are DISTINCT on the same expression, with no companion non-distinct aggregates) should always be rewritten to preserve parallel hash aggregation. The heuristic gating should only apply when non-distinct companion aggregates (like count(*) or sum(y)) are present.

Does that direction sound good to you? If so, I'll update the rule accordingly so we preserve ClickBench performance.

…pache#25932)

SingleDistinctToGroupBy converts single DISTINCT aggregate expressions into
a two-phase GROUP BY plan. This enables parallel deduplication via
RepartitionExec hash partitioning, which is critical for lone-distinct
queries like COUNT(DISTINCT x).

However, when companion non-distinct aggregates are present alongside the
distinct aggregate (e.g. SELECT sum(y), count(DISTINCT x)), the companion
aggregates move to the inner group-by at a finer grain. When the distinct
aggregate already has native GroupsAccumulator support, this inner grouping
adds unnecessary memory and CPU overhead without benefiting from adapter
offloading.

This change:
1. Gates SingleDistinctToGroupBy with rewrite_pays() only when companion
   non-distinct aggregates are present.
2. Preserves the two-phase rewrite for pure lone-distinct queries so ClickBench
   and similar workloads maintain parallel deduplication without regression.
3. Consistently handles distinct aggregates with unsupported distinct handling.
4. Updates optimizer plan snapshots and sqllogictests.
@mohitgurav20
mohitgurav20 force-pushed the fix/single-distinct-groupby-memory-usage branch from 287b932 to ac76b0d Compare October 17, 2026 07:46
@mohitgurav20

Copy link
Copy Markdown
Contributor Author

Hi @mkleen, just following up on this when you get a chance.

I've rebased cleanly onto latest main and addressed the ClickBench regression — lone-distinct queries now always retain the 2-phase GROUP BY fanout, while the cost guard only applies when companion aggregates are present. CI and coverage checks are all passing.

Could you trigger a benchmark run whenever convenient? Thanks!

@mkleen

mkleen commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

run benchmarks

@adriangbot

Copy link
Copy Markdown

Benchmark for this request failed before finishing (Kubernetes reason: BackoffLimitExceeded).

Benchmarks requested: clickbench_partitioned

Kubernetes message
Job has reached the specified backoff limit

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6235987877-3544-tdnvp 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing fix/single-distinct-groupby-memory-usage (bbf4ccb) to 484183d (merge-base) diff

Run configuration
run benchmark tpch

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6235987877-3543-kmxlp 6.12.94+ #1 SMP Fri Aug 21 08:00:16 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing fix/single-distinct-groupby-memory-usage (bbf4ccb) to 484183d (merge-base) diff

Run configuration
run benchmark tpcds

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing fix/single-distinct-groupby-memory-usage (bbf4ccb) to 484183d (merge-base) diff

Run configuration
run benchmark tpch
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark tpch_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query     ┃     HEAD ┃ fix_single-distinct-groupby-memory-usage ┃    Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ QQuery 1  │ 40.67 ms │                                 40.37 ms │ no change │
│ QQuery 2  │ 18.39 ms │                                 18.09 ms │ no change │
│ QQuery 3  │ 28.50 ms │                                 28.33 ms │ no change │
│ QQuery 4  │ 17.29 ms │                                 17.34 ms │ no change │
│ QQuery 5  │ 35.11 ms │                                 35.10 ms │ no change │
│ QQuery 6  │ 13.51 ms │                                 13.59 ms │ no change │
│ QQuery 7  │ 41.82 ms │                                 42.07 ms │ no change │
│ QQuery 8  │ 40.50 ms │                                 40.89 ms │ no change │
│ QQuery 9  │ 50.27 ms │                                 50.25 ms │ no change │
│ QQuery 10 │ 40.05 ms │                                 39.91 ms │ no change │
│ QQuery 11 │ 13.35 ms │                                 13.22 ms │ no change │
│ QQuery 12 │ 18.08 ms │                                 18.14 ms │ no change │
│ QQuery 13 │ 39.00 ms │                                 38.85 ms │ no change │
│ QQuery 14 │ 24.47 ms │                                 24.35 ms │ no change │
│ QQuery 15 │ 30.59 ms │                                 30.87 ms │ no change │
│ QQuery 16 │ 13.79 ms │                                 13.68 ms │ no change │
│ QQuery 17 │ 73.73 ms │                                 74.40 ms │ no change │
│ QQuery 18 │ 45.89 ms │                                 44.98 ms │ no change │
│ QQuery 19 │ 31.69 ms │                                 31.94 ms │ no change │
│ QQuery 20 │ 31.72 ms │                                 31.52 ms │ no change │
│ QQuery 21 │ 55.44 ms │                                 55.01 ms │ no change │
│ QQuery 22 │ 13.86 ms │                                 13.58 ms │ no change │
└───────────┴──────────┴──────────────────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 717.73ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 716.49ms │
│ Average Time (HEAD)                                     │  32.62ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │  32.57ms │
│ Queries Faster                                          │        0 │
│ Queries Slower                                          │        0 │
│ Queries with No Change                                  │       22 │
│ Queries with Failure                                    │        0 │
└─────────────────────────────────────────────────────────┴──────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark tpch_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃                           HEAD ┃ fix_single-distinct-groupby-memory-usage ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │ 40.67 / 42.14 ±1.51 / 44.08 ms │           40.37 / 41.96 ±1.30 / 43.33 ms │     no change │
│ QQuery 2  │ 18.39 / 19.19 ±0.90 / 20.40 ms │           18.09 / 18.49 ±0.34 / 19.07 ms │     no change │
│ QQuery 3  │ 28.50 / 28.76 ±0.18 / 28.92 ms │           28.33 / 28.65 ±0.32 / 29.17 ms │     no change │
│ QQuery 4  │ 17.29 / 17.49 ±0.16 / 17.72 ms │           17.34 / 17.66 ±0.45 / 18.56 ms │     no change │
│ QQuery 5  │ 35.11 / 35.42 ±0.16 / 35.60 ms │           35.10 / 35.40 ±0.37 / 36.10 ms │     no change │
│ QQuery 6  │ 13.51 / 13.88 ±0.49 / 14.85 ms │           13.59 / 14.13 ±0.58 / 15.19 ms │     no change │
│ QQuery 7  │ 41.82 / 43.34 ±1.48 / 45.84 ms │           42.07 / 44.04 ±1.45 / 46.47 ms │     no change │
│ QQuery 8  │ 40.50 / 41.22 ±0.78 / 42.72 ms │           40.89 / 42.57 ±1.51 / 44.91 ms │     no change │
│ QQuery 9  │ 50.27 / 54.13 ±5.41 / 64.89 ms │           50.25 / 51.03 ±0.42 / 51.48 ms │ +1.06x faster │
│ QQuery 10 │ 40.05 / 41.52 ±1.68 / 44.76 ms │           39.91 / 40.32 ±0.25 / 40.60 ms │     no change │
│ QQuery 11 │ 13.35 / 13.51 ±0.18 / 13.82 ms │           13.22 / 13.44 ±0.23 / 13.84 ms │     no change │
│ QQuery 12 │ 18.08 / 18.40 ±0.44 / 19.27 ms │           18.14 / 18.36 ±0.21 / 18.65 ms │     no change │
│ QQuery 13 │ 39.00 / 40.81 ±1.87 / 44.16 ms │           38.85 / 39.20 ±0.30 / 39.74 ms │     no change │
│ QQuery 14 │ 24.47 / 24.66 ±0.15 / 24.81 ms │           24.35 / 24.64 ±0.16 / 24.83 ms │     no change │
│ QQuery 15 │ 30.59 / 31.64 ±1.28 / 33.91 ms │           30.87 / 31.52 ±0.80 / 33.08 ms │     no change │
│ QQuery 16 │ 13.79 / 13.91 ±0.12 / 14.11 ms │           13.68 / 13.80 ±0.15 / 14.07 ms │     no change │
│ QQuery 17 │ 73.73 / 77.96 ±2.40 / 80.84 ms │           74.40 / 75.50 ±1.06 / 77.01 ms │     no change │
│ QQuery 18 │ 45.89 / 47.52 ±1.90 / 50.68 ms │           44.98 / 45.83 ±0.58 / 46.78 ms │     no change │
│ QQuery 19 │ 31.69 / 31.89 ±0.16 / 32.11 ms │           31.94 / 33.24 ±1.46 / 35.38 ms │     no change │
│ QQuery 20 │ 31.72 / 32.78 ±1.51 / 35.77 ms │           31.52 / 31.95 ±0.43 / 32.76 ms │     no change │
│ QQuery 21 │ 55.44 / 57.10 ±2.33 / 61.65 ms │           55.01 / 56.23 ±1.22 / 58.38 ms │     no change │
│ QQuery 22 │ 13.86 / 13.95 ±0.07 / 14.04 ms │           13.58 / 13.86 ±0.30 / 14.39 ms │     no change │
└───────────┴────────────────────────────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 741.21ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 731.82ms │
│ Average Time (HEAD)                                     │  33.69ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │  33.26ms │
│ Queries Faster                                          │        1 │
│ Queries Slower                                          │        0 │
│ Queries with No Change                                  │       21 │
│ Queries with Failure                                    │        0 │
└─────────────────────────────────────────────────────────┴──────────┘

Resource Usage

tpch — base (merge-base)

Metric Value
Wall time 5.0s
Peak memory 1.1 GiB
Avg memory 480.6 MiB
CPU user 20.0s
CPU sys 1.7s
Peak spill 0 B

tpch — branch

Metric Value
Wall time 5.0s
Peak memory 1.1 GiB
Avg memory 489.3 MiB
CPU user 19.9s
CPU sys 1.6s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing fix/single-distinct-groupby-memory-usage (bbf4ccb) to 484183d (merge-base) diff

Run configuration
run benchmark tpcds
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark tpcds_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃       HEAD ┃ fix_single-distinct-groupby-memory-usage ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │    5.76 ms │                                  5.61 ms │     no change │
│ QQuery 2  │   83.34 ms │                                 82.53 ms │     no change │
│ QQuery 3  │   29.31 ms │                                 28.98 ms │     no change │
│ QQuery 4  │  502.51 ms │                                500.69 ms │     no change │
│ QQuery 5  │   52.20 ms │                                 50.92 ms │     no change │
│ QQuery 6  │   37.43 ms │                                 37.04 ms │     no change │
│ QQuery 7  │   72.79 ms │                                 73.03 ms │     no change │
│ QQuery 8  │   37.65 ms │                                 36.88 ms │     no change │
│ QQuery 9  │   51.43 ms │                                 52.06 ms │     no change │
│ QQuery 10 │   61.69 ms │                                 61.48 ms │     no change │
│ QQuery 11 │  321.78 ms │                                323.56 ms │     no change │
│ QQuery 12 │   30.62 ms │                                 30.13 ms │     no change │
│ QQuery 13 │  116.96 ms │                                116.41 ms │     no change │
│ QQuery 14 │  397.30 ms │                                390.83 ms │     no change │
│ QQuery 15 │   63.64 ms │                                 62.10 ms │     no change │
│ QQuery 16 │    6.91 ms │                                  5.75 ms │ +1.20x faster │
│ QQuery 17 │   82.26 ms │                                 81.07 ms │     no change │
│ QQuery 18 │  110.93 ms │                                109.86 ms │     no change │
│ QQuery 19 │   42.66 ms │                                 42.21 ms │     no change │
│ QQuery 20 │   38.31 ms │                                 36.82 ms │     no change │
│ QQuery 21 │   17.86 ms │                                 17.91 ms │     no change │
│ QQuery 22 │   71.45 ms │                                 68.67 ms │     no change │
│ QQuery 23 │  334.68 ms │                                324.91 ms │     no change │
│ QQuery 24 │  203.53 ms │                                203.23 ms │     no change │
│ QQuery 25 │  110.79 ms │                                111.25 ms │     no change │
│ QQuery 26 │   47.26 ms │                                 46.94 ms │     no change │
│ QQuery 27 │    6.42 ms │                                  6.28 ms │     no change │
│ QQuery 28 │   62.26 ms │                                 61.00 ms │     no change │
│ QQuery 29 │   97.67 ms │                                 97.65 ms │     no change │
│ QQuery 30 │   35.46 ms │                                 34.68 ms │     no change │
│ QQuery 31 │  112.48 ms │                                111.69 ms │     no change │
│ QQuery 32 │   21.52 ms │                                 21.14 ms │     no change │
│ QQuery 33 │   36.96 ms │                                 37.56 ms │     no change │
│ QQuery 34 │   10.61 ms │                                 10.55 ms │     no change │
│ QQuery 35 │   78.82 ms │                                 76.71 ms │     no change │
│ QQuery 36 │    6.34 ms │                                  6.12 ms │     no change │
│ QQuery 37 │    7.41 ms │                                  7.27 ms │     no change │
│ QQuery 38 │   65.79 ms │                                 64.30 ms │     no change │
│ QQuery 39 │   77.44 ms │                                 77.08 ms │     no change │
│ QQuery 40 │   24.94 ms │                                 25.35 ms │     no change │
│ QQuery 41 │   13.24 ms │                                 13.23 ms │     no change │
│ QQuery 42 │   24.48 ms │                                 24.37 ms │     no change │
│ QQuery 43 │    5.56 ms │                                  5.37 ms │     no change │
│ QQuery 44 │    9.69 ms │                                  9.31 ms │     no change │
│ QQuery 45 │   44.49 ms │                                 42.06 ms │ +1.06x faster │
│ QQuery 46 │   12.61 ms │                                 12.02 ms │     no change │
│ QQuery 47 │  254.85 ms │                                251.01 ms │     no change │
│ QQuery 48 │   92.44 ms │                                 92.45 ms │     no change │
│ QQuery 49 │   70.91 ms │                                 71.63 ms │     no change │
│ QQuery 50 │   59.20 ms │                                 60.64 ms │     no change │
│ QQuery 51 │   94.43 ms │                                 94.21 ms │     no change │
│ QQuery 52 │   24.52 ms │                                 24.45 ms │     no change │
│ QQuery 53 │   29.89 ms │                                 29.63 ms │     no change │
│ QQuery 54 │   54.40 ms │                                 55.36 ms │     no change │
│ QQuery 55 │   24.08 ms │                                 24.53 ms │     no change │
│ QQuery 56 │   38.14 ms │                                 39.34 ms │     no change │
│ QQuery 57 │  175.74 ms │                                176.96 ms │     no change │
│ QQuery 58 │  109.56 ms │                                108.10 ms │     no change │
│ QQuery 59 │  118.72 ms │                                118.25 ms │     no change │
│ QQuery 60 │   39.22 ms │                                 38.47 ms │     no change │
│ QQuery 61 │   11.47 ms │                                 11.06 ms │     no change │
│ QQuery 62 │   45.61 ms │                                 44.62 ms │     no change │
│ QQuery 63 │   30.70 ms │                                 29.84 ms │     no change │
│ QQuery 64 │  382.12 ms │                                378.06 ms │     no change │
│ QQuery 65 │  134.53 ms │                                135.66 ms │     no change │
│ QQuery 66 │   81.57 ms │                                 78.90 ms │     no change │
│ QQuery 67 │  278.86 ms │                                273.26 ms │     no change │
│ QQuery 68 │   12.57 ms │                                 12.19 ms │     no change │
│ QQuery 69 │   58.06 ms │                                 56.32 ms │     no change │
│ QQuery 70 │  105.08 ms │                                108.50 ms │     no change │
│ QQuery 71 │   35.12 ms │                                 35.08 ms │     no change │
│ QQuery 72 │ 1752.61 ms │                               1767.53 ms │     no change │
│ QQuery 73 │   11.08 ms │                                 10.82 ms │     no change │
│ QQuery 74 │  181.96 ms │                                175.91 ms │     no change │
│ QQuery 75 │  145.70 ms │                                142.97 ms │     no change │
│ QQuery 76 │   35.64 ms │                                 35.35 ms │     no change │
│ QQuery 77 │   62.01 ms │                                 60.58 ms │     no change │
│ QQuery 78 │  176.13 ms │                                172.60 ms │     no change │
│ QQuery 79 │   69.51 ms │                                 69.10 ms │     no change │
│ QQuery 80 │   97.89 ms │                                 99.09 ms │     no change │
│ QQuery 81 │   26.94 ms │                                 27.29 ms │     no change │
│ QQuery 82 │   17.74 ms │                                 17.44 ms │     no change │
│ QQuery 83 │   34.33 ms │                                 33.93 ms │     no change │
│ QQuery 84 │   30.52 ms │                                 30.68 ms │     no change │
│ QQuery 85 │  101.40 ms │                                100.67 ms │     no change │
│ QQuery 86 │   26.63 ms │                                 26.44 ms │     no change │
│ QQuery 87 │   65.36 ms │                                 66.12 ms │     no change │
│ QQuery 88 │   60.27 ms │                                 60.34 ms │     no change │
│ QQuery 89 │   35.42 ms │                                 35.78 ms │     no change │
│ QQuery 90 │   16.90 ms │                                 16.79 ms │     no change │
│ QQuery 91 │   42.27 ms │                                 42.22 ms │     no change │
│ QQuery 92 │   30.67 ms │                                 30.82 ms │     no change │
│ QQuery 93 │   52.44 ms │                                 50.99 ms │     no change │
│ QQuery 94 │   39.36 ms │                                 38.83 ms │     no change │
│ QQuery 95 │   81.90 ms │                                 81.02 ms │     no change │
│ QQuery 96 │   24.10 ms │                                 24.19 ms │     no change │
│ QQuery 97 │   52.26 ms │                                 52.34 ms │     no change │
│ QQuery 98 │   45.90 ms │                                 45.76 ms │     no change │
│ QQuery 99 │   67.39 ms │                                 67.10 ms │     no change │
└───────────┴────────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 9431.37ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 9375.86ms │
│ Average Time (HEAD)                                     │   95.27ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │   94.71ms │
│ Queries Faster                                          │         2 │
│ Queries Slower                                          │         0 │
│ Queries with No Change                                  │        97 │
│ Queries with Failure                                    │         0 │
└─────────────────────────────────────────────────────────┴───────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and fix_single-distinct-groupby-memory-usage
--------------------
Benchmark tpcds_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃                                  HEAD ┃ fix_single-distinct-groupby-memory-usage ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 1  │           5.76 / 6.51 ±1.25 / 9.01 ms │              5.61 / 6.28 ±1.02 / 8.31 ms │     no change │
│ QQuery 2  │        83.34 / 84.35 ±1.20 / 86.66 ms │           82.53 / 82.91 ±0.29 / 83.27 ms │     no change │
│ QQuery 3  │        29.31 / 29.46 ±0.14 / 29.63 ms │           28.98 / 29.13 ±0.11 / 29.32 ms │     no change │
│ QQuery 4  │     502.51 / 513.59 ±7.66 / 523.98 ms │        500.69 / 510.80 ±6.18 / 519.55 ms │     no change │
│ QQuery 5  │        52.20 / 52.80 ±0.46 / 53.59 ms │           50.92 / 51.36 ±0.33 / 51.84 ms │     no change │
│ QQuery 6  │        37.43 / 38.04 ±0.41 / 38.66 ms │           37.04 / 37.32 ±0.24 / 37.69 ms │     no change │
│ QQuery 7  │        72.79 / 73.79 ±0.64 / 74.37 ms │           73.03 / 73.81 ±0.48 / 74.52 ms │     no change │
│ QQuery 8  │        37.65 / 38.98 ±2.18 / 43.31 ms │           36.88 / 37.57 ±0.63 / 38.70 ms │     no change │
│ QQuery 9  │        51.43 / 56.48 ±3.88 / 62.96 ms │           52.06 / 53.50 ±1.12 / 54.68 ms │ +1.06x faster │
│ QQuery 10 │        61.69 / 62.23 ±0.49 / 63.15 ms │           61.48 / 61.74 ±0.24 / 62.18 ms │     no change │
│ QQuery 11 │     321.78 / 328.18 ±6.07 / 336.32 ms │        323.56 / 326.71 ±3.21 / 332.68 ms │     no change │
│ QQuery 12 │        30.62 / 31.08 ±0.37 / 31.54 ms │           30.13 / 30.68 ±0.46 / 31.37 ms │     no change │
│ QQuery 13 │     116.96 / 119.23 ±1.29 / 120.95 ms │        116.41 / 117.54 ±0.71 / 118.30 ms │     no change │
│ QQuery 14 │     397.30 / 398.53 ±2.09 / 402.69 ms │        390.83 / 399.46 ±5.41 / 407.34 ms │     no change │
│ QQuery 15 │        63.64 / 65.40 ±2.18 / 69.70 ms │           62.10 / 62.85 ±0.88 / 64.28 ms │     no change │
│ QQuery 16 │           6.91 / 7.05 ±0.16 / 7.33 ms │              5.75 / 5.93 ±0.11 / 6.07 ms │ +1.19x faster │
│ QQuery 17 │        82.26 / 83.66 ±1.49 / 86.32 ms │           81.07 / 82.14 ±1.67 / 85.46 ms │     no change │
│ QQuery 18 │     110.93 / 112.40 ±0.79 / 113.10 ms │        109.86 / 111.17 ±1.25 / 113.36 ms │     no change │
│ QQuery 19 │        42.66 / 42.87 ±0.18 / 43.19 ms │           42.21 / 42.72 ±0.61 / 43.92 ms │     no change │
│ QQuery 20 │        38.31 / 40.11 ±3.04 / 46.16 ms │           36.82 / 38.77 ±1.54 / 41.48 ms │     no change │
│ QQuery 21 │        17.86 / 18.34 ±0.33 / 18.81 ms │           17.91 / 18.06 ±0.17 / 18.40 ms │     no change │
│ QQuery 22 │        71.45 / 72.41 ±1.02 / 73.90 ms │           68.67 / 69.91 ±0.83 / 71.28 ms │     no change │
│ QQuery 23 │     334.68 / 337.16 ±2.62 / 342.04 ms │        324.91 / 335.63 ±6.63 / 342.70 ms │     no change │
│ QQuery 24 │     203.53 / 213.89 ±7.41 / 223.61 ms │        203.23 / 212.19 ±6.51 / 221.49 ms │     no change │
│ QQuery 25 │     110.79 / 113.44 ±2.07 / 117.17 ms │        111.25 / 112.98 ±1.78 / 116.36 ms │     no change │
│ QQuery 26 │        47.26 / 48.84 ±2.14 / 53.03 ms │           46.94 / 49.00 ±2.97 / 54.87 ms │     no change │
│ QQuery 27 │           6.42 / 6.53 ±0.11 / 6.72 ms │              6.28 / 6.43 ±0.10 / 6.54 ms │     no change │
│ QQuery 28 │        62.26 / 63.09 ±0.63 / 64.01 ms │           61.00 / 61.78 ±0.47 / 62.49 ms │     no change │
│ QQuery 29 │       97.67 / 99.13 ±1.36 / 101.19 ms │          97.65 / 99.49 ±1.55 / 101.85 ms │     no change │
│ QQuery 30 │        35.46 / 38.37 ±4.48 / 47.29 ms │           34.68 / 36.58 ±2.98 / 42.51 ms │     no change │
│ QQuery 31 │     112.48 / 113.58 ±0.87 / 114.67 ms │        111.69 / 112.91 ±0.67 / 113.51 ms │     no change │
│ QQuery 32 │        21.52 / 21.74 ±0.20 / 21.99 ms │           21.14 / 21.58 ±0.38 / 22.27 ms │     no change │
│ QQuery 33 │        36.96 / 37.45 ±0.25 / 37.65 ms │           37.56 / 37.86 ±0.32 / 38.34 ms │     no change │
│ QQuery 34 │        10.61 / 12.36 ±3.02 / 18.38 ms │           10.55 / 11.06 ±0.59 / 12.18 ms │ +1.12x faster │
│ QQuery 35 │        78.82 / 79.70 ±0.88 / 81.24 ms │           76.71 / 78.87 ±2.41 / 83.07 ms │     no change │
│ QQuery 36 │           6.34 / 6.48 ±0.14 / 6.72 ms │              6.12 / 6.18 ±0.07 / 6.30 ms │     no change │
│ QQuery 37 │           7.41 / 7.51 ±0.07 / 7.59 ms │              7.27 / 7.45 ±0.16 / 7.69 ms │     no change │
│ QQuery 38 │        65.79 / 67.55 ±1.23 / 69.00 ms │           64.30 / 65.31 ±0.74 / 66.31 ms │     no change │
│ QQuery 39 │        77.44 / 80.02 ±2.65 / 84.87 ms │           77.08 / 78.76 ±1.83 / 81.91 ms │     no change │
│ QQuery 40 │        24.94 / 25.55 ±0.42 / 25.97 ms │           25.35 / 25.68 ±0.33 / 26.17 ms │     no change │
│ QQuery 41 │        13.24 / 13.28 ±0.04 / 13.35 ms │           13.23 / 13.99 ±1.28 / 16.55 ms │  1.05x slower │
│ QQuery 42 │        24.48 / 24.88 ±0.48 / 25.75 ms │           24.37 / 24.84 ±0.54 / 25.83 ms │     no change │
│ QQuery 43 │           5.56 / 5.61 ±0.05 / 5.67 ms │              5.37 / 5.48 ±0.08 / 5.61 ms │     no change │
│ QQuery 44 │          9.69 / 9.91 ±0.17 / 10.11 ms │              9.31 / 9.44 ±0.10 / 9.57 ms │     no change │
│ QQuery 45 │        44.49 / 45.52 ±0.85 / 46.83 ms │           42.06 / 43.48 ±1.33 / 45.96 ms │     no change │
│ QQuery 46 │        12.61 / 12.77 ±0.17 / 13.02 ms │           12.02 / 12.48 ±0.30 / 12.84 ms │     no change │
│ QQuery 47 │     254.85 / 261.77 ±5.99 / 272.95 ms │        251.01 / 253.36 ±2.68 / 258.51 ms │     no change │
│ QQuery 48 │        92.44 / 92.64 ±0.18 / 92.90 ms │           92.45 / 93.91 ±1.22 / 95.57 ms │     no change │
│ QQuery 49 │        70.91 / 72.53 ±1.13 / 74.12 ms │           71.63 / 72.60 ±0.84 / 73.69 ms │     no change │
│ QQuery 50 │        59.20 / 60.51 ±0.85 / 61.88 ms │           60.64 / 62.96 ±3.99 / 70.89 ms │     no change │
│ QQuery 51 │        94.43 / 95.66 ±0.78 / 96.84 ms │          94.21 / 96.98 ±3.05 / 102.89 ms │     no change │
│ QQuery 52 │        24.52 / 25.26 ±0.96 / 27.17 ms │           24.45 / 24.83 ±0.27 / 25.07 ms │     no change │
│ QQuery 53 │        29.89 / 31.93 ±2.97 / 37.79 ms │           29.63 / 29.85 ±0.15 / 30.05 ms │ +1.07x faster │
│ QQuery 54 │        54.40 / 54.77 ±0.45 / 55.63 ms │           55.36 / 57.78 ±4.11 / 65.99 ms │  1.05x slower │
│ QQuery 55 │        24.08 / 24.45 ±0.39 / 25.04 ms │           24.53 / 24.78 ±0.30 / 25.36 ms │     no change │
│ QQuery 56 │        38.14 / 38.58 ±0.37 / 39.07 ms │           39.34 / 39.89 ±0.46 / 40.50 ms │     no change │
│ QQuery 57 │     175.74 / 178.61 ±2.61 / 183.20 ms │        176.96 / 179.00 ±2.06 / 182.96 ms │     no change │
│ QQuery 58 │     109.56 / 111.33 ±1.96 / 114.88 ms │        108.10 / 109.77 ±1.15 / 111.53 ms │     no change │
│ QQuery 59 │     118.72 / 119.92 ±0.65 / 120.71 ms │        118.25 / 120.25 ±1.84 / 123.46 ms │     no change │
│ QQuery 60 │        39.22 / 39.58 ±0.29 / 40.05 ms │           38.47 / 39.33 ±0.65 / 40.36 ms │     no change │
│ QQuery 61 │        11.47 / 11.63 ±0.13 / 11.76 ms │           11.06 / 11.31 ±0.15 / 11.49 ms │     no change │
│ QQuery 62 │        45.61 / 47.46 ±2.02 / 51.10 ms │           44.62 / 46.42 ±2.58 / 51.53 ms │     no change │
│ QQuery 63 │        30.70 / 30.91 ±0.13 / 31.10 ms │           29.84 / 30.50 ±0.42 / 30.99 ms │     no change │
│ QQuery 64 │     382.12 / 384.93 ±1.96 / 388.02 ms │        378.06 / 383.83 ±6.81 / 396.52 ms │     no change │
│ QQuery 65 │     134.53 / 137.91 ±1.99 / 140.28 ms │        135.66 / 138.42 ±2.31 / 142.12 ms │     no change │
│ QQuery 66 │        81.57 / 83.02 ±1.82 / 86.34 ms │           78.90 / 81.26 ±4.02 / 89.27 ms │     no change │
│ QQuery 67 │     278.86 / 283.02 ±2.90 / 287.85 ms │        273.26 / 277.68 ±4.89 / 286.66 ms │     no change │
│ QQuery 68 │        12.57 / 12.78 ±0.16 / 12.99 ms │           12.19 / 12.34 ±0.13 / 12.58 ms │     no change │
│ QQuery 69 │        58.06 / 61.96 ±5.07 / 71.93 ms │           56.32 / 57.26 ±0.57 / 57.98 ms │ +1.08x faster │
│ QQuery 70 │     105.08 / 108.28 ±2.23 / 111.94 ms │        108.50 / 111.39 ±4.02 / 119.21 ms │     no change │
│ QQuery 71 │        35.12 / 35.67 ±0.30 / 36.03 ms │           35.08 / 35.52 ±0.25 / 35.82 ms │     no change │
│ QQuery 72 │ 1752.61 / 1819.02 ±59.84 / 1928.13 ms │    1767.53 / 1799.64 ±36.60 / 1870.08 ms │     no change │
│ QQuery 73 │        11.08 / 12.43 ±2.13 / 16.68 ms │           10.82 / 11.04 ±0.16 / 11.29 ms │ +1.13x faster │
│ QQuery 74 │     181.96 / 186.26 ±3.00 / 190.40 ms │        175.91 / 182.95 ±5.00 / 188.40 ms │     no change │
│ QQuery 75 │     145.70 / 147.82 ±1.97 / 151.50 ms │        142.97 / 147.38 ±3.99 / 154.56 ms │     no change │
│ QQuery 76 │        35.64 / 35.85 ±0.15 / 36.05 ms │           35.35 / 35.97 ±0.46 / 36.48 ms │     no change │
│ QQuery 77 │        62.01 / 64.48 ±4.23 / 72.93 ms │           60.58 / 61.37 ±0.75 / 62.61 ms │     no change │
│ QQuery 78 │     176.13 / 184.00 ±7.14 / 194.93 ms │        172.60 / 177.29 ±5.62 / 188.31 ms │     no change │
│ QQuery 79 │        69.51 / 71.89 ±3.66 / 79.17 ms │           69.10 / 71.87 ±4.50 / 80.81 ms │     no change │
│ QQuery 80 │      97.89 / 100.20 ±2.04 / 103.85 ms │         99.09 / 101.44 ±2.14 / 104.15 ms │     no change │
│ QQuery 81 │        26.94 / 28.52 ±2.68 / 33.86 ms │           27.29 / 27.67 ±0.20 / 27.87 ms │     no change │
│ QQuery 82 │        17.74 / 17.93 ±0.12 / 18.10 ms │           17.44 / 17.77 ±0.31 / 18.14 ms │     no change │
│ QQuery 83 │        34.33 / 35.09 ±0.85 / 36.72 ms │           33.93 / 34.46 ±0.35 / 34.81 ms │     no change │
│ QQuery 84 │        30.52 / 31.91 ±2.13 / 36.14 ms │           30.68 / 32.29 ±2.38 / 37.00 ms │     no change │
│ QQuery 85 │     101.40 / 102.78 ±1.83 / 106.35 ms │        100.67 / 101.56 ±0.95 / 103.27 ms │     no change │
│ QQuery 86 │        26.63 / 27.10 ±0.32 / 27.58 ms │           26.44 / 26.72 ±0.18 / 26.96 ms │     no change │
│ QQuery 87 │        65.36 / 66.81 ±1.05 / 68.43 ms │           66.12 / 66.95 ±0.54 / 67.46 ms │     no change │
│ QQuery 88 │        60.27 / 60.84 ±0.35 / 61.29 ms │           60.34 / 60.81 ±0.35 / 61.21 ms │     no change │
│ QQuery 89 │        35.42 / 35.53 ±0.09 / 35.67 ms │           35.78 / 36.16 ±0.26 / 36.56 ms │     no change │
│ QQuery 90 │        16.90 / 18.35 ±2.75 / 23.85 ms │           16.79 / 16.94 ±0.18 / 17.25 ms │ +1.08x faster │
│ QQuery 91 │        42.27 / 42.57 ±0.35 / 43.22 ms │           42.22 / 42.56 ±0.26 / 42.82 ms │     no change │
│ QQuery 92 │        30.67 / 31.46 ±0.73 / 32.61 ms │           30.82 / 32.84 ±3.94 / 40.72 ms │     no change │
│ QQuery 93 │        52.44 / 53.35 ±0.84 / 54.45 ms │           50.99 / 52.74 ±1.55 / 55.23 ms │     no change │
│ QQuery 94 │        39.36 / 39.98 ±0.35 / 40.43 ms │           38.83 / 39.82 ±0.78 / 40.75 ms │     no change │
│ QQuery 95 │        81.90 / 84.00 ±2.40 / 88.52 ms │           81.02 / 82.16 ±0.85 / 83.12 ms │     no change │
│ QQuery 96 │        24.10 / 24.38 ±0.42 / 25.20 ms │           24.19 / 25.57 ±2.61 / 30.80 ms │     no change │
│ QQuery 97 │        52.26 / 53.11 ±0.69 / 54.11 ms │           52.34 / 53.96 ±1.56 / 55.88 ms │     no change │
│ QQuery 98 │        45.90 / 46.67 ±0.75 / 48.03 ms │           45.76 / 46.13 ±0.49 / 47.04 ms │     no change │
│ QQuery 99 │        67.39 / 68.96 ±1.71 / 72.27 ms │           67.10 / 67.39 ±0.21 / 67.65 ms │     no change │
└───────────┴───────────────────────────────────────┴──────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                                       ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                                       │ 9656.23ms │
│ Total Time (fix_single-distinct-groupby-memory-usage)   │ 9564.43ms │
│ Average Time (HEAD)                                     │   97.54ms │
│ Average Time (fix_single-distinct-groupby-memory-usage) │   96.61ms │
│ Queries Faster                                          │         7 │
│ Queries Slower                                          │         2 │
│ Queries with No Change                                  │        90 │
│ Queries with Failure                                    │         0 │
└─────────────────────────────────────────────────────────┴───────────┘

Resource Usage

tpcds — base (merge-base)

Metric Value
Wall time 50.0s
Peak memory 2.4 GiB
Avg memory 1.6 GiB
CPU user 208.1s
CPU sys 6.0s
Peak spill 0 B

tpcds — branch

Metric Value
Wall time 50.0s
Peak memory 2.2 GiB
Avg memory 1.5 GiB
CPU user 207.8s
CPU sys 5.9s
Peak spill 0 B

File an issue against this benchmark runner

@mkleen

mkleen commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

@mohitgurav20 Thanks! Looks good at first glance. I’ll go ahead and start the review.

shehabgamin pushed a commit to lakehq/datafusion that referenced this pull request Nov 8, 2026
## Which issue does this PR close?

Closes apache#25936.

## Rationale for this change

Grouped integer `count(DISTINCT x)` can use its native GroupsAccumulator
to avoid an extra aggregate over distinct `(group, value)` pairs. This
helps the original single-target-partition workload, but the choice also
affects spilling and performance on other data shapes.

The native plan is now **opt-in**. The default keeps the existing
two-phase rewrite. Under a 1 MiB memory limit, the reviewer's query with
two groups and 100,000 distinct values completes with the rewrite and
returns `2, 100000`; the native plan runs out of memory. Native DISTINCT
must fit each group's distinct values in memory, including when merging
spilled or partial states.

## What changes are included in this PR?

Add `datafusion.optimizer.single_distinct_prefer_native`, default
`false`. When enabled, grouped all-DISTINCT aggregates retain the native
plan only if:

- `execution.target_partitions == 1`;
- all aggregates share one DISTINCT argument;
- every aggregate explicitly reports specialized GroupsAccumulator
support for its argument types.

Unknown support preserves the rewrite. Integer DISTINCT counts currently
qualify; string and floating-point arguments, global aggregates, mixed
aggregates and multiple target partitions keep their existing behavior.

The earlier [GKE ClickBench
comparison](apache#25988 (comment))
regressed with native parallel plans, so multiple target partitions
continue to distribute deduplication by both the group key and distinct
value.

One target partition can still have multiple input partitions. `UNION
ALL` and a table created with four partitions can use Partial → Coalesce
→ Final and rebuild the DISTINCT set in Final. This is covered by
execution tests and physical-plan snapshots.

Add a ten-query SQL benchmark suite covering argument types,
grouping-key types, cardinality, skew and duplicates. Q10 has one
million groups with four copies of the same value per group at the
default four million rows. Native execution probes both the group table
and the distinct set on every row; the rewrite's outer aggregate only
sees deduplicated pairs. This is an unfavorable case for the native
path, and the logical rule has no statistics to cost it.

This overlaps with the option proposed in apache#26000. `prefer_native`
describes the plan choice without promising lower memory use; the two
PRs should converge on one setting.

### Local comparison, November 7

Compared main `636590b` with review-fix commit `4a0bacd4`, flag off and
on, on Linux (Xeon Gold 6238T, CPU affinity 0–7), Rust 1.99.1,
`release-nonlto`, four million rows, greedy / 1 GiB. All ten queries ran
at 1, 4 and 12 target partitions in three rounds, reversing version
order in the second round. Each round had six iterations per query, with
the first discarded: 15 retained samples per version and configuration.
All result assertions passed. The main executable was reused from the
November 2 build; the review fix was freshly rebuilt, and all versions
used the same updated ten-query suite. No builds ran during timing.

Single-partition medians:

| Case | Main | Flag off (default) | Flag on | On vs off elapsed time |
| --- | ---: | ---: | ---: | ---: |
| Q01: Int64, 2,000 groups, high NDV | 448.97 ms | 424.17 ms | 305.27 ms
| −28.0% |
| Q02: Int64, 2,000 groups, low NDV | 102.96 ms | 101.23 ms | 79.91 ms |
−21.1% |
| Q10: 1M groups, four identical values per group | 316.79 ms | 284.87
ms | 398.12 ms | +39.8% |

Q10 reproduces the reviewer's regression. Its memory-pool peak also
rises from 69.88 MiB with the flag off to 95.88 MiB with it on. Q01 goes
the other way, from 191.45 to 168.17 MiB. Both the runtime and memory
tradeoffs are reasons to keep this opt-in.

The primary comparison is off/on in the same executable. Main and
flag-off use the same plans but have timing differences on this shared
host. At four and twelve target partitions both flag values keep the
rewrite; observed off/on median differences range from −8.0% to +11.2%.
Separate physical-plan checks for Q01 and Q10 passed for all three
versions at all three partition settings (18 checks).

## What is the testing strategy for this PR?

SQL tests cover the default and both flag values in one session, integer
types and NULLs, the low-memory spill reproducer, native plan selection,
`UNION ALL`, a pre-partitioned table, and the rewrite at four target
partitions. The configuration documentation and information-schema
snapshots include the new default.

After syncing with main `4bf32cb4`, formatting, generated configuration
docs, Prettier, strict all-targets/all-features Clippy and the full
`dev/rust_lint.sh` passed on Linux at `0db14a06`. The extended workspace
run passed 13,537 Rust tests (8 ignored, none failed) and all 547 SQL
files. The CLI run passed 116 tests, and the focused SQL run passed all
seven files. The committed PR range also passed the large-file check.

## Are there any user-facing changes?

The new setting lets users opt into native grouped integer DISTINCT
counts with one target partition. Default planning stays unchanged.

---------

Co-authored-by: hushen <[email protected]>
…roupby-memory-usage

# Conflicts:
#	datafusion/common/src/config.rs
#	datafusion/optimizer/src/single_distinct_to_groupby.rs
#	datafusion/sqllogictest/test_files/information_schema.slt
#	docs/source/user-guide/configs.md
@mohitgurav20
mohitgurav20 force-pushed the fix/single-distinct-groupby-memory-usage branch from e3dd29a to c413a0e Compare November 8, 2026 10:55
@mohitgurav20

Copy link
Copy Markdown
Contributor Author

@mkleen gentle reminder sir to review whenever you are free.

@jayzhan211 jayzhan211 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.

@mohitgurav20 Thanks for the updates and the benchmark numbers. Two things block this for me: with the setting on, float DISTINCT returns different results, and #25988 (merged today) already added single_distinct_prefer_native to this rule, so this would be a second setting for the same choice. Details are inline.

@mkleen one question for you: with a memory limit and spilling on, this setting never lowered the limit a query needed in my runs, and at 4 target partitions it raised it 4–12x (numbers in the config.rs comment). It only lowers peak memory when no limit is set. Is that still the case you want the setting for?

Comment on lines +128 to +130
| DataType::Float16
| DataType::Float32
| DataType::Float64

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.

With the setting on, float DISTINCT gives different answers: on (0.0, -0.0, 1.5), count(DISTINCT f) goes from 2 to 3 and avg(DISTINCT f) from 0.75 to 0.5. The native accumulators compare raw bits (Hashable in functions-aggregate-common/src/utils.rs), so -0.0 and 0.0 count as two values, while the rewrite's GROUP BY treats them as one. Could you keep floats on the rewrite and add the test below? It fails without this change.

Suggested change
| DataType::Float16
| DataType::Float32
| DataType::Float64
Regression test for single_distinct_to_groupby.slt (place it while the setting is on)
# Float DISTINCT keeps the rewrite: the native accumulator counts -0.0 and 0.0 separately.
statement ok
CREATE TABLE t_prefer_mem_float AS SELECT * FROM (VALUES (1, CAST('0.0' AS DOUBLE)), (1, CAST('-0.0' AS DOUBLE)), (1, CAST('1.5' AS DOUBLE))) AS v(g, f);

query IIR
SELECT g, count(DISTINCT f), avg(DISTINCT f) FROM t_prefer_mem_float GROUP BY g;
----
1 2 0.75

statement ok
DROP TABLE t_prefer_mem_float;

Comment thread datafusion/common/src/config.rs Outdated
Comment on lines +1943 to +1957
/// When `true`, the `SingleDistinctToGroupBy` optimizer rule applies its cost guard
/// universally — including to lone-distinct queries that have no companion non-distinct
/// aggregates. The rewrite is skipped whenever the distinct function already has a
/// native `GroupsAccumulator`, reducing peak memory by avoiding the larger intermediate
/// `(group, distinct value)` result set that the two-phase plan produces.
///
/// The trade-off is reduced parallelism: skipping the rewrite causes the engine to fall
/// back to serial per-group accumulation, which is typically 2–3× slower for queries
/// with high-cardinality distinct values.
///
/// When `false` (the default), lone-distinct queries are always rewritten into the
/// two-phase `GROUP BY` so they can fan out across partitions and achieve maximum
/// throughput.
///
/// See <https://github.com/apache/datafusion/issues/25932>

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.

Under a memory limit, this setting needs more memory, not less: at 4 target partitions, sum(DISTINCT x) on the #25932 table needs 16M with the setting on and finishes within 4M without it (count: 48M vs 4M). The rewrite can spill its (group, value) pairs, but the native accumulator keeps each group's distinct values in memory, so with a few large groups it fails where the default finishes. Your numbers are right for a pool with no limit, so the doc should say that:

Suggested change
/// When `true`, the `SingleDistinctToGroupBy` optimizer rule applies its cost guard
/// universally — including to lone-distinct queries that have no companion non-distinct
/// aggregates. The rewrite is skipped whenever the distinct function already has a
/// native `GroupsAccumulator`, reducing peak memory by avoiding the larger intermediate
/// `(group, distinct value)` result set that the two-phase plan produces.
///
/// The trade-off is reduced parallelism: skipping the rewrite causes the engine to fall
/// back to serial per-group accumulation, which is typically 2–3× slower for queries
/// with high-cardinality distinct values.
///
/// When `false` (the default), lone-distinct queries are always rewritten into the
/// two-phase `GROUP BY` so they can fan out across partitions and achieve maximum
/// throughput.
///
/// See <https://github.com/apache/datafusion/issues/25932>
/// When `true`, the `SingleDistinctToGroupBy` optimizer rule keeps a grouped
/// DISTINCT aggregate native, instead of rewriting it into a two-phase `GROUP BY`,
/// when every DISTINCT argument is a fixed-width, non-float type. This lowers peak
/// memory when no memory limit is set, because the intermediate
/// `(group, distinct value)` result is never built.
///
/// Under a memory limit, the rewrite can spill those pairs, but the native
/// accumulator keeps each group's distinct values in memory. With this option
/// enabled, a query can need more memory, or fail where the rewrite finishes.
///
/// Queries without `GROUP BY`, and other argument types, keep the rewrite.
///
/// See <https://github.com/apache/datafusion/issues/25932>
Smallest memory limit that finishes (release datafusion-cli, greedy pool, spilling on)
query on the #25932 table (4M rows, 2,000 groups) target partitions setting off setting on
sum(DISTINCT x) 1 ≤4M ≤4M (also 1M)
sum(DISTINCT x) 4 ≤4M 16M
count(DISTINCT x) 1 ≤4M ≤4M
count(DISTINCT x) 4 ≤4M 48M

Two large groups at a 1M limit (SELECT v % 2, count(DISTINCT v) FROM generate_series(1, 100000) AS t(v) GROUP BY v % 2, batch_size = 128): the setting fails with Resources exhausted at 1 target partition for count and sum, and at 4 for count. The default finishes in every case. The same query is in aggregate_memory_spill.slt.

configs.md and information_schema.slt need regenerating afterwards.

Comment thread datafusion/common/src/config.rs Outdated
/// throughput.
///
/// See <https://github.com/apache/datafusion/issues/25932>
pub single_distinct_aggregation_prefer_memory: bool, default = false

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.

#25988 (merged today, not released yet) added single_distinct_prefer_native just below for the same choice, and with both settings count(DISTINCT int) at one target partition skips the rewrite under either one. Could this PR widen that setting's gate instead of adding a second setting? Because it hasn't been released, its name and doc can still change, and users would get one switch for "keep grouped DISTINCT native".

Comment on lines +200 to +207
// Aggregates with DistinctHandling::Unsupported (e.g. approx_median) cannot
// run natively with DISTINCT; they must always be rewritten.
if distinct_aggs
.iter()
.any(|(func, _)| func.distinct_handling() == DistinctHandling::Unsupported)
{
return Ok(true);
}

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.

With the setting off, this return also changes main's behavior: SELECT g, count(*), stddev(DISTINCT x) FROM t GROUP BY g fails on main with STDDEV_POP(DISTINCT) aggregations are not available and returns the right rows here. That's a fix, but no test covers it at the default, and the description says the default is unchanged. Could you add this test before the SET … = true line in single_distinct_to_groupby.slt and mention the fix in the description?

# stddev cannot run DISTINCT itself, so it is rewritten even next to count(*).
query IIR rowsort
SELECT g, count(*), stddev(DISTINCT x) FROM t_prefer_mem GROUP BY g;
----
1 2 7.071067811865
2 1 NULL

Comment on lines +236 to +244
// Resolve the data type of each distinct argument.
let all_fixed_width = distinct_aggs.iter().all(|(_, args)| {
args.iter().all(|arg| {
arg.get_type(input_schema)
.map(|dt| is_fixed_width_type(&dt))
.unwrap_or(false)
})
});
if all_fixed_width {

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.

Optional cleanup: .unwrap_or(false) silently keeps the rewrite when a type can't be resolved, while distinct_groups_support above returns the error with ?. This does the same with one pass:

Suggested change
// Resolve the data type of each distinct argument.
let all_fixed_width = distinct_aggs.iter().all(|(_, args)| {
args.iter().all(|arg| {
arg.get_type(input_schema)
.map(|dt| is_fixed_width_type(&dt))
.unwrap_or(false)
})
});
if all_fixed_width {
let arg_types = distinct_aggs
.iter()
.flat_map(|(_, args)| args.iter())
.map(|arg| arg.get_type(input_schema))
.collect::<Result<Vec<_>>>()?;
if arg_types.iter().all(is_fixed_width_type) {

@mohitgurav20

Copy link
Copy Markdown
Contributor Author

Yeah that's a legit bug, thanks for catching it. The problem is is_fixed_width_type was lumping floats in with integers, which breaks down because the two codepaths disagree on -0.0/0.0 — GROUP BY treats them equal (IEEE 754), but the native accumulator hashes raw bits so they end up in separate buckets. Net result: count(DISTINCT f) changes from 2 to 3 on your example, which is wrong.

Pulled floats out of is_fixed_width_type so prefer_memory never skips the rewrite for float columns. Added a plan-level unit test confirming the rewrite still fires with prefer_memory=true on a Float64 column, and the .slt test with (0.0, -0.0, 1.5) verifying the count stays at 2 under both settings.

On the config overlap with #25988 — makes sense to let @mkleen weigh in first before I touch that. If the consensus is to fold both into one knob I'm happy to do that in a follow-up.

@mohitgurav20

Copy link
Copy Markdown
Contributor Author

@mkleen @jayzhan211 i have worked on it according to the changes you said . pls feel free to review and suggest for any changes .

@mkleen

mkleen commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

@mkleen one question for you: with a memory limit and spilling on, this setting never lowered the limit a query needed in my runs, and at 4 target partitions it raised it 4–12x (numbers in the config.rs comment). It only lowers peak memory when no limit is set. Is that still the case you want the setting for?

@jayzhan211 Yes, the idea was have an option to prefer memory-consumption-over-performance without setting a memory-limit.

@mkleen

mkleen commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

On the config overlap with #25988 — makes sense to let @mkleen weigh in first before I touch that. If the consensus is to fold both into one knob I'm happy to do that in a follow-up.

The config overlap with single_distinct_prefer_native is there. I like the idea to fold both into one setting.

@mohitgurav20

Copy link
Copy Markdown
Contributor Author

@mkleen @jayzhan211 Done — consolidated everything into single_distinct_prefer_native and dropped single_distinct_aggregation_prefer_memory.

Grouped all-distinct queries on fixed-width, non-float types (including sum(DISTINCT int)) now stay native at target_partitions = 1. Multi-partition runs, floats (-0.0 == 0.0), strings, un-grouped queries, and unsupported functions like stddev still rewrite. Docs, schema, and sqllogictests are all updated to match.

@jayzhan211 jayzhan211 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.

@mohitgurav20 thanks for folding this into one setting. I found two things to fix before merge: avg(DISTINCT <duration>) errors with the setting on, and the config doc says the setting lowers memory, but with many small groups it needs about 4x more. Details are inline.

Could you also update the title and description? They still name single_distinct_aggregation_prefer_memory and rewrite_pays, and "user-facing changes" is empty. Please mention that SELECT g, count(*), stddev(DISTINCT x) ... GROUP BY g now works at the default setting (it errors on main).

| DataType::Time32(_)
| DataType::Time64(_)
| DataType::Timestamp(_, _)
| DataType::Duration(_)

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.

With the setting on, avg(DISTINCT d) over a Duration column now fails with AVG(DISTINCT) for (Duration(s) --> Duration(s)) not supported, while the rewrite returns rows. avg has no DISTINCT accumulator for durations. I ran every built-in aggregate against every type in this list with the setting on and off, and this is the only mismatch. Could you drop Duration here and add the test below? It fails without the change.

Suggested change
| DataType::Duration(_)
Test for single_distinct_to_groupby.slt, after DROP TABLE t_prefer_mem_float;
# `avg` has no DISTINCT accumulator for durations, so durations keep the rewrite.
query I?
SELECT g, avg(DISTINCT d) FROM (SELECT g, arrow_cast(x, 'Duration(Second)') AS d FROM t_prefer_mem) GROUP BY g ORDER BY g;
----
1 0 days 0 hours 0 mins 15 secs
2 0 days 0 hours 0 mins 10 secs

Comment thread datafusion/common/src/config.rs Outdated
Comment on lines +1948 to +1953
/// The native plan avoids materializing intermediate `(group, distinct value)`
/// pairs, lowering peak memory when no memory limit is set. However,
/// native DISTINCT must fit each group's distinct values in memory: under
/// a memory limit, the rewrite can spill those pairs, but the native
/// accumulator cannot, so queries that succeed by spilling with the rewrite
/// may fail with this option enabled.

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.

With many small groups, the native plan needs more memory, not less. At one target partition on 4M rows with 1M groups, sum(DISTINCT x) needs a 384M limit (no spilling) with this setting on vs 96M without it, and runs 3.8x slower. sum goes through GroupsAccumulatorAdapter, which keeps one accumulator per group. On the #25932 table (2,000 groups) it does halve memory (192M → 96M), so the doc should say when it helps:

Suggested change
/// The native plan avoids materializing intermediate `(group, distinct value)`
/// pairs, lowering peak memory when no memory limit is set. However,
/// native DISTINCT must fit each group's distinct values in memory: under
/// a memory limit, the rewrite can spill those pairs, but the native
/// accumulator cannot, so queries that succeed by spilling with the rewrite
/// may fail with this option enabled.
/// The native plan avoids materializing intermediate `(group, distinct value)`
/// pairs, which lowers peak memory when there are few groups with many
/// distinct values each. With many small groups it can use more memory and
/// run slower, because functions without a specialized DISTINCT
/// `GroupsAccumulator` (such as `sum`) keep one accumulator per group.
/// Native DISTINCT must also fit each group's distinct values in memory:
/// under a memory limit, the rewrite can spill those pairs, but the native
/// accumulator cannot, so queries that succeed by spilling with the rewrite
/// may fail with this option enabled.

configs.md and information_schema.slt need regenerating after this.

Comment on lines +1484 to +1534
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct UnsupportedUdaf {
signature: Signature,
}

impl UnsupportedUdaf {
fn new() -> Self {
Self {
signature: Signature::exact(
vec![DataType::UInt32],
Volatility::Immutable,
),
}
}
}

impl AggregateUDFImpl for UnsupportedUdaf {
fn name(&self) -> &str {
"unsupported"
}

fn signature(&self) -> &Signature {
&self.signature
}

fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
Ok(DataType::UInt32)
}

fn accumulator(
&self,
_acc_args: AccumulatorArgs,
) -> Result<Box<dyn Accumulator>> {
unimplemented!("not needed for optimizer tests")
}

fn distinct_handling(&self) -> DistinctHandling {
DistinctHandling::Unsupported
}
}

fn unsupported_distinct(expr: Expr) -> Expr {
Expr::AggregateFunction(AggregateFunction::new_udf(
Arc::new(AggregateUDF::from(UnsupportedUdaf::new())),
vec![expr],
true,
None,
vec![],
None,
))
}

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.

Optional cleanup: crate::test::udfs::DistinctHandlingTestUDAF already provides an aggregate with a chosen DistinctHandling, so this struct can go. The snapshot stays the same:

Suggested change
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct UnsupportedUdaf {
signature: Signature,
}
impl UnsupportedUdaf {
fn new() -> Self {
Self {
signature: Signature::exact(
vec![DataType::UInt32],
Volatility::Immutable,
),
}
}
}
impl AggregateUDFImpl for UnsupportedUdaf {
fn name(&self) -> &str {
"unsupported"
}
fn signature(&self) -> &Signature {
&self.signature
}
fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
Ok(DataType::UInt32)
}
fn accumulator(
&self,
_acc_args: AccumulatorArgs,
) -> Result<Box<dyn Accumulator>> {
unimplemented!("not needed for optimizer tests")
}
fn distinct_handling(&self) -> DistinctHandling {
DistinctHandling::Unsupported
}
}
fn unsupported_distinct(expr: Expr) -> Expr {
Expr::AggregateFunction(AggregateFunction::new_udf(
Arc::new(AggregateUDF::from(UnsupportedUdaf::new())),
vec![expr],
true,
None,
vec![],
None,
))
}
fn unsupported_distinct(expr: Expr) -> Expr {
Expr::AggregateFunction(AggregateFunction::new_udf(
Arc::new(AggregateUDF::from(udfs::DistinctHandlingTestUDAF::new(
"unsupported",
DistinctHandling::Unsupported,
))),
vec![expr],
true,
None,
vec![],
None,
))
}

@mohitgurav20

Copy link
Copy Markdown
Contributor Author

@jayzhan211 thanks for your review I'll soon catch up with the changes sirr.

@mohitgurav20
mohitgurav20 force-pushed the fix/single-distinct-groupby-memory-usage branch from cf8fe84 to 678fe74 Compare November 12, 2026 14:39
@mohitgurav20

Copy link
Copy Markdown
Contributor Author

@jayzhan211 sir i have addressed the changes just go through it once whenever u are freee .

@mohitgurav20 mohitgurav20 changed the title fix: Only apply SingleDistinctToGroupBy rewrite when it pays (closes #25932) fix: skip SingleDistinctToGroupBy rewrite when single_distinct_prefer_native is set (closes #25932) Nov 13, 2026

@jayzhan211 jayzhan211 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.

@mohitgurav20 thanks, everything from the last round is addressed. One optional doc fix inline.

Comment thread datafusion/common/src/config.rs Outdated
Comment on lines +1943 to +1959
/// When `true`, the `SingleDistinctToGroupBy` optimizer rule keeps a grouped
/// DISTINCT aggregate native, instead of rewriting it into a two-phase `GROUP BY`,
/// when every DISTINCT argument is a fixed-width, non-float type. This lowers peak
/// memory when no memory limit is set, because the intermediate
/// `(group, distinct value)` result is never built.
///
/// The native plan can avoid materializing distinct (group, value) pairs,
/// but can be slower for many groups with duplicate values. It must fit
/// each group's distinct values in memory, so queries that succeed by
/// spilling with the rewrite may fail with this option enabled. One target
/// partition can still have multiple input partitions and merge native states.
/// The native plan avoids materializing intermediate `(group, distinct value)`
/// pairs, which lowers peak memory when there are few groups with many
/// distinct values each. With many small groups it can use more memory and
/// run slower, because functions without a specialized DISTINCT
/// `GroupsAccumulator` (such as `sum`) keep one accumulator per group.
/// Native DISTINCT must also fit each group's distinct values in memory:
/// under a memory limit, the rewrite can spill those pairs, but the native
/// accumulator cannot, so queries that succeed by spilling with the rewrite
/// may fail with this option enabled.
///
/// Queries without `GROUP BY`, and other argument types, keep the rewrite.

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.

Optional cleanup: this doc no longer says the setting only applies at target_partitions = 1 with all-DISTINCT aggregates, so with default settings turning it on does nothing. Its first paragraph also says memory always goes down, while the second says many small groups need more. Last, "the native accumulator cannot" spill was my wording and it's wrong: at one partition with 50k groups it spilled 12 times and matched the rewrite. What it can't do is split one large group. This version fixes all three (I checked that dev/update_config_docs.sh and information_schema.slt pass with it):

Suggested change
/// When `true`, the `SingleDistinctToGroupBy` optimizer rule keeps a grouped
/// DISTINCT aggregate native, instead of rewriting it into a two-phase `GROUP BY`,
/// when every DISTINCT argument is a fixed-width, non-float type. This lowers peak
/// memory when no memory limit is set, because the intermediate
/// `(group, distinct value)` result is never built.
///
/// The native plan can avoid materializing distinct (group, value) pairs,
/// but can be slower for many groups with duplicate values. It must fit
/// each group's distinct values in memory, so queries that succeed by
/// spilling with the rewrite may fail with this option enabled. One target
/// partition can still have multiple input partitions and merge native states.
/// The native plan avoids materializing intermediate `(group, distinct value)`
/// pairs, which lowers peak memory when there are few groups with many
/// distinct values each. With many small groups it can use more memory and
/// run slower, because functions without a specialized DISTINCT
/// `GroupsAccumulator` (such as `sum`) keep one accumulator per group.
/// Native DISTINCT must also fit each group's distinct values in memory:
/// under a memory limit, the rewrite can spill those pairs, but the native
/// accumulator cannot, so queries that succeed by spilling with the rewrite
/// may fail with this option enabled.
///
/// Queries without `GROUP BY`, and other argument types, keep the rewrite.
/// When `true` and `target_partitions` is 1, the `SingleDistinctToGroupBy`
/// optimizer rule keeps a grouped aggregate whose aggregates are all DISTINCT
/// native, instead of rewriting it into a two-phase `GROUP BY`, when every
/// DISTINCT argument is an integer, boolean, decimal, date, time, timestamp
/// or interval type.
///
/// The native plan avoids materializing intermediate `(group, distinct value)`
/// pairs, which lowers peak memory when there are few groups with many
/// distinct values each. With many small groups it can use more memory and
/// run slower, because functions without a specialized DISTINCT
/// `GroupsAccumulator` (such as `sum`) keep one accumulator per group.
/// Native DISTINCT must also fit each group's distinct values in memory:
/// under a memory limit, it can spill whole groups but not split one group's
/// distinct values, so a query with a few large groups that succeeds by
/// spilling with the rewrite may fail with this option enabled.
///
/// Queries without `GROUP BY`, and other argument types, keep the rewrite.

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.

@jayzhan211 sirr i Applied the updated doc wording across config.rs, configs.md, and information_schema.slt. Verified information_schema.slt and the Prettier checks pass cleanly. Thanks for catching the spill distinction.

@alamb alamb 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.

Thank you @mohitgurav20 and @jayzhan211 . I think this code looks ok, but I think we should clean up the tests and comments if possible before merging

cc @mkleen

Comment thread datafusion/common/src/config.rs Outdated
/// and every aggregate explicitly supports a specialized GroupsAccumulator
/// for its DISTINCT argument types. Defaults to false, preserving the
/// two-phase GROUP BY rewrite.
/// When `true` and `target_partitions` is 1, the `SingleDistinctToGroupBy`

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.

Can we please rephrase this so it starts with the impact of this knob -- I think it is "queries with a single DISTINCT aggregate will take less memory but also run more slowly"?

i think all the background of the tradeoff is way too much detail for the config docs -- it probably belongs on the SingleDistinctToGroupBy docs, with perhaps a link from the config


/// Check whether all aggregate exprs are eligible for the single-distinct-to-group-by rewrite.
///
/// With `use_native_grouped_distinct` enabled (which requires one target partition,

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.

can you please summarieze this paragraph -- it has too much irrelevant detail (eg. restating what use_native_grouped_distinct does in English)

Ok(())
}

fn unsupported_distinct(expr: Expr) -> Expr {

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.

If these can be .slt tests, I think we should rewrite them to be slt -- the rust is far more verbose

@mohitgurav20

mohitgurav20 commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor Author

thanks @alamb addressed all three points in the latest commit:

Rephrased the config doc to lead with the memory vs performance impact, moving the deeper background into SingleDistinctToGroupBy docs.
Trimmed the comment on is_single_distinct_agg down to just the key eligibility rules.
Replaced the verbose Rust plan-builder tests with EXPLAIN transcript queries in single_distinct_to_groupby.slt covering the same edge cases (un-grouped, strings, floats, unsupported UDAFs).

@mohitgurav20
mohitgurav20 requested a review from alamb November 14, 2026 19:17
@mohitgurav20

Copy link
Copy Markdown
Contributor Author

all checks are running green pls fell free to review this pr .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

common Related to common crate documentation Improvements or additions to documentation optimizer Optimizer rules sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sum(DISTINCT x) with GROUP BY uses twice the memory or more

7 participants