Skip to content

perf(spectrum): hybrid point/window boundary seed + keyed-accessor fast path - #64

Merged
nh13 merged 1 commit into
mainfrom
perf/boundary-search
Jul 6, 2026
Merged

perf(spectrum): hybrid point/window boundary seed + keyed-accessor fast path#64
nh13 merged 1 commit into
mainfrom
perf/boundary-search

Conversation

@nh13

@nh13 nh13 commented Jul 5, 2026

Copy link
Copy Markdown

Second of a three-PR stack. Stacked on #63 (perf/interval-recovery) — review that first; this PR's diff is against it.

What

Three byte-identical changes to the forward maximal-match boundary search:

  1. Hybrid boundary seed. forward_maximal_len_seeded now seeds the seed-independent find_boundary by model-window width: a narrow window (width <= 64, the confident/common case) seeds at the model's POINT prediction ([center, center+1)), so the gallop's probes cluster near the prediction (cache-local once the SA spills to DRAM); a wide window seeds at the whole [pred-err, pred+err+1), which already brackets the boundary. Byte-identical either way — width only picks the seed.
  2. Option-free keyed probe for mode-2/3 sidecars: has_stored_keys() + sa_entry_keyed() read (position, key) from one cache line with no release-mode bounds assert, no mode branch, and no Option wrap, feeding a new compare_query_vs_suffix_2x_keyed_with_mask_k. The generic mode-1 path is unchanged.
  3. Branchless bisection in find_boundary via core::hint::select_unpredictable — the direction test is a ~50/50 coin flip near the boundary, so a data-dependent branch mispredicts ~half the probes; the conditional-select leaves the cold SA read as the only stall.

Because (3) needs core::hint::select_unpredictable (stable since Rust 1.88), this PR raises the workspace MSRV to 1.96 (latest stable) and adopts u64::is_multiple_of in bloom_file.rs for a clean -D warnings build on that toolchain (a lint stabilized in the newer clippy; no behavior change).

Wall (x86, measured)

mem_search_bench on c7i.4xlarge (AVX-512), PRMI_BENCH_REFLEN=100M (≈2.6 GB SA, DRAM-bound), same-box main vs this branch:

mem_search_forward/model_launch base this PR ratio
unique (≈96% of SMEMs, common) 46.70 µs 32.98 µs 0.706 (−29%)
repeat (high-occ tandem, rare) 180.51 µs 196.05 µs 1.086 (+8.6%)

The benefit is a cache-locality effect: at 500 kbp (SA fits in L3) the same bench is neutral; the −29% appears only once the SA is DRAM-resident. The hybrid softens but does not eliminate the high-occ regression — a tandem-repeat 32-mer has a narrow model window (the RMI predicts its sort position tightly) yet a wide SA interval, and occ is unknown before the search, so no a-priori window-width test routes it to the window seed. Net wall-positive under any realistic occurrence mix; the regression is confined to pathological high-occ regions.

Correctness

Byte-identical. forward_boundary_seed_independent pins the seeded search (point seeds and a wide window) against a full-SA galloping oracle; keyed_equals_scalar_mode2 now also covers the _k comparator against the scalar reference across a full SA sweep including near-sentinel entries; the existing mem_search/warm-start oracle proptests continue to gate the integrated result.

Summary by CodeRabbit

  • New Features

    • Added faster access for keyed index lookups and suffix comparisons, improving hot-path performance.
    • Improved boundary search behavior for more efficient forward maximal-length queries.
  • Bug Fixes

    • Expanded test coverage for keyed comparison and boundary-seed consistency across different search paths.
  • Chores

    • Increased the minimum required Rust version for the workspace to 1.96.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8ce64b2a-f74b-452a-bef0-3bb5c1dca193

📥 Commits

Reviewing files that changed from the base of the PR and between 1157a71 and 5491c6d.

📒 Files selected for processing (5)
  • Cargo.toml
  • prmi/src/index/mod.rs
  • prmi/src/index/spectrum.rs
  • prmi/src/sidecar/bloom_file.rs
  • prmi/src/sidecar/sa_file.rs

Walkthrough

Adds keyed-mode fast-path entry accessors to SaFileReader and LearnedIndex, an option-free keyed suffix comparator, a branchless find_boundary binary-search update, and a refactored seeded boundary-search that unifies point/window seeding via forward_boundary_windowed_fallback. Also bumps Rust MSRV and switches a bloom-file modulo check to is_multiple_of.

Changes

Keyed SA access and boundary search refactor

Layer / File(s) Summary
SaFileReader keyed entry accessors
prmi/src/sidecar/sa_file.rs
Adds has_keys() and entry_keyed(i), an unsafe combined (position, key) read for mode 2/3 files, guarded by debug_assert!.
LearnedIndex keyed accessor wrappers
prmi/src/index/mod.rs
Adds has_stored_keys() and sa_entry_keyed(i) delegating to SaFileReader.
Option-free keyed suffix comparator
prmi/src/index/spectrum.rs
Adds compare_query_vs_suffix_2x_keyed_with_mask_k taking stored_key: u64 instead of Option<u64>; test sweep validates it against the scalar oracle.
Branchless find_boundary and seeded search refactor
prmi/src/index/spectrum.rs
Replaces if/else lo/hi update with select_unpredictable; refactors forward_maximal_len_seeded to choose point vs window seed and always call forward_boundary_windowed_fallback; tests renamed to forward_boundary_seed_independent with deterministic seed sets.
MSRV bump / bloom-file modulo cleanup
Cargo.toml, prmi/src/sidecar/bloom_file.rs
Raises workspace rust-version to 1.96; switches num_bits % 64 != 0 check and test assertion to is_multiple_of(64).

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • fg-labs/prmi#21: Both modify find_boundary/SA-entry prefetch hot paths in spectrum.rs.
  • fg-labs/prmi#58: Both touch forward_maximal_len_seeded trusted/untrusted window boundary handling.
  • fg-labs/prmi#37: Builds on the same SA position+key probe-loop pattern introduced for SaFileReader::entry/LearnedIndex::sa_entry.

Risk: entry_keyed/sa_entry_keyed skip bounds checks in release builds (debug_assert! only) — any caller path that reaches keyed reads without has_keys()/index-range guarantees will read out-of-bounds silently in release. Fix: verify all call sites gate on has_stored_keys() and valid i before invoking, or add release-mode checks.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is specific and covers the main changes: hybrid boundary seeding and keyed-accessor fast path.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/boundary-search

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nh13
nh13 force-pushed the perf/interval-recovery branch from ad9d557 to 0213444 Compare July 6, 2026 05:34
Base automatically changed from perf/interval-recovery to main July 6, 2026 06:54
…st path

Seed forward_maximal_len_seeded's boundary gallop by model-window width:

  * NARROW window (confident, width <= 64 -- the common case): seed
    find_boundary at the model's POINT prediction ([center, center+1)). It
    gallops outward with probes clustered near the prediction (bwa-meme's
    mem_search_tradeoff access pattern), which is cache-local once the SA
    spills to DRAM.
  * WIDE window (uncertain): seed at the whole [pred-err, pred+err+1), which
    already brackets the boundary.

Both seed the SAME seed-independent find_boundary, so the choice is
byte-identical -- width only picks the seed.

Measured on x86 (c7i.4xlarge, 100M SA, DRAM-bound, same-box base vs this
commit): cold forward mem_search on UNIQUE reads (~96% of SMEMs) is 46.7 ->
33.0 us = -29%. The high-occ tandem-repeat stress case is 180.5 -> 196.0 us =
+8.6% (an earlier pure point-seed revision was +22% there; the hybrid softens
but does not eliminate it -- a repeat 32-mer has a narrow model window yet a
wide SA interval, and occ is unknown before the search, so no a-priori
window-width test routes it to the window seed). Net wall-positive under any
realistic occurrence mix; the regression is confined to pathological high-occ
regions.

Add an Option-free keyed probe for mode-2/3 sidecars: has_stored_keys() +
sa_entry_keyed() read (position, key) from one cache line with no release-mode
bounds assert, no mode branch, and no Option wrap, feeding
compare_query_vs_suffix_2x_keyed_with_mask_k. The generic mode-1 path is
unchanged.

Make the final bisection in find_boundary branchless via
core::hint::select_unpredictable: the direction test is a ~50/50 coin flip
near the boundary, so a data-dependent branch mispredicts ~half the probes;
the conditional-select leaves the cold SA read as the only stall. This needs
Rust >= 1.88, so raise the workspace MSRV to 1.96 (latest stable) and, for a
clean -D warnings build on that toolchain, adopt u64::is_multiple_of in
bloom_file.rs (a lint stabilized in the newer clippy; no behavior change).

Byte-identity gates: forward_boundary_seed_independent pins the seeded search
(point seeds AND a wide window) against a full-SA galloping oracle;
keyed_equals_scalar_mode2 now also covers the _k comparator against the scalar
reference across a full SA sweep (including near-sentinel entries).
@nh13
nh13 force-pushed the perf/boundary-search branch from 3d8fb77 to 5491c6d Compare July 6, 2026 07:04
@nh13

nh13 commented Jul 6, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13

nh13 commented Jul 6, 2026

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Action performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nh13
nh13 merged commit 9a93116 into main Jul 6, 2026
4 checks passed
@nh13
nh13 deleted the perf/boundary-search branch July 6, 2026 18:21
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.

1 participant