Skip to content

perf: inline hot varint/cell decode calls on the SeekRowid path (#657) - #659

Merged
iheitlager merged 1 commit into
mainfrom
fix/657-read-join-inline-hot-path
Aug 30, 2026
Merged

perf: inline hot varint/cell decode calls on the SeekRowid path (#657)#659
iheitlager merged 1 commit into
mainfrom
fix/657-read-join-inline-hot-path

Conversation

@iheitlager

Copy link
Copy Markdown
Member

Summary

read_join benched at 1.9x slower than the oracle with no static smoking gun. Deeper profiling (this PR) pinpointed the actual hot loop:

  • sample-profiling the read_join bench (16700-row bench_data JOIN bench_lookup on PK) showed 56% of samples landing in TableCursor::seek's binary search.
  • Cross-checking against read_full_scan (never calls seek()) showed that path already beats the oracle (0.71x) — so the gap was isolated to seek(), not general record decode.
  • Compiled-opcode comparison against oracle's own EXPLAIN confirmed byte-for-byte identical opcode shape/count per row — no extra/missing VDBE work, no access-path bug.
  • bench_lookup's b-tree is depth-2 (root interior, 5 leaf children, ~227-240 cells/leaf), so each SeekRowid does ~8-9 binary-search probes, each calling decode_varint (x2), decode_cell_head, and read_cell_pointer — all compiled as real, non-inlined function calls (confirmed via disassembly of the hot address range).

Marking those three functions #[inline] (src/record/varint.rs, src/btree.rs) lets LTO fold them into the loop.

Result

cargo bench --bench crud -- read_join:

  • Before: 3.63ms (ours) vs 1.89ms (oracle) — 1.9x
  • After: 2.95ms (ours) vs 1.97ms (oracle) — 1.5x (-18.6%, p < 0.05)

Confirms the issue's own "diffuse per-row overhead" hypothesis was half right — the overhead was concentrated in one specific uninlined loop, not spread evenly.

Test plan

  • cargo test --release — full suite passes (all btree/record unit tests green)
  • cargo clippy --release --all-targets — clean
  • cargo bench --bench crud -- read_join — 18.6% improvement, statistically significant

Closes #657

spend: ~1.5x estimate — first profiling pass found no fix (diffuse overhead), a second pass comparing against read_full_scan and oracle's EXPLAIN output pinned the actual hot loop.

…rch path (#657)

Profiling with `sample` on the read_join bench (16700-row bench_data
joined to bench_lookup on its PK) showed 56% of samples landing in
TableCursor::seek's binary search — decode_varint, decode_cell_head,
and read_cell_pointer were compiled as real (non-inlined) function
calls, paid twice per probed cell across ~8-9 binary-search probes per
row against bench_lookup's ~230-cell leaf pages. read_full_scan, which
never calls seek(), was already faster than the oracle (0.71x),
confirming the gap was isolated to this path rather than general
record decode.

Marking these three small, always-hot functions #[inline] lets LTO
fold them into the binary-search loop. read_join drops from 3.63ms to
2.95ms (measured via `cargo bench --bench crud -- read_join`), closing
the oracle gap from 1.9x to 1.5x.

spend: ~1.5x estimate (initial profiling pointed to diffuse overhead
with no fix; a second pass comparing read_join against read_full_scan
and the compiled opcode stream against oracle's EXPLAIN output pinned
the actual hot loop).
@iheitlager
iheitlager force-pushed the fix/657-read-join-inline-hot-path branch from f57e957 to caa556f Compare August 30, 2026 08:44
@iheitlager
iheitlager merged commit 95afa3b into main Aug 30, 2026
6 checks passed
@iheitlager
iheitlager deleted the fix/657-read-join-inline-hot-path branch August 30, 2026 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: profile read_join execution path (1.9x slower than oracle, no static root cause found)

1 participant