perf(spectrum): reuse the masked query key in reseed carry loops - #45
Conversation
The min_intv-truncation carry loops in `forward_truncate_below_maximal` and `span_rc_walk`'s `expand` re-tokenized the query 32-mer key at every length step (`tokenize_32mer(&q[..l])` per depth). The keyed comparator already masks the key to the active prefix length, so the full-32 key — computed once — is byte-identical at every depth: bases beyond `l` are masked off, and for `l > 32` the >32 tail reads the `p` slice directly. Hoist the key out of both loops. Instruction-attributed profiling (`perf record -e instructions:u`) on hg38 seeding showed `tokenize_32mer` at ~28% of retired instructions, almost entirely from these per-step calls. Eliminating them cut seeding instructions ~28% and moved seeding-only wall from 0.60x to 0.75x of the FM-index path (hg38, t16, load-excluded) in a fuller optimization branch. Byte-identical: `forward_truncate_below_maximal_equals_oracle`, `mem_search_backward_truncated_span_rc_equals_oracle`, and the keyed comparator proptests pass unchanged.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughIn ChangesKey hoisting in RC-walk and forward-truncate loops
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
@coderabbitai review |
✅ Action performedReview finished.
|
The
min_intv-truncation carry loops inforward_truncate_below_maximalandspan_rc_walk'sexpandclosure re-tokenized the query 32-mer key at every length step (tokenize_32mer(&q[..l])per depth). But the keyed comparator already masks the key to the active prefix length, so the full-32 key — computed once — is byte-identical at every depth: the bases beyondlare masked off, and forl > 32the >32bp tail reads thepslice directly. This hoists the key out of both per-depth loops.Why
Instruction-attributed profiling (
perf record -e instructions:u) of hg38 seeding showedtokenize_32merat ~28% of retired instructions, almost entirely from these per-length-step calls in the two reseed carry loops. (A cycles profile badly understated it at ~11% — the function is pure compute, so cycle-sampling hid it behind memory stalls elsewhere; instruction sampling is the right lens here.)Impact
Measured on hg38 (
t1, load-subtracted, byte-id verified) in a fuller optimization branch: seeding instructions dropped ~28%, and seeding-only wall moved from 0.60× → 0.75× of the FM-index path (t16, load-excluded). This change is the dominant contributor to that delta.Correctness (byte-identity)
The keyed comparator (
compare_query_vs_suffix_2x_keyed_with_mask) masks the supplied key tomin(p.len(), 32), so passing the full-32 key with the per-step slicepis provably equivalent to passingtokenize_32mer(&q[..l]). Verified unchanged:forward_truncate_below_maximal_equals_oraclemem_search_backward_truncated_span_rc_equals_oraclekeyed_equals_scalar_mode2and the other keyed comparator proptestsNo interface, format, or behavior change — pure compute reduction.
Summary by CodeRabbit