Skip to content

Split the partitioned hash join dynamic filter into bounds AND membership - #24235

Draft
adriangb wants to merge 3 commits into
apache:mainfrom
pydantic:prototype/split-dynamic-filter
Draft

Split the partitioned hash join dynamic filter into bounds AND membership#24235
adriangb wants to merge 3 commits into
apache:mainfrom
pydantic:prototype/split-dynamic-filter

Conversation

@adriangb

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

  • None. This is a prototype opened as a draft, for evaluation only — I want the project's benchmark fleet to judge it, not to merge it as-is. See "Honest summary" below: on TPC-H the bounds split does not pay for itself, and I would rather have that measured on neutral hardware than argued about.

Rationale for this change

A dynamic filter produced by a hash-partitioned join currently reaches the scan as a single opaque conjunct:

DynamicFilter[ CASE hash_repartition % n
                 WHEN 0 THEN bounds_0 AND membership_0
                 WHEN 1 THEN bounds_1 AND membership_1
                 ... END ]

Everything the join knows is sealed inside one expression. split_conjunction splits only on a top-level BinaryExpr(And) and does not descend into a DynamicFilterPhysicalExpr, so row_filter.rs builds exactly one ArrowPredicate: every probe row must have its routing hash computed before any row can be rejected, and row-group pruning can make nothing of a CASE.

This PR pushes two filters instead:

DynamicFilter[ merged bounds ] AND DynamicFilter[ membership ]

Two wrappers, not two expressions inside one wrapper, is the load-bearing detail. Split in two, each becomes its own ArrowPredicate, and arrow-rs applies them in sequence against an accumulating RowSelection — building a fresh array reader per predicate over only the still-selected rows. The intent is that the cheap vectorized range check runs first and the routing hash plus hash-table lookup only see the survivors.

Honest summary: on TPC-H, the bounds half does not pay for itself

The idea above is sound but the premise fails on this workload, for a structural reason worth stating plainly. Hash routing scatters keys uniformly across partitions, so every partition's key range converges on the global domain. The union of those ranges is then very close to the full column domain. TPC-H q3's merged bound is l_orderkey ∈ [96, 5999975] against a ~6M-row domain: it rejects roughly 0.002% of rows. The cheap first predicate compacts nothing, so all it can do is add cost.

The part that carries real value is the InList collapse (third commit). When every non-empty build partition pushes an InList, the routing CASE is redundant — routing is a deterministic function of the key columns, so testing a key against the union of the lists accepts and rejects precisely what the CASE does. That is an equality, not a relaxation, and it removes the routing hash from the probe path entirely, leaving an InListExpr that datafusion/pruning actually understands. That is where the measured wins come from.

The catch is that it fires rarely: hash_join_inlist_pushdown_max_size is 128 KiB with a 150-distinct-value cap, per partition, so most joins never produce an all-InList build.

Measurements

Re-measured from scratch, because an earlier round of numbers was not trustworthy. Protocol:

  • Both binaries built from the same worktree and the same target dir (build change → copy binary aside → revert to base → rebuild). An earlier comparison across two worktrees at different upstream commits attributed hundreds of unrelated commits to this change; its tell was a 6.7% delta on a control query containing no joins.
  • Counterbalanced ordering within each round — (branch, base) on odd rounds, (base, branch) on even — reported separately as well as pooled.
  • Controls: TPC-H q1 and q6 contain no joins, so this PR cannot affect them. Their delta is the noise floor. Nothing smaller than it is reported as a result.
  • 12 rounds × --iterations 5, TPC-H SF=1 parquet. The machine was not idle and was not quiesced.

Primary statistic is the paired per-round delta (median over 12 rounds of branch/base − 1); pairing cancels round-level machine drift, which matters because the unpaired cross-round spread exceeds 100% on some queries. Positive = branch slower. p is an exact two-sided sign test over the 12 paired rounds.

pushdown_filters = false — control noise floor 5.09%

query paired Δ% branch-first Δ% base-first Δ% rounds branch faster sign p
q1 (control) +5.09 +10.83 −0.96 5/12 0.774
q6 (control) +1.10 −9.51 +2.69 5/12 0.774
q3 +2.08 +0.26 +4.06 5/12 0.774
q5 −4.28 −4.97 −2.02 7/12 0.774
q9 +4.74 +7.09 +4.74 1/12 0.006
q17 +8.33 +10.50 +6.24 2/12 0.039
q18 +1.35 +2.26 +1.35 5/12 0.774

pushdown_filters = true — control noise floor 1.99%

query paired Δ% branch-first Δ% base-first Δ% rounds branch faster sign p
q1 (control) +1.99 −1.47 +7.16 4/12 0.388
q6 (control) −0.38 −8.19 +3.78 6/12 1.000
q3 −0.72 −1.19 +1.58 7/12 0.774
q5 −1.14 −5.66 +7.93 6/12 1.000
q9 +5.34 +10.16 +2.63 3/12 0.146
q17 −33.76 −34.61 −33.08 12/12 0.000
q18 −11.44 −11.14 −12.55 9/12 0.146

Reading these:

  • The previously claimed "~4% cost at pushdown_filters=true" did NOT survive correct measurement. It is not a smaller cost than reported — it is not there. On q3 the paired delta is −0.72%, i.e. the sign flipped, with 7 of 12 rounds actually favouring the branch (p = 0.77); on q5 it is −1.14%, 6/12, p = 1.00. Both sit inside a 1.99% control floor, so neither direction is resolvable. That earlier ~4% figure was an artifact of the confounded protocol described above (chiefly the cross-worktree build), and it should not be carried forward.
  • q17 −33.8% at pushdown_filters=true is unambiguous: 12/12 rounds, both orderings agree to within 1.5 points, far outside any floor. q18 −11.4% is consistent in direction across both orderings (9/12 rounds). Both are the InList collapse, not the bounds split.
  • The residual cost instead shows up at pushdown_filters=false, on q9 (+4.7%, only 1 of 12 rounds faster, p = 0.006) and q17 (+8.3%, 2/12, p = 0.039). To be explicit about how much weight these carry: the control floor in this mode is 5.09%, so both rest on the paired sign test — the consistency of the direction across rounds and across both orderings — rather than on their magnitude. q9's +4.7% is below that floor outright; what makes it worth reporting is that the branch lost 11 of 12 paired rounds, not that the number is large. Read them as "there is probably a small real cost here", not as calibrated cost estimates.
  • q17 flips from +8.3% at pushdown_filters=false to −33.8% at pushdown_filters=true. That is the expected shape rather than a contradiction: the InList collapse only pays when the dynamic filter is actually evaluated per row inside the scan, which is precisely what pushdown_filters=true turns on. With pushdown off, the filter never reaches the row-level evaluation path where removing the routing hash would help — but the extra wrapper, the bounds union, and the second DynamicFilter are constructed and charged either way. So the same change is a small cost in one mode and a large win in the other.
  • q1's control row at pushdown_filters=true (+1.99% pooled, but −10.18% branch-first vs +10.02% base-first in the unpaired view) is a good advertisement for reporting orderings separately — a query this change provably cannot touch can still show a double-digit unpaired swing.

The lever this branch did not pull

The obvious next move, which is not attempted here: gate the bounds conjunct on a degeneracy check using probe-side column statistics from right_child.partition_statistics(). When the merged union covers (or nearly covers) the probe column's own min/max domain, the conjunct is known to be useless before a single row is read, and it should simply not be pushed. That is the most likely way to turn the bounds half from a cost into a neutral — it keeps the win in the cases where per-partition ranges genuinely are narrow (correlated or pre-clustered build sides) and stops paying for it on uniformly hashed keys like TPC-H's. I would rather land that gate than tune anything here to make the current numbers look better.

What changes are included in this PR?

Three commits, each building green on its own:

  1. refactor: let a hash join carry more than one self filter — routes handle_child_pushdown_result and dynamic_expressions_produced through one HashJoinExecDynamicFilter::produced_expressions helper and pops the driven filter off the pushed list instead of indexing position zero. No behaviour change; exactly one self filter is still pushed.
  2. Split the partitioned hash join dynamic filter into bounds AND membership — adds bounds_union.rs, which computes the set-theoretic union of the per-partition ranges, and pushes it as a second, routing-free DynamicFilter wrapper. The union is a relaxation (it admits keys that route to a partition not holding them), which is sound because the membership half behind it is exact. Multi-column keys are merged per column and emitted as a product of ranges, a superset of the true union. The bounds stay inside the CASE exactly as before whenever the union cannot describe the build side (a cancelled partition), the merge is degenerate, or the second filter did not survive pushdown — so no plan loses selectivity it previously had. CollectLeft joins have no routing to hoist and are untouched.
  3. Collapse an all-InList partitioned membership check into one InList — when every non-empty build partition pushed an InList, replace the routing CASE with a single InListExpr over the union. Capped at 1 MiB, since each partition's list is independently limited and the concatenation grows with partition count; past some size the routed CASE (where a probe row only ever probes one list) is the cheaper shape. Partitions pushing a hash map, and builds where a cancelled partition makes the union incomplete, keep the CASE.

Are these changes tested?

Yes.

  • bounds_union.rs ships unit tests for the union arithmetic, including the multi-column product, degenerate merges, and the relaxation estimate.
  • Existing hash-join and dynamic-filter coverage passes: cargo test --profile ci -p datafusion-physical-plan --lib — 1675 passed, 0 failed.
  • cargo test --profile ci -p datafusion-sqllogictest --test sqllogictests — 502/502 files pass.
  • Three .slt files have updated EXPLAIN output, showing the intended DynamicFilter [ empty ] AND DynamicFilter [ empty ] shape on the probe-side scan: preserve_file_partitioning.slt, push_down_filter_parquet.slt, statistics_registry.slt. These are plan-shape changes only; every result set is unchanged.
  • cargo fmt --all clean; cargo clippy --profile ci --all-targets --features avro,json,backtrace,extended_tests,recursive_protection,parquet_encryption --workspace -- -D warnings clean.

Are there any user-facing changes?

No API changes and no changes to query results.

The one visible difference is in EXPLAIN output: the probe-side scan of a partitioned hash join now shows two DynamicFilter conjuncts where it previously showed one. Anything asserting on that exact string will need updating.

No new configuration options. The existing hash_join_inlist_pushdown_max_size (128 KiB) and hash_join_inlist_pushdown_max_distinct_values (150), both per-partition, continue to govern whether the InList path is reachable at all.

adriangb and others added 3 commits August 10, 2026 17:04
`handle_child_pushdown_result` reached for the single self filter it had
pushed and `dynamic_expressions_produced` cloned the single field behind it.
Route both through one `HashJoinExecDynamicFilter::produced_expressions`
helper and pop the driven filter off the pushed list instead of indexing
position zero, so a second pushed filter is a matter of adding a field
rather than of rewriting the plumbing.

No behaviour change: exactly one self filter is still pushed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ship

A partitioned join used to push one `DynamicFilter[CASE hash % n WHEN i THEN
(bounds_i AND membership_i) ... END]`. Everything the join knows was sealed
inside a single opaque conjunct: `split_conjunction` splits only on a
top-level `BinaryExpr(And)` and does not descend into a
`DynamicFilterPhysicalExpr`, so the Parquet reader saw one `ArrowPredicate`
that had to compute the routing hash for every probe row before it could
reject any of them, and row-group pruning could make nothing of a `CASE`.

Push two filters instead:

    DynamicFilter[ merged bounds ] AND DynamicFilter[ routing CASE ]

Two *wrappers* is the load-bearing part, not two expressions inside one
wrapper. Split in two, `row_filter.rs` turns each into its own
`ArrowPredicate` and arrow-rs applies them in sequence against an
accumulating `RowSelection`, building a fresh array reader per predicate over
only the still-selected rows. The cheap vectorized range check therefore runs
first and the routing hash and hash-table lookup only see the survivors. The
range half is also plain enough for `PruningPredicate` to use against
row-group statistics, which a `CASE` never was.

The merged bounds are the union of the per-partition ranges (see
`bounds_union`), which is a relaxation: it admits keys that route to a
partition that does not hold them. That is sound because the membership half
behind it is exact. The bounds stay inside the `CASE` — exactly as before —
whenever the union cannot describe the build side (a canceled partition), the
merge is degenerate, or the second filter did not survive pushdown, so no
plan loses selectivity it used to have. `CollectLeft` joins have no routing to
hoist out of and are left untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
When every non-empty build partition pushes an `InList`, the routing `CASE`
is redundant. Routing is a deterministic function of the key columns, so
every build row holding key `K` lands in the same partition a probe row
holding `K` routes to; testing `K` against the union of all the lists
therefore accepts and rejects *precisely* what the `CASE` does. This is an
equality, not the relaxation the bounds merge is.

Collapsing drops the routing hash from the probe path entirely and leaves an
`InListExpr`, which unlike a `CASE` is a shape `datafusion/pruning`
understands (up to `max_in_list_size`).

The union is capped at 1 MiB: each partition's list is independently limited
by `hash_join_inlist_pushdown_max_size`, so the concatenation grows with the
partition count, and past some size the routed `CASE` — where a probe row
only ever probes one list — is the cheaper shape. Partitions that push a hash
map, and builds where a canceled partition makes the union incomplete, keep
the `CASE`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@adriangb

Copy link
Copy Markdown
Contributor Author

run benchmarks

@github-actions github-actions Bot added sqllogictest SQL Logic Tests (.slt) physical-plan Changes to the physical-plan crate labels Aug 10, 2026
@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c5246026791-1528-btw97 6.12.85+ #1 SMP Wed Jun 17 20:31:55 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 prototype/split-dynamic-filter (8768c31) to 33ad1cc (merge-base) diff

Run configuration
run benchmark clickbench_partitioned

Results will be posted here when complete


File an issue against this benchmark runner

@adriangb

Copy link
Copy Markdown
Contributor Author

show benchmark queue

@adriangbot

Copy link
Copy Markdown

Hi @adriangb, you asked to view the benchmark queue (#24235 (comment)).

Comment Repo PR User Benchmarks Status
#5246026791 apache/datafusion #24235 adriangb ["clickbench_partitioned"] running
#5246026791 apache/datafusion #24235 adriangb ["tpch"] running
#5246026975 apache/datafusion #24186 alamb ["clickbench_partitioned"] running
#5246058582 apache/datafusion #24236 adriangb ["clickbench_partitioned"] running
#5246058582 apache/datafusion #24236 adriangb ["tpcds"] running
#5246058582 apache/datafusion #24236 adriangb ["tpch"] running

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

Benchmark for this request failed.

Run configuration
run benchmark tpch

Last 20 lines of output:

Click to expand
Cloning into '/workspace/datafusion-branch'...
fatal: unable to access 'https://github.com/apache/datafusion.git/': Could not resolve host: github.com

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 prototype/split-dynamic-filter (8768c31) to 33ad1cc (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 prototype_split-dynamic-filter
--------------------
Benchmark clickbench_partitioned.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃       HEAD ┃ prototype_split-dynamic-filter ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 0  │    1.25 ms │                        1.22 ms │     no change │
│ QQuery 1  │   11.95 ms │                       12.12 ms │     no change │
│ QQuery 2  │   36.89 ms │                       35.92 ms │     no change │
│ QQuery 3  │   30.58 ms │                       30.31 ms │     no change │
│ QQuery 4  │  227.12 ms │                      223.07 ms │     no change │
│ QQuery 5  │  276.45 ms │                      274.36 ms │     no change │
│ QQuery 6  │    1.37 ms │                        1.25 ms │ +1.09x faster │
│ QQuery 7  │   13.63 ms │                       13.17 ms │     no change │
│ QQuery 8  │  329.68 ms │                      326.06 ms │     no change │
│ QQuery 9  │  460.83 ms │                      454.08 ms │     no change │
│ QQuery 10 │   70.22 ms │                       70.07 ms │     no change │
│ QQuery 11 │   81.68 ms │                       81.80 ms │     no change │
│ QQuery 12 │  269.62 ms │                      273.08 ms │     no change │
│ QQuery 13 │  366.93 ms │                      373.69 ms │     no change │
│ QQuery 14 │  290.34 ms │                      291.03 ms │     no change │
│ QQuery 15 │  281.77 ms │                      288.80 ms │     no change │
│ QQuery 16 │  619.68 ms │                      624.65 ms │     no change │
│ QQuery 17 │  630.42 ms │                      637.68 ms │     no change │
│ QQuery 18 │ 1267.34 ms │                     1275.19 ms │     no change │
│ QQuery 19 │   27.39 ms │                       26.94 ms │     no change │
│ QQuery 20 │  515.85 ms │                      517.14 ms │     no change │
│ QQuery 21 │  510.02 ms │                      511.08 ms │     no change │
│ QQuery 22 │  982.27 ms │                      984.80 ms │     no change │
│ QQuery 23 │ 3033.18 ms │                     3079.93 ms │     no change │
│ QQuery 24 │   41.08 ms │                       41.17 ms │     no change │
│ QQuery 25 │  110.00 ms │                      112.26 ms │     no change │
│ QQuery 26 │   41.86 ms │                       41.77 ms │     no change │
│ QQuery 27 │  512.88 ms │                      516.04 ms │     no change │
│ QQuery 28 │ 2903.24 ms │                     2893.13 ms │     no change │
│ QQuery 29 │   41.16 ms │                       41.72 ms │     no change │
│ QQuery 30 │  300.44 ms │                      310.69 ms │     no change │
│ QQuery 31 │  293.19 ms │                      291.55 ms │     no change │
│ QQuery 32 │  919.06 ms │                      965.31 ms │  1.05x slower │
│ QQuery 33 │ 1450.92 ms │                     1503.98 ms │     no change │
│ QQuery 34 │ 1479.37 ms │                     1492.81 ms │     no change │
│ QQuery 35 │  288.30 ms │                      300.49 ms │     no change │
│ QQuery 36 │   67.70 ms │                       70.68 ms │     no change │
│ QQuery 37 │   35.65 ms │                       36.45 ms │     no change │
│ QQuery 38 │   42.59 ms │                       42.66 ms │     no change │
│ QQuery 39 │  136.38 ms │                      146.41 ms │  1.07x slower │
│ QQuery 40 │   14.44 ms │                       14.65 ms │     no change │
│ QQuery 41 │   14.10 ms │                       14.32 ms │     no change │
│ QQuery 42 │   13.50 ms │                       13.77 ms │     no change │
└───────────┴────────────┴────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                             ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                             │ 19042.33ms │
│ Total Time (prototype_split-dynamic-filter)   │ 19257.27ms │
│ Average Time (HEAD)                           │   442.84ms │
│ Average Time (prototype_split-dynamic-filter) │   447.84ms │
│ Queries Faster                                │          1 │
│ Queries Slower                                │          2 │
│ Queries with No Change                        │         40 │
│ Queries with Failure                          │          0 │
└───────────────────────────────────────────────┴────────────┘

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

Comparing HEAD and prototype_split-dynamic-filter
--------------------
Benchmark clickbench_partitioned.json
--------------------
┏━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Query     ┃                                  HEAD ┃        prototype_split-dynamic-filter ┃        Change ┃
┡━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ QQuery 0  │          1.25 / 4.02 ±5.42 / 14.85 ms │          1.22 / 4.01 ±5.46 / 14.93 ms │     no change │
│ QQuery 1  │        11.95 / 12.37 ±0.23 / 12.62 ms │        12.12 / 12.33 ±0.20 / 12.69 ms │     no change │
│ QQuery 2  │        36.89 / 37.27 ±0.37 / 37.89 ms │        35.92 / 36.47 ±0.43 / 37.01 ms │     no change │
│ QQuery 3  │        30.58 / 31.19 ±0.73 / 32.58 ms │        30.31 / 30.68 ±0.31 / 31.25 ms │     no change │
│ QQuery 4  │     227.12 / 230.34 ±2.31 / 233.47 ms │     223.07 / 228.43 ±2.97 / 231.81 ms │     no change │
│ QQuery 5  │     276.45 / 280.71 ±3.65 / 287.42 ms │     274.36 / 277.19 ±2.35 / 280.95 ms │     no change │
│ QQuery 6  │           1.37 / 1.61 ±0.36 / 2.32 ms │           1.25 / 1.39 ±0.21 / 1.81 ms │ +1.16x faster │
│ QQuery 7  │        13.63 / 13.84 ±0.19 / 14.12 ms │        13.17 / 13.40 ±0.13 / 13.55 ms │     no change │
│ QQuery 8  │     329.68 / 333.69 ±2.92 / 338.54 ms │     326.06 / 332.23 ±3.87 / 337.64 ms │     no change │
│ QQuery 9  │     460.83 / 473.81 ±7.77 / 484.65 ms │    454.08 / 468.14 ±10.44 / 486.51 ms │     no change │
│ QQuery 10 │        70.22 / 70.73 ±0.48 / 71.63 ms │       70.07 / 76.82 ±11.41 / 99.59 ms │  1.09x slower │
│ QQuery 11 │        81.68 / 82.65 ±0.87 / 84.20 ms │        81.80 / 83.28 ±1.55 / 85.90 ms │     no change │
│ QQuery 12 │     269.62 / 273.53 ±2.65 / 276.27 ms │     273.08 / 280.96 ±8.66 / 296.20 ms │     no change │
│ QQuery 13 │    366.93 / 384.28 ±12.06 / 396.92 ms │    373.69 / 412.62 ±35.84 / 479.86 ms │  1.07x slower │
│ QQuery 14 │     290.34 / 293.49 ±3.03 / 297.48 ms │    291.03 / 325.58 ±63.29 / 451.96 ms │  1.11x slower │
│ QQuery 15 │     281.77 / 285.97 ±3.76 / 290.49 ms │    288.80 / 352.76 ±40.15 / 398.34 ms │  1.23x slower │
│ QQuery 16 │     619.68 / 625.33 ±5.68 / 634.60 ms │    624.65 / 643.02 ±15.16 / 666.46 ms │     no change │
│ QQuery 17 │     630.42 / 637.62 ±5.36 / 645.84 ms │    637.68 / 650.65 ±11.64 / 671.74 ms │     no change │
│ QQuery 18 │ 1267.34 / 1291.61 ±18.29 / 1323.03 ms │ 1275.19 / 1293.17 ±13.60 / 1316.00 ms │     no change │
│ QQuery 19 │        27.39 / 27.68 ±0.24 / 28.03 ms │       26.94 / 36.74 ±14.65 / 64.99 ms │  1.33x slower │
│ QQuery 20 │    515.85 / 526.96 ±11.83 / 547.90 ms │     517.14 / 525.68 ±7.86 / 539.37 ms │     no change │
│ QQuery 21 │    510.02 / 528.28 ±10.66 / 543.09 ms │     511.08 / 520.53 ±6.46 / 530.10 ms │     no change │
│ QQuery 22 │   982.27 / 998.30 ±14.04 / 1015.06 ms │     984.80 / 988.16 ±4.01 / 994.77 ms │     no change │
│ QQuery 23 │ 3033.18 / 3113.08 ±46.04 / 3166.34 ms │ 3079.93 / 3124.17 ±24.50 / 3148.31 ms │     no change │
│ QQuery 24 │        41.08 / 41.51 ±0.55 / 42.55 ms │        41.17 / 45.14 ±3.67 / 50.68 ms │  1.09x slower │
│ QQuery 25 │     110.00 / 111.25 ±1.29 / 113.53 ms │     112.26 / 113.73 ±1.39 / 116.34 ms │     no change │
│ QQuery 26 │        41.86 / 45.17 ±5.47 / 56.02 ms │        41.77 / 42.89 ±0.99 / 44.27 ms │ +1.05x faster │
│ QQuery 27 │     512.88 / 517.75 ±4.47 / 525.57 ms │     516.04 / 520.03 ±2.81 / 524.48 ms │     no change │
│ QQuery 28 │  2903.24 / 2909.74 ±7.71 / 2924.67 ms │ 2893.13 / 2940.76 ±29.76 / 2976.49 ms │     no change │
│ QQuery 29 │       41.16 / 54.57 ±19.22 / 91.17 ms │        41.72 / 44.20 ±4.37 / 52.92 ms │ +1.23x faster │
│ QQuery 30 │     300.44 / 310.31 ±9.12 / 324.22 ms │     310.69 / 318.03 ±8.27 / 334.21 ms │     no change │
│ QQuery 31 │    293.19 / 309.71 ±28.19 / 366.00 ms │     291.55 / 297.94 ±3.42 / 301.42 ms │     no change │
│ QQuery 32 │   919.06 / 959.74 ±33.10 / 1014.11 ms │     965.31 / 974.60 ±7.63 / 986.90 ms │     no change │
│ QQuery 33 │ 1450.92 / 1464.21 ±10.06 / 1475.97 ms │ 1503.98 / 1538.09 ±21.25 / 1563.02 ms │  1.05x slower │
│ QQuery 34 │ 1479.37 / 1522.97 ±29.22 / 1560.42 ms │ 1492.81 / 1536.63 ±26.40 / 1571.38 ms │     no change │
│ QQuery 35 │    288.30 / 298.39 ±10.18 / 317.56 ms │    300.49 / 315.93 ±12.57 / 334.15 ms │  1.06x slower │
│ QQuery 36 │        67.70 / 73.45 ±5.79 / 82.15 ms │        70.68 / 76.50 ±4.67 / 82.80 ms │     no change │
│ QQuery 37 │        35.65 / 42.22 ±7.33 / 52.02 ms │        36.45 / 42.28 ±6.29 / 51.06 ms │     no change │
│ QQuery 38 │        42.59 / 44.99 ±2.29 / 47.98 ms │        42.66 / 45.49 ±2.30 / 49.20 ms │     no change │
│ QQuery 39 │     136.38 / 146.48 ±5.60 / 153.08 ms │    146.41 / 159.92 ±10.42 / 177.05 ms │  1.09x slower │
│ QQuery 40 │        14.44 / 17.39 ±5.62 / 28.62 ms │        14.65 / 15.70 ±0.78 / 17.00 ms │ +1.11x faster │
│ QQuery 41 │        14.10 / 16.10 ±2.18 / 20.00 ms │        14.32 / 16.16 ±2.68 / 21.43 ms │     no change │
│ QQuery 42 │        13.50 / 15.65 ±3.72 / 23.07 ms │        13.77 / 15.78 ±3.20 / 22.15 ms │     no change │
└───────────┴───────────────────────────────────────┴───────────────────────────────────────┴───────────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                             ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                             │ 19459.94ms │
│ Total Time (prototype_split-dynamic-filter)   │ 19778.20ms │
│ Average Time (HEAD)                           │   452.56ms │
│ Average Time (prototype_split-dynamic-filter) │   459.96ms │
│ 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 100.0s
Peak memory 12.5 GiB
Avg memory 4.4 GiB
CPU user 996.4s
CPU sys 68.6s
Peak spill 0 B

clickbench_partitioned — branch

Metric Value
Wall time 100.0s
Peak memory 11.1 GiB
Avg memory 4.2 GiB
CPU user 1010.1s
CPU sys 73.3s
Peak spill 0 B

File an issue against this benchmark runner

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 sqllogictest SQL Logic Tests (.slt)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants