Skip to content

Probe identity-keyed expression caches without cloning the key - #10615

Open
robert3005 wants to merge 3 commits into
developfrom
rk/exact-key-probes
Open

robert3005 wants to merge 3 commits into
developfrom
rk/exact-key-probes

Conversation

@robert3005

Copy link
Copy Markdown
Contributor

Every split of a scan prunes with each filter conjunct, and each layout
reader on the way looks the conjunct up in a cache keyed by tree
identity. Each lookup cloned the expression into a key, hashed the
options of its function, such as literal lists, and dropped the key
again. An ExactKey trait lets these maps be probed with a borrowed expression,
the hash uses only the identity of the children, and the struct reader
lends its cached partitioning instead of cloning it.

@robert3005 robert3005 added the changelog/performance A performance improvement label Oct 19, 2026
@codspeed

codspeed Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 3.14%

4 warnings, 1 note

⚠️ Walltime results may be unreliable

Some benchmarks used the Walltime instrument outside CodSpeed Macro Runners, which can make the results inconsistent.

Learn more about Macro Runners

See affected benchmarks

⚠️ Unable to measure the execution of 2 benchmarks

These benchmarks measured no execution time, so their results are not comparable and count as unchanged. This usually happens when the compiler removes the code under test.

Prevent compiler optimizations

See affected benchmarks

⚠️ 22 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

ℹ️ 40 benchmarks have no flamegraph

The flamegraph wasn't generated: the symbols of the profiling trace could not be resolved, because no information about the language frames was collected.

See affected benchmarks

⚡ 2 improved benchmarks
❌ 1 regressed benchmark
✅ 2436 untouched benchmarks
⏩ 12 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ CPU Simulation chunked_dict_primitive_canonical_into[f64, (1000, 1000, 10)] 146.7 µs 201.5 µs -27.18%
⚡ CPU Simulation test_scan_filter[int] 3.4 ms 3 ms +13.43%
⚡ Walltime compare_u64_avx2 3.7 µs 3.3 µs +10.03%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing rk/exact-key-probes (1251b6b) with develop (a59294e)

Open in CodSpeed

Footnotes

  1. 12 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@robert3005
robert3005 marked this pull request as draft October 20, 2026 12:09
robert3005 and others added 3 commits October 26, 2026 23:39
Every split of a scan prunes with each filter conjunct, and each layout
reader on the way looks the conjunct up in a cache keyed by tree
identity. Each lookup cloned the expression into a key, hashed the
options of its function, such as literal lists, and dropped the key
again. The clones touch reference counts that every scan thread shares,
and on TPC-DS q39 on DuckDB they were more than half of the pruning
time.

An ExactKey trait lets these maps be probed with a borrowed expression,
the hash uses only the identity of the children, and the struct reader
lends its cached partitioning instead of cloning it. Pruning in q39
takes a third of the samples it took before.

Signed-off-by: Robert Kruszewski <robert@spiraldb.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Robert Kruszewski <github@robertk.io>
DuckDB scans of local files reuse the layout readers of a file across
queries. The readers cache pruning results, partitioned expressions and
evaluated dictionary values by the identity of the expression, and every
query binds new expressions, so each query added entries that no later
lookup could hit. The file statistics reader held every filter it pruned,
which kept the expressions alive for all the caches below it. On
ClickBench q20 the process grew by about 30 MiB per query, and the pages
of each new allocation were faulted in and zeroed.

Key these caches with WeakBoundExprKey, which identifies an expression as
ExactBoundExpr does but holds its children weakly, and drop the entries of
dead expressions when inserting a new one. The weak reference keeps the
allocation of the children, so a later expression cannot take their
address and match a stale key. ExactKey now compares identities so that
both kinds of key can be probed with a borrowed expression.

Memory of 13 runs of q20 stays at 190 MiB instead of growing to 540 MiB,
and q20 and q21 take 16% and 21% less time.

Signed-off-by: Robert Kruszewski <robert@spiraldb.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qFEJyt9gzF3mi2F91ZwML
@robert3005
robert3005 marked this pull request as ready for review October 26, 2026 23:42

This branch has not been deployed

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

Labels

changelog/performance A performance improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant