Skip to content

Rich t kid/dictionary encoding hash optmize#21589

Open
Rich-T-kid wants to merge 11 commits intoapache:mainfrom
Rich-T-kid:rich-t-kid/Dictionary-encoding-Hash-optmize
Open

Rich t kid/dictionary encoding hash optmize#21589
Rich-T-kid wants to merge 11 commits intoapache:mainfrom
Rich-T-kid:rich-t-kid/Dictionary-encoding-Hash-optmize

Conversation

@Rich-T-kid
Copy link
Copy Markdown
Contributor

@Rich-T-kid Rich-T-kid commented Apr 13, 2026

Which issue does this PR close?

This PR make an effort towards #7000 & closing materializing dictionary columns + #21466

Rationale for this change

This PR implements a specialized GroupValues implementation for single-column dictionary-encoded GROUP BY columns, motivated by the dictionary encoding efficiency work tracked in #7647. GroupValuesRows was inefficient for dictionary-encoded columns because it runs the full RowConverter pipeline on every row, decoding the dictionary back to its underlying string values and serializing them into a packed row format for comparison, completely discarding the integer key structure that dictionary encoding provides and doing O(n) expensive string operations when the same d distinct values could be processed just once.
Initial approach
the first implementation used HashMap<ScalarValue, usize> to map group values to group indices. While correct, profiling revealed this was significantly slower than GroupValuesRows due to per-row heap allocation from ScalarValue::try_from_array, with ~60% of intern time spent on allocation and deallocation.
Final approach
after profiling and iteration the implementation now uses a two-pass strategy that directly exploits dictionary encoding's structure. Pass 1 iterates the small values array (d distinct values) once, building a key_to_group lookup via raw byte comparison and pre-computed hashes. Pass 2 iterates the keys array (n rows) using only cheap array index lookups — no hashing, no byte comparison, no hashmap lookup in the hot path. This reduces the expensive work from O(n) to O(d) per batch.
Benchmarks show consistent 1.9x–2.7x improvement over GroupValuesRows across all cardinality and batch size configurations with no regressions.

What changes are included in this PR?

Update the match statement in new_group_values to include a custom dictionary encoding branch that works for single fields that are of type Dictionary array

Are these changes tested?

Yes a large portion of the PR are test, these include

  1. benchmark's to show a significant improvement over the generic GroupValueRows implementation
  2. functional test that prove the logically the group values is upheld

Are there any user-facing changes?

No everything is internal.

@github-actions github-actions bot added the physical-plan Changes to the physical-plan crate label Apr 13, 2026
@Rich-T-kid
Copy link
Copy Markdown
Contributor Author

Currenly the CI is breaking because my current emit implementations returns the values dirrectly instead of returning a dictionary encoded column. & im not handling nulls.

@Rich-T-kid Rich-T-kid force-pushed the rich-t-kid/Dictionary-encoding-Hash-optmize branch from 8ea71fd to cc18a8f Compare April 13, 2026 16:07
@Rich-T-kid
Copy link
Copy Markdown
Contributor Author

run benchmarks

@adriangbot
Copy link
Copy Markdown

@gabotechs
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-c4242152155-1208-bgwdd 6.12.55+ #1 SMP Sun Feb 1 08:59:41 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 rich-t-kid/Dictionary-encoding-Hash-optmize (aa69892) to 37cd3de (merge-base) diff using: 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-c4242152155-1210-wjf8p 6.12.55+ #1 SMP Sun Feb 1 08:59:41 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 rich-t-kid/Dictionary-encoding-Hash-optmize (aa69892) to 37cd3de (merge-base) diff using: 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-c4242152155-1209-85flv 6.12.55+ #1 SMP Sun Feb 1 08:59:41 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 rich-t-kid/Dictionary-encoding-Hash-optmize (aa69892) to 37cd3de (merge-base) diff using: 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)

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 rich-t-kid_Dictionary-encoding-Hash-optmize
--------------------
Benchmark tpch_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query     ┃                           HEAD ┃ rich-t-kid_Dictionary-encoding-Hash-optmize ┃    Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ QQuery 1  │ 45.17 / 46.04 ±0.64 / 47.09 ms │              45.38 / 46.02 ±0.75 / 47.46 ms │ no change │
│ QQuery 2  │ 21.03 / 21.25 ±0.27 / 21.74 ms │              21.07 / 21.53 ±0.50 / 22.48 ms │ no change │
│ QQuery 3  │ 31.41 / 31.56 ±0.13 / 31.76 ms │              31.42 / 31.94 ±0.58 / 33.07 ms │ no change │
│ QQuery 4  │ 19.91 / 21.17 ±0.91 / 22.24 ms │              20.26 / 21.16 ±0.68 / 22.29 ms │ no change │
│ QQuery 5  │ 47.80 / 49.52 ±1.35 / 51.30 ms │              47.54 / 48.55 ±1.68 / 51.89 ms │ no change │
│ QQuery 6  │ 16.94 / 17.08 ±0.15 / 17.36 ms │              17.19 / 17.51 ±0.45 / 18.39 ms │ no change │
│ QQuery 7  │ 53.99 / 54.85 ±0.55 / 55.66 ms │              53.89 / 55.01 ±0.67 / 55.91 ms │ no change │
│ QQuery 8  │ 47.06 / 49.08 ±2.01 / 52.71 ms │              47.95 / 48.21 ±0.30 / 48.78 ms │ no change │
│ QQuery 9  │ 54.51 / 55.99 ±2.06 / 60.00 ms │              53.86 / 54.75 ±0.83 / 55.98 ms │ no change │
│ QQuery 10 │ 70.66 / 71.15 ±0.31 / 71.53 ms │              70.10 / 70.70 ±0.51 / 71.28 ms │ no change │
│ QQuery 11 │ 13.78 / 14.34 ±0.56 / 15.22 ms │              13.77 / 14.14 ±0.37 / 14.80 ms │ no change │
│ QQuery 12 │ 27.96 / 28.18 ±0.15 / 28.42 ms │              27.66 / 28.07 ±0.22 / 28.27 ms │ no change │
│ QQuery 13 │ 37.48 / 38.70 ±0.92 / 39.70 ms │              37.88 / 38.99 ±0.70 / 39.97 ms │ no change │
│ QQuery 14 │ 28.24 / 28.44 ±0.15 / 28.63 ms │              28.78 / 29.13 ±0.41 / 29.77 ms │ no change │
│ QQuery 15 │ 33.05 / 33.53 ±0.50 / 34.48 ms │              33.72 / 33.88 ±0.19 / 34.22 ms │ no change │
│ QQuery 16 │ 15.91 / 16.05 ±0.12 / 16.20 ms │              15.99 / 16.51 ±0.36 / 16.91 ms │ no change │
│ QQuery 17 │ 72.01 / 73.43 ±1.30 / 75.16 ms │              71.46 / 72.13 ±0.90 / 73.87 ms │ no change │
│ QQuery 18 │ 76.35 / 77.24 ±0.55 / 78.00 ms │              75.79 / 76.78 ±0.77 / 77.81 ms │ no change │
│ QQuery 19 │ 37.37 / 38.02 ±0.76 / 39.20 ms │              37.77 / 38.21 ±0.42 / 38.96 ms │ no change │
│ QQuery 20 │ 39.53 / 40.86 ±0.89 / 42.31 ms │              39.61 / 40.34 ±0.68 / 41.26 ms │ no change │
│ QQuery 21 │ 62.95 / 64.17 ±1.18 / 65.74 ms │              63.24 / 64.95 ±1.25 / 66.57 ms │ no change │
│ QQuery 22 │ 17.13 / 17.57 ±0.32 / 18.01 ms │              17.58 / 18.07 ±0.32 / 18.56 ms │ no change │
└───────────┴────────────────────────────────┴─────────────────────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓
┃ Benchmark Summary                                          ┃          ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩
│ Total Time (HEAD)                                          │ 888.26ms │
│ Total Time (rich-t-kid_Dictionary-encoding-Hash-optmize)   │ 886.59ms │
│ Average Time (HEAD)                                        │  40.38ms │
│ Average Time (rich-t-kid_Dictionary-encoding-Hash-optmize) │  40.30ms │
│ Queries Faster                                             │        0 │
│ Queries Slower                                             │        0 │
│ Queries with No Change                                     │       22 │
│ Queries with Failure                                       │        0 │
└────────────────────────────────────────────────────────────┴──────────┘

Resource Usage

tpch — base (merge-base)

Metric Value
Wall time 4.7s
Peak memory 4.0 GiB
Avg memory 3.6 GiB
CPU user 33.0s
CPU sys 2.8s
Peak spill 0 B

tpch — branch

Metric Value
Wall time 4.7s
Peak memory 4.0 GiB
Avg memory 3.6 GiB
CPU user 33.2s
CPU sys 2.7s
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)

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 rich-t-kid_Dictionary-encoding-Hash-optmize
--------------------
Benchmark tpcds_sf1.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ Query     ┃                                     HEAD ┃ rich-t-kid_Dictionary-encoding-Hash-optmize ┃       Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│ QQuery 1  │           43.47 / 44.23 ±0.78 / 45.71 ms │              44.05 / 45.02 ±0.79 / 45.97 ms │    no change │
│ QQuery 2  │        145.23 / 146.39 ±0.93 / 147.65 ms │           145.70 / 146.81 ±0.83 / 147.82 ms │    no change │
│ QQuery 3  │        113.80 / 115.90 ±1.48 / 118.21 ms │           114.46 / 115.90 ±0.85 / 116.81 ms │    no change │
│ QQuery 4  │    1371.78 / 1402.01 ±29.61 / 1450.43 ms │        1416.99 / 1425.53 ±7.71 / 1439.03 ms │    no change │
│ QQuery 5  │        173.69 / 175.79 ±1.77 / 178.72 ms │           173.11 / 174.30 ±1.57 / 177.39 ms │    no change │
│ QQuery 6  │    1006.29 / 1040.83 ±28.46 / 1092.41 ms │       1019.07 / 1044.28 ±23.82 / 1082.73 ms │    no change │
│ QQuery 7  │        361.17 / 362.79 ±1.31 / 365.12 ms │           355.90 / 359.06 ±2.09 / 361.38 ms │    no change │
│ QQuery 8  │        116.68 / 118.27 ±1.48 / 121.05 ms │           116.67 / 117.92 ±0.83 / 118.87 ms │    no change │
│ QQuery 9  │        101.25 / 104.66 ±2.67 / 108.45 ms │           102.22 / 104.93 ±2.22 / 107.90 ms │    no change │
│ QQuery 10 │        110.43 / 110.74 ±0.21 / 110.97 ms │           109.63 / 110.67 ±0.67 / 111.58 ms │    no change │
│ QQuery 11 │     993.31 / 1020.17 ±19.24 / 1045.98 ms │         982.41 / 999.64 ±10.42 / 1011.76 ms │    no change │
│ QQuery 12 │           44.83 / 45.87 ±0.89 / 47.51 ms │              46.47 / 48.40 ±1.59 / 51.19 ms │ 1.06x slower │
│ QQuery 13 │        406.44 / 407.78 ±1.13 / 409.68 ms │           404.25 / 409.15 ±2.76 / 412.58 ms │    no change │
│ QQuery 14 │     1013.01 / 1024.44 ±8.10 / 1037.43 ms │        1034.70 / 1039.69 ±4.14 / 1045.88 ms │    no change │
│ QQuery 15 │           16.20 / 17.43 ±1.21 / 19.44 ms │              16.34 / 17.12 ±0.85 / 18.67 ms │    no change │
│ QQuery 16 │           41.12 / 42.81 ±2.11 / 46.95 ms │              41.12 / 42.64 ±1.19 / 44.44 ms │    no change │
│ QQuery 17 │        241.53 / 243.67 ±1.95 / 246.84 ms │           242.68 / 244.68 ±1.36 / 246.29 ms │    no change │
│ QQuery 18 │        130.91 / 132.30 ±1.36 / 134.47 ms │           130.95 / 133.23 ±1.43 / 135.45 ms │    no change │
│ QQuery 19 │        155.55 / 156.91 ±1.38 / 159.29 ms │           157.37 / 157.86 ±0.33 / 158.21 ms │    no change │
│ QQuery 20 │           14.07 / 14.34 ±0.14 / 14.47 ms │              14.15 / 14.84 ±0.52 / 15.45 ms │    no change │
│ QQuery 21 │           19.94 / 20.25 ±0.23 / 20.52 ms │              20.18 / 20.47 ±0.19 / 20.73 ms │    no change │
│ QQuery 22 │        494.92 / 498.32 ±4.38 / 506.94 ms │           497.85 / 504.85 ±5.61 / 511.07 ms │    no change │
│ QQuery 23 │        887.05 / 893.50 ±5.67 / 900.91 ms │           907.37 / 921.52 ±8.62 / 932.59 ms │    no change │
│ QQuery 24 │        418.76 / 421.36 ±1.86 / 424.17 ms │           420.90 / 424.29 ±2.19 / 426.53 ms │    no change │
│ QQuery 25 │        353.21 / 356.50 ±1.99 / 358.63 ms │           354.65 / 359.36 ±2.76 / 363.02 ms │    no change │
│ QQuery 26 │           83.77 / 86.03 ±2.73 / 91.04 ms │              82.95 / 85.01 ±1.33 / 86.57 ms │    no change │
│ QQuery 27 │        348.05 / 352.70 ±4.68 / 361.04 ms │           352.46 / 356.08 ±3.22 / 360.59 ms │    no change │
│ QQuery 28 │        148.71 / 150.18 ±0.98 / 151.24 ms │           149.81 / 151.83 ±2.37 / 155.29 ms │    no change │
│ QQuery 29 │        300.83 / 302.91 ±1.52 / 305.08 ms │           301.70 / 303.80 ±1.79 / 306.01 ms │    no change │
│ QQuery 30 │           46.14 / 46.80 ±0.68 / 48.11 ms │              44.37 / 45.95 ±1.15 / 47.78 ms │    no change │
│ QQuery 31 │        172.89 / 173.99 ±1.42 / 176.79 ms │           170.19 / 173.64 ±2.00 / 176.27 ms │    no change │
│ QQuery 32 │           58.50 / 59.31 ±0.55 / 60.11 ms │              58.14 / 59.47 ±0.90 / 60.89 ms │    no change │
│ QQuery 33 │        140.33 / 143.58 ±1.85 / 145.41 ms │           140.52 / 143.16 ±1.88 / 145.68 ms │    no change │
│ QQuery 34 │        108.03 / 108.51 ±0.37 / 109.04 ms │           107.76 / 108.40 ±0.52 / 109.02 ms │    no change │
│ QQuery 35 │        108.41 / 110.19 ±1.62 / 112.64 ms │           110.80 / 112.17 ±0.83 / 113.06 ms │    no change │
│ QQuery 36 │        215.85 / 220.01 ±3.72 / 225.43 ms │           219.67 / 222.15 ±1.86 / 225.37 ms │    no change │
│ QQuery 37 │        177.29 / 181.35 ±2.25 / 183.62 ms │           179.98 / 182.28 ±1.67 / 184.96 ms │    no change │
│ QQuery 38 │           85.58 / 88.84 ±1.67 / 90.30 ms │              84.65 / 90.20 ±3.86 / 96.35 ms │    no change │
│ QQuery 39 │        127.08 / 129.84 ±1.99 / 132.75 ms │           129.32 / 130.89 ±1.39 / 132.50 ms │    no change │
│ QQuery 40 │        112.99 / 117.04 ±3.74 / 123.80 ms │           112.71 / 116.07 ±5.79 / 127.65 ms │    no change │
│ QQuery 41 │           14.25 / 15.05 ±0.45 / 15.61 ms │              14.67 / 15.87 ±0.87 / 16.92 ms │ 1.05x slower │
│ QQuery 42 │        109.00 / 109.61 ±0.59 / 110.73 ms │           107.58 / 109.29 ±1.96 / 113.09 ms │    no change │
│ QQuery 43 │           83.19 / 84.28 ±0.97 / 86.03 ms │              83.98 / 84.52 ±0.63 / 85.70 ms │    no change │
│ QQuery 44 │           11.81 / 12.35 ±0.70 / 13.70 ms │              11.82 / 12.19 ±0.30 / 12.68 ms │    no change │
│ QQuery 45 │           53.06 / 53.53 ±0.34 / 53.94 ms │              53.19 / 54.92 ±1.69 / 58.08 ms │    no change │
│ QQuery 46 │        232.74 / 234.67 ±1.05 / 235.65 ms │           239.16 / 241.42 ±1.71 / 243.98 ms │    no change │
│ QQuery 47 │       724.49 / 733.52 ±11.74 / 756.54 ms │           749.54 / 755.43 ±4.79 / 763.17 ms │    no change │
│ QQuery 48 │        288.06 / 293.59 ±3.06 / 297.27 ms │           289.52 / 297.77 ±5.03 / 304.25 ms │    no change │
│ QQuery 49 │        252.41 / 256.12 ±2.67 / 259.96 ms │           256.08 / 259.72 ±3.29 / 265.41 ms │    no change │
│ QQuery 50 │        226.85 / 233.20 ±3.73 / 237.83 ms │           233.94 / 236.72 ±1.88 / 239.12 ms │    no change │
│ QQuery 51 │        182.49 / 185.22 ±1.51 / 186.85 ms │           182.45 / 185.97 ±2.62 / 189.85 ms │    no change │
│ QQuery 52 │        108.81 / 109.79 ±0.66 / 110.46 ms │           108.47 / 109.81 ±1.04 / 111.62 ms │    no change │
│ QQuery 53 │        103.65 / 104.55 ±0.95 / 106.37 ms │           103.69 / 104.55 ±0.78 / 106.03 ms │    no change │
│ QQuery 54 │        149.54 / 150.94 ±1.14 / 152.67 ms │           147.10 / 150.32 ±1.89 / 152.60 ms │    no change │
│ QQuery 55 │        107.95 / 109.22 ±0.78 / 110.09 ms │           107.01 / 108.82 ±1.73 / 111.87 ms │    no change │
│ QQuery 56 │        141.36 / 142.92 ±0.80 / 143.46 ms │           143.05 / 144.36 ±1.01 / 145.46 ms │    no change │
│ QQuery 57 │        173.39 / 176.29 ±1.70 / 178.24 ms │           176.02 / 179.01 ±2.62 / 183.76 ms │    no change │
│ QQuery 58 │        292.56 / 300.73 ±6.54 / 309.36 ms │           294.78 / 304.57 ±8.71 / 319.87 ms │    no change │
│ QQuery 59 │        198.82 / 199.53 ±0.58 / 200.46 ms │           198.49 / 200.92 ±1.59 / 202.69 ms │    no change │
│ QQuery 60 │        143.53 / 144.67 ±1.16 / 146.75 ms │           146.11 / 147.15 ±0.64 / 147.82 ms │    no change │
│ QQuery 61 │        171.65 / 173.85 ±1.40 / 175.25 ms │           176.33 / 177.65 ±1.08 / 179.58 ms │    no change │
│ QQuery 62 │       890.15 / 920.38 ±28.00 / 967.86 ms │         926.36 / 953.59 ±40.77 / 1033.12 ms │    no change │
│ QQuery 63 │        104.87 / 106.93 ±1.44 / 108.79 ms │           103.17 / 106.27 ±2.32 / 110.19 ms │    no change │
│ QQuery 64 │        698.54 / 708.14 ±6.84 / 719.62 ms │           708.55 / 718.45 ±6.60 / 727.81 ms │    no change │
│ QQuery 65 │        252.43 / 256.06 ±2.13 / 258.48 ms │           262.56 / 264.75 ±2.18 / 268.81 ms │    no change │
│ QQuery 66 │        245.63 / 248.09 ±2.73 / 253.10 ms │          241.14 / 258.57 ±11.39 / 276.86 ms │    no change │
│ QQuery 67 │        321.93 / 328.80 ±6.39 / 338.77 ms │           315.58 / 324.61 ±8.46 / 336.54 ms │    no change │
│ QQuery 68 │        282.59 / 289.51 ±5.77 / 296.49 ms │           281.36 / 288.89 ±4.53 / 293.21 ms │    no change │
│ QQuery 69 │        106.21 / 107.01 ±0.44 / 107.50 ms │           103.55 / 105.55 ±1.20 / 106.85 ms │    no change │
│ QQuery 70 │       338.48 / 354.29 ±10.59 / 369.77 ms │          329.98 / 345.74 ±12.85 / 367.05 ms │    no change │
│ QQuery 71 │        134.73 / 138.65 ±2.47 / 142.39 ms │           134.56 / 135.04 ±0.30 / 135.46 ms │    no change │
│ QQuery 72 │       718.65 / 731.75 ±11.12 / 751.37 ms │          718.08 / 732.86 ±10.58 / 749.90 ms │    no change │
│ QQuery 73 │        106.08 / 107.66 ±1.27 / 109.37 ms │           104.51 / 106.53 ±1.24 / 108.02 ms │    no change │
│ QQuery 74 │        625.24 / 630.79 ±4.97 / 639.20 ms │           604.93 / 609.53 ±3.69 / 616.03 ms │    no change │
│ QQuery 75 │        277.62 / 280.28 ±2.43 / 284.88 ms │           277.29 / 281.55 ±2.38 / 284.27 ms │    no change │
│ QQuery 76 │        133.88 / 135.60 ±1.03 / 136.77 ms │           133.48 / 135.74 ±2.20 / 139.59 ms │    no change │
│ QQuery 77 │        189.58 / 191.52 ±1.65 / 193.96 ms │           189.99 / 191.15 ±1.00 / 192.53 ms │    no change │
│ QQuery 78 │        352.63 / 358.29 ±3.90 / 364.78 ms │           354.58 / 359.88 ±3.68 / 364.23 ms │    no change │
│ QQuery 79 │        239.43 / 243.09 ±2.13 / 245.48 ms │           234.64 / 236.97 ±1.92 / 239.35 ms │    no change │
│ QQuery 80 │        331.10 / 335.01 ±2.74 / 338.29 ms │           332.37 / 335.44 ±2.21 / 337.31 ms │    no change │
│ QQuery 81 │           27.66 / 28.72 ±1.32 / 31.30 ms │              27.31 / 28.07 ±0.50 / 28.85 ms │    no change │
│ QQuery 82 │        202.49 / 204.72 ±1.26 / 206.29 ms │           203.56 / 205.64 ±1.15 / 206.85 ms │    no change │
│ QQuery 83 │           41.47 / 43.44 ±2.25 / 47.84 ms │              41.21 / 42.79 ±1.55 / 45.61 ms │    no change │
│ QQuery 84 │           50.28 / 50.94 ±0.58 / 51.73 ms │              49.94 / 50.89 ±0.94 / 52.51 ms │    no change │
│ QQuery 85 │        152.24 / 153.97 ±1.52 / 156.09 ms │           152.85 / 155.53 ±1.92 / 158.84 ms │    no change │
│ QQuery 86 │           39.92 / 40.39 ±0.48 / 41.12 ms │              39.69 / 40.70 ±1.19 / 42.92 ms │    no change │
│ QQuery 87 │           87.68 / 91.21 ±3.29 / 97.08 ms │             88.43 / 93.25 ±4.13 / 100.68 ms │    no change │
│ QQuery 88 │        101.39 / 102.63 ±1.16 / 104.76 ms │           102.00 / 102.73 ±0.50 / 103.36 ms │    no change │
│ QQuery 89 │        119.50 / 120.86 ±0.79 / 121.89 ms │           120.60 / 122.57 ±1.45 / 124.01 ms │    no change │
│ QQuery 90 │           24.88 / 26.01 ±0.79 / 27.07 ms │              23.96 / 24.91 ±0.66 / 25.79 ms │    no change │
│ QQuery 91 │           63.96 / 66.44 ±1.60 / 68.33 ms │              64.96 / 66.80 ±0.97 / 67.68 ms │    no change │
│ QQuery 92 │           58.59 / 59.54 ±0.64 / 60.57 ms │              58.83 / 59.37 ±0.47 / 59.93 ms │    no change │
│ QQuery 93 │        193.15 / 195.66 ±1.42 / 197.35 ms │           196.93 / 199.22 ±2.14 / 202.10 ms │    no change │
│ QQuery 94 │           62.75 / 63.16 ±0.29 / 63.59 ms │              62.52 / 63.07 ±0.53 / 64.08 ms │    no change │
│ QQuery 95 │        135.00 / 135.61 ±0.81 / 137.20 ms │           136.29 / 138.18 ±1.24 / 140.14 ms │    no change │
│ QQuery 96 │           75.17 / 76.77 ±1.50 / 78.78 ms │              75.26 / 76.58 ±1.04 / 78.21 ms │    no change │
│ QQuery 97 │        132.42 / 134.28 ±1.24 / 136.18 ms │           135.35 / 137.01 ±1.21 / 138.94 ms │    no change │
│ QQuery 98 │        158.14 / 159.00 ±0.75 / 159.93 ms │           158.64 / 160.74 ±1.65 / 163.69 ms │    no change │
│ QQuery 99 │ 10830.51 / 10854.73 ±24.50 / 10884.81 ms │    10795.70 / 10841.72 ±49.04 / 10909.15 ms │    no change │
└───────────┴──────────────────────────────────────────┴─────────────────────────────────────────────┴──────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                                          ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                                          │ 34092.09ms │
│ Total Time (rich-t-kid_Dictionary-encoding-Hash-optmize)   │ 34245.13ms │
│ Average Time (HEAD)                                        │   344.36ms │
│ Average Time (rich-t-kid_Dictionary-encoding-Hash-optmize) │   345.91ms │
│ Queries Faster                                             │          0 │
│ Queries Slower                                             │          2 │
│ Queries with No Change                                     │         97 │
│ Queries with Failure                                       │          0 │
└────────────────────────────────────────────────────────────┴────────────┘

Resource Usage

tpcds — base (merge-base)

Metric Value
Wall time 170.8s
Peak memory 5.8 GiB
Avg memory 4.8 GiB
CPU user 274.4s
CPU sys 17.9s
Peak spill 0 B

tpcds — branch

Metric Value
Wall time 171.5s
Peak memory 5.7 GiB
Avg memory 4.7 GiB
CPU user 275.3s
CPU sys 18.2s
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)

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 rich-t-kid_Dictionary-encoding-Hash-optmize
--------------------
Benchmark clickbench_partitioned.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃                                  HEAD ┃ rich-t-kid_Dictionary-encoding-Hash-optmize ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 0  │          1.31 / 4.52 ±6.32 / 17.16 ms │                1.33 / 4.60 ±6.40 / 17.40 ms │     no change │
│ QQuery 1  │        14.36 / 14.75 ±0.37 / 15.37 ms │              14.74 / 15.44 ±0.39 / 15.90 ms │     no change │
│ QQuery 2  │        45.15 / 45.90 ±0.44 / 46.51 ms │              45.67 / 45.97 ±0.27 / 46.44 ms │     no change │
│ QQuery 3  │        43.83 / 46.26 ±1.38 / 47.91 ms │              44.62 / 46.23 ±1.13 / 47.52 ms │     no change │
│ QQuery 4  │    309.10 / 321.74 ±11.73 / 342.44 ms │          289.85 / 307.70 ±12.53 / 324.09 ms │     no change │
│ QQuery 5  │     348.09 / 354.69 ±4.81 / 362.38 ms │          350.92 / 365.25 ±11.69 / 380.29 ms │     no change │
│ QQuery 6  │           6.39 / 7.14 ±0.44 / 7.75 ms │                 5.24 / 7.01 ±1.63 / 9.81 ms │     no change │
│ QQuery 7  │        16.50 / 17.27 ±0.77 / 18.57 ms │              17.15 / 18.35 ±1.32 / 20.90 ms │  1.06x slower │
│ QQuery 8  │    421.92 / 445.71 ±12.76 / 459.26 ms │           447.41 / 457.46 ±7.84 / 470.36 ms │     no change │
│ QQuery 9  │    644.71 / 661.04 ±18.06 / 689.86 ms │          658.54 / 690.29 ±22.67 / 712.97 ms │     no change │
│ QQuery 10 │      98.82 / 101.15 ±3.67 / 108.44 ms │             95.14 / 98.50 ±2.20 / 101.79 ms │     no change │
│ QQuery 11 │     109.97 / 112.01 ±1.47 / 114.04 ms │           110.39 / 112.60 ±2.45 / 116.95 ms │     no change │
│ QQuery 12 │     375.21 / 381.38 ±7.31 / 393.55 ms │          348.66 / 372.53 ±13.97 / 391.09 ms │     no change │
│ QQuery 13 │     508.99 / 518.26 ±9.71 / 536.14 ms │          459.85 / 477.14 ±12.38 / 498.47 ms │ +1.09x faster │
│ QQuery 14 │     386.55 / 394.85 ±5.98 / 400.51 ms │          361.57 / 373.85 ±14.02 / 399.01 ms │ +1.06x faster │
│ QQuery 15 │     407.09 / 415.24 ±4.26 / 418.45 ms │          359.97 / 387.46 ±23.70 / 421.51 ms │ +1.07x faster │
│ QQuery 16 │    739.93 / 765.87 ±19.89 / 797.45 ms │          766.95 / 778.13 ±15.59 / 808.81 ms │     no change │
│ QQuery 17 │    734.23 / 756.71 ±15.96 / 771.03 ms │           745.17 / 757.69 ±8.83 / 768.23 ms │     no change │
│ QQuery 18 │ 1409.11 / 1442.53 ±25.30 / 1472.57 ms │       1457.81 / 1568.34 ±69.50 / 1657.27 ms │  1.09x slower │
│ QQuery 19 │        37.42 / 39.63 ±1.29 / 41.18 ms │              34.80 / 37.60 ±1.94 / 40.21 ms │ +1.05x faster │
│ QQuery 20 │    711.38 / 725.22 ±13.50 / 749.08 ms │           722.82 / 730.96 ±8.20 / 744.98 ms │     no change │
│ QQuery 21 │     760.60 / 768.69 ±7.65 / 782.52 ms │           770.35 / 775.98 ±4.26 / 781.45 ms │     no change │
│ QQuery 22 │  1139.40 / 1153.50 ±8.00 / 1161.81 ms │        1135.49 / 1143.65 ±9.02 / 1158.54 ms │     no change │
│ QQuery 23 │ 3147.96 / 3175.13 ±19.91 / 3206.23 ms │       3194.41 / 3237.72 ±40.02 / 3294.45 ms │     no change │
│ QQuery 24 │      99.84 / 103.39 ±2.51 / 107.42 ms │           102.11 / 103.19 ±0.97 / 104.62 ms │     no change │
│ QQuery 25 │     141.57 / 143.86 ±1.69 / 146.33 ms │           143.76 / 145.04 ±1.55 / 148.08 ms │     no change │
│ QQuery 26 │     102.78 / 106.52 ±2.78 / 110.28 ms │            99.56 / 103.04 ±2.28 / 106.28 ms │     no change │
│ QQuery 27 │     849.15 / 853.18 ±4.48 / 861.70 ms │          856.51 / 866.61 ±10.35 / 886.52 ms │     no change │
│ QQuery 28 │ 7796.11 / 7839.68 ±39.09 / 7898.77 ms │       7737.26 / 7816.71 ±53.87 / 7906.76 ms │     no change │
│ QQuery 29 │        53.56 / 57.17 ±3.34 / 62.35 ms │              51.58 / 56.22 ±4.71 / 61.98 ms │     no change │
│ QQuery 30 │    363.50 / 384.31 ±17.46 / 415.08 ms │           387.33 / 392.27 ±4.21 / 398.45 ms │     no change │
│ QQuery 31 │    382.22 / 398.28 ±19.61 / 435.89 ms │          392.19 / 410.77 ±17.27 / 436.60 ms │     no change │
│ QQuery 32 │ 1085.51 / 1098.77 ±15.67 / 1126.13 ms │       1101.11 / 1165.55 ±34.42 / 1204.97 ms │  1.06x slower │
│ QQuery 33 │ 1478.07 / 1536.38 ±37.09 / 1592.79 ms │       1536.67 / 1552.46 ±15.92 / 1578.67 ms │     no change │
│ QQuery 34 │ 1493.74 / 1555.45 ±41.89 / 1598.50 ms │       1537.28 / 1559.82 ±17.86 / 1591.86 ms │     no change │
│ QQuery 35 │     449.59 / 463.77 ±8.12 / 472.68 ms │          389.75 / 417.82 ±28.61 / 469.79 ms │ +1.11x faster │
│ QQuery 36 │     128.19 / 134.52 ±5.50 / 143.01 ms │           126.13 / 132.12 ±4.20 / 137.87 ms │     no change │
│ QQuery 37 │        52.79 / 54.39 ±1.50 / 56.45 ms │              47.01 / 50.35 ±2.16 / 52.30 ms │ +1.08x faster │
│ QQuery 38 │        76.74 / 79.07 ±1.85 / 81.96 ms │              75.72 / 78.66 ±2.34 / 81.25 ms │     no change │
│ QQuery 39 │    209.69 / 233.23 ±12.57 / 244.59 ms │          210.60 / 225.24 ±13.14 / 244.64 ms │     no change │
│ QQuery 40 │        25.85 / 28.28 ±2.39 / 32.06 ms │              23.36 / 28.01 ±3.12 / 33.00 ms │     no change │
│ QQuery 41 │        20.22 / 22.48 ±1.64 / 24.47 ms │              20.56 / 21.36 ±0.64 / 22.37 ms │     no change │
│ QQuery 42 │        20.22 / 20.76 ±0.44 / 21.40 ms │              19.12 / 20.01 ±0.81 / 21.50 ms │     no change │
└───────────┴───────────────────────────────────────┴─────────────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                                          ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                                          │ 27782.69ms │
│ Total Time (rich-t-kid_Dictionary-encoding-Hash-optmize)   │ 27955.70ms │
│ Average Time (HEAD)                                        │   646.11ms │
│ Average Time (rich-t-kid_Dictionary-encoding-Hash-optmize) │   650.13ms │
│ Queries Faster                                             │          6 │
│ Queries Slower                                             │          3 │
│ Queries with No Change                                     │         34 │
│ Queries with Failure                                       │          0 │
└────────────────────────────────────────────────────────────┴────────────┘

Resource Usage

clickbench_partitioned — base (merge-base)

Metric Value
Wall time 140.1s
Peak memory 41.3 GiB
Avg memory 33.8 GiB
CPU user 1324.2s
CPU sys 90.7s
Peak spill 0 B

clickbench_partitioned — branch

Metric Value
Wall time 141.0s
Peak memory 41.2 GiB
Avg memory 32.4 GiB
CPU user 1326.4s
CPU sys 95.5s
Peak spill 0 B

File an issue against this benchmark runner

@Rich-T-kid
Copy link
Copy Markdown
Contributor Author

Wrote some criterion benchmarks and they reflect what the query benchmarks show. I think storing Scalar values directly in the hash map is causing performance issues. Need to profile to see heap allocations.

@Rich-T-kid
Copy link
Copy Markdown
Contributor Author

ScalarValue::try_from_array (38%) — allocating a new ScalarValue heap object for every single row
hash_one (18%) — hashing the full ScalarValue including its string contents on every row
PartialEq::eq (5.8%) — comparing ScalarValues in the HashMap on every row

lots of heap allocations are happening on the hot path (_xzm_free) - a solution to this may be to pre-allocate space for the unique values array and hashmap.
the core problem with using ScalarValue in the hot path, it's a heap allocated enum that gets created and destroyed for every single row. Should work directly with the array buffers

@Rich-T-kid
Copy link
Copy Markdown
Contributor Author

Image 4-14-26 at 1 55 PM

@Rich-T-kid
Copy link
Copy Markdown
Contributor Author

Optimization 1 — Hash caching

Instead of hashing each row's value individually, pre-compute hashes for all d distinct values in the values array once per batch and cache them in a Vec indexed by dictionary key. For a batch of n rows with d distinct values this reduces hash computations from O(n) to O(d).

Optimization 2 — Eliminate ScalarValue

Replace HashMap<ScalarValue, usize> with a structure that stores raw hashes and raw string slices pointing directly into the Arrow buffer. This eliminates per-row heap allocation (ScalarValue::try_from_array) and deallocation (drop_in_place) which the profiler shows accounts for ~60% of intern time combined.

Optimization 3 — Pre-allocate using occupancy

Use dict_array.occupancy().count_set_bits() to determine the number of truly distinct non-null values in the batch upfront and pre-allocate internal storage accordingly. This avoids incremental Vec growth during intern.

@Rich-T-kid Rich-T-kid force-pushed the rich-t-kid/Dictionary-encoding-Hash-optmize branch 3 times, most recently from 8adc7ef to c8fe15a Compare April 15, 2026 03:29
@Rich-T-kid Rich-T-kid force-pushed the rich-t-kid/Dictionary-encoding-Hash-optmize branch 2 times, most recently from 796573d to 2d7bc48 Compare April 15, 2026 15:25
@Rich-T-kid Rich-T-kid force-pushed the rich-t-kid/Dictionary-encoding-Hash-optmize branch 2 times, most recently from a3ad48e to eb28497 Compare April 15, 2026 15:45
@Rich-T-kid
Copy link
Copy Markdown
Contributor Author

Rich-T-kid commented Apr 15, 2026

Benchmark Results: Dictionary vs GroupValueRows (single dict column)

Benchmarks were parameterized across cardinality (XSmall/Small/Medium/Large), batch size (Small=8192 / Medium=32768 / Large=65536), and null rate (Zero/Low/Medium/High). see [single_column_aggr.rs] for the full benchmark code.

XSmall cardinality, Small batch

Zero nulls: GroupValueRows 73.1 µs → Dictionary 33.9 µs (~2.2x)
High nulls: GroupValueRows 94.1 µs → Dictionary 32.5 µs (~2.9x)

XSmall cardinality, Medium batch

Zero nulls: GroupValueRows 280.5 µs → Dictionary 143.6 µs (~2.0x)
High nulls: GroupValueRows 389.9 µs → Dictionary 131.8 µs (~3.0x)

Large cardinality, Large batch

Zero nulls: GroupValueRows 568.1 µs → Dictionary 217.4 µs (~2.6x)
High nulls: GroupValueRows 836.6 µs → Dictionary 253.7 µs (~3.3x)

Large cardinality, Large batch (multi_batch)

Zero nulls: GroupValueRows 1.668 ms → Dictionary 878 µs (~1.9x)
High nulls: GroupValueRows 2.519 ms → Dictionary 741.6 µs (~3.4x)

Dictionary is consistently 2–3.4x faster across all configurations. The speedup grows with higher null rates, as nulls increase, Dictionary benefits from its internal value caching, avoiding redundant work on null entries.

Full benchmarks below

single_dict_column_result.txt

Note
I've updated the code slightly since running these benchmarks. going to run them again and place the results in a separate comment
.

@Rich-T-kid Rich-T-kid force-pushed the rich-t-kid/Dictionary-encoding-Hash-optmize branch from eb28497 to b84ec0b Compare April 15, 2026 17:44
@Rich-T-kid Rich-T-kid force-pushed the rich-t-kid/Dictionary-encoding-Hash-optmize branch from b84ec0b to deec858 Compare April 15, 2026 17:50
@Rich-T-kid Rich-T-kid marked this pull request as ready for review April 15, 2026 18:46
@Rich-T-kid
Copy link
Copy Markdown
Contributor Author

CC @tustvold @qrilka, @blaginin given your work on Materialize Dictionaries in Group Keys & Distinct(count x) , would love your thoughts on this.

Comment thread datafusion/physical-plan/profile.json.gz
Comment thread datafusion/physical-plan/Cargo.toml
Comment thread datafusion/physical-plan/src/aggregates/row_hash.rs
Comment thread datafusion/physical-plan/src/aggregates/row_hash.rs
Comment on lines +266 to +276
fn supported_single_dictionary_value(t: &DataType) -> bool {
matches!(
t,
DataType::Utf8
| DataType::LargeUtf8
| DataType::Binary
| DataType::LargeBinary
| DataType::Utf8View
| DataType::BinaryView
)
}
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.

These are the most common types, but why not support other types? Other types would fall under the slow path no?

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.

This PR is intended to serve as a proof of concept that dictionary encoding works and improves efficiency. It makes sense to start with string types since that is what dictionary arrays are most commonly used with and where they provide the most benefit. A follow up issue can be created to support additional types such as LargeUtf8, LargeList, and numeric values ,which should be as minimal as adding the relevant branches in get_raw_bytes and sentinel_repr. This is also related to how sentinel representations of types work but I think thats worth a separate comment.

Comment thread datafusion/physical-plan/src/aggregates/group_values/mod.rs
@@ -20,4 +20,5 @@
pub(crate) mod boolean;
pub(crate) mod bytes;
pub(crate) mod bytes_view;
pub mod dictionary;
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.

why not pub(crate) ? otherwise we leak GroupValuesDictionary in the public DF API

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.

this was only to expose the GroupValuesDictionary::new() method for performance benchmarks.

let raw = Self::get_raw_bytes(values, value_idx);
if let Some((group_id, _)) = entries
.iter()
.find(|(_, stored_bytes)| raw == stored_bytes.as_slice())
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.

Is this comparison for hash collisions?

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.

Yes, there's a chance of hash collisions where hash_array[i] == hash_array[j] but original_array[i] != original_array[j]. So when a hash already exists in the hash table, we can't just return that group id, we need to verify the actual value matches. We iterate through the vector of values that map to this hash, comparing each one to find the entry where the actual value matches, and return that group id. If no match is found it's a new group.


*/
// stores the order new unique elements are seen for self.emit()
seen_elements: Vec<Vec<u8>>, // Box<dyn Builder> doesnt provide the flexibility of building partition arrays that wed need to support emit::First(N)
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.

maybe instread of a Vec<Vec<u8>> we can use another structure like ArrowBytesMap doc: https://docs.rs/datafusion/latest/datafusion/physical_expr_common/binary_map/struct.ArrowBytesMap.html , that would provide better memory tracking but I don't think the insert functions support dictionary right now

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.

I think this is a good idea with the current approach. The only issue I can think of is in the future when other value types are supported. ArrowBytesMap only supports,
Utf8,
Utf8View,
Binary,
BinaryView,

in my revised PR I could abstract away how seen_elements are stored into a trait comprised ofstore() retrive(N).This should make it easier for future work to integrate with ArrowDictionaryKeyType while also giving us a performance boost in the string/binary case

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.

Also I think its worth benchmarking this. ArrowBytesMap insert api forces the client to insert arrays, not single elements. its very unlikely that the keys array is a 1-1 mapping of the values array, this means for each insertion we'll to do a Arc::new(StringArray::from(values[idx])). the overhead that comes with creating a new array (heap allocation for the buffer + heap allocation for the offsets + heap allocation for the null bitmap) might negate the benefits the specialized map provides.

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.

Also the ArrowBytesMap doesnt allow for partial takes. each call to retrieve values from ArrowBytesMap drains the entire data structure, this doesnt blend well with emit since it allows for partial takes.

@Rich-T-kid
Copy link
Copy Markdown
Contributor Author

Null handling

How nulls are currently handled
The first time a null is encountered, a sentinel byte representation is written to seen_elements as well as the hash map. This sentinel exists so that during emit(), when transform_into_array is called, we can compare the raw bytes of each entry against the sentinel to distinguish a null from a real value — ensuring that a null is appended to the output array as an actual null rather than being written out as a value. Without this, there would be no way to tell from the raw byte representation alone whether an entry represents a null or a legitimate value that happens to have the same bytes.

Why this is hard to extend to other types

This approach works for Utf8 because there exist byte sequences that are invalid UTF-8, which can serve as an unambiguous sentinel value that could never be confused with real data. However, this is difficult to extend to other types. For example, there is no equivalent concept of an 'invalid' binary buffer, since any sequence of raw bytes is valid by definition. This problem extends to numeric types as well.
One idea was to use an n+1 sized buffer to signal a null state for example, representing a null i32 as 5 bytes instead of the expected 4, where the extra byte acts as a flag to distinguish it from a valid value. However this feels clunky, adds overhead to every value comparison, and still doesn't resolve the core issue for binary types where no such invalid state can be expressed through the raw byte representation alone. This is worth its own investigation and is another reason to defer non-string types to a follow up issue.

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

Labels

physical-plan Changes to the physical-plan crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants