Skip to content

perf: skip probe hashing in the routed lookup when every map is an array map - #27324

Merged
jayzhan211 merged 3 commits into
apache:mainfrom
adriangb:routed-lookup-skip-hashes
Nov 12, 2026
Merged

jayzhan211 merged 3 commits into
apache:mainfrom
adriangb:routed-lookup-skip-hashes

Conversation

@adriangb

@adriangb adriangb commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

A Partitioned hash join with dense integer keys builds an ArrayMap in every partition. Its dynamic filter then computes a hash for every probe row, but an ArrayMap looks keys up by value, so nothing reads those hashes. Skipping them makes the filter cheaper on every probe batch.

What changes are included in this PR?

HashTableLookupExpr::evaluate_routed hashes the probe keys only when at least one partition is a hash table. The counting sort by partition runs either way.

What is the testing strategy for this PR?

  • A new unit test builds every partition as an ArrayMap and checks exact matches, including a null probe row.
  • The routed lookup fuzz test passes with and without force_hash_collisions.
  • Lookup cost on a GCP VM (16 partitions, batch 8192): 7.49 → 6.56 ns/row.

Are there any user-facing changes?

No.

🤖 Generated with Claude Code

@github-actions github-actions Bot added the physical-plan Changes to the physical-plan crate label Nov 11, 2026
@adriangb
adriangb marked this pull request as ready for review November 11, 2026 19:00
@adriangb
adriangb requested a review from jayzhan211 November 11, 2026 20:57
adriangb and others added 3 commits November 11, 2026 23:44
…ray map

An array map probes by key value, so when no partition holds a hash map
the routed lookup computed per-row hashes that nothing read. The lookup
now hashes the probe keys only when some partition is a hash map. The
counting sort by partition runs either way, and the sorted hashes are
gathered from the sorted rows only when hashes exist.

A new test builds every partition as an array map and checks exact
matches, including a null probe row.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review asked for two changes. A `matches!` on `Map::HashMap` would
silently treat a new `Map` variant as not needing hashes, so an
exhaustive match makes the compiler flag that case. An empty `Vec`
stood for "no hashes", which a reader could mistake for zero rows, so
`Option<Vec<u64>>` states it directly. The hash map arm returns an
internal error if it ever runs without hashes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
The routed lookup carried `Option` hashes and a hash map arm that had
to return an internal error when the hashes were missing. That state
could not happen, but the types allowed it. Review asked to remove it.

The lookup now collects the maps into `Option<Vec<Option<&ArrayMap>>>`,
which is `Some` only when no partition holds a hash map. That path has
no hashes and no hash map arm. The other path always computes hashes.
Both paths share the counting sort (`sort_rows_by_partition`) and the
per-partition probe loop (`probe_runs`). Behavior does not change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@adriangb
adriangb force-pushed the routed-lookup-skip-hashes branch from 99abca1 to 8e8462b Compare November 12, 2026 06:46
@jayzhan211

Copy link
Copy Markdown
Contributor

Thanks @adriangb !

@jayzhan211
jayzhan211 added this pull request to the merge queue Nov 12, 2026
Merged via the queue into apache:main with commit 4a832a0 Nov 12, 2026
44 checks passed
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.

2 participants