Repository navigation
perf(ext): walk the ungapped mismatch bitmap one set bit at a time - #697
Conversation
|
@coderabbitai pause |
|
Note Reviews paused
Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. Walkthrough
ChangesUngapped mismatch walk
Estimated code review effort: 2 (Simple) | ~10 minutes Change: Refactor Merge Risk:
|
Action performed
|
|
@coderabbitai pause |
Action performed
|
f367e51 to
ebe8177
Compare
57f487c to
9c3d8d8
Compare
ebe8177 to
6acfdf5
Compare
9c3d8d8 to
976c519
Compare
ungapped_walk_mis found each next mismatch by rescanning the bitmap from the current position: it masked the current word below j and looked at it and then later words for the lowest set bit. Visit the mismatches in increasing order instead by walking each word's set bits (lowest bit, then bits &= bits - 1), loading the next word only when this one has none left, and score the final run of matches to N after the loop. The record, floor and stop rules are unchanged, so the walk returns the same score, record position and final value; the per-base walk equivalence test and BWA_MEM3_DEBUG_UNGAPPED_XCHECK check it. Byte-identical. A new unit test compares the walk with the per-base walk at the boundaries the random masks reach only by chance: N one short of, at, and one past representative multiples of 64 up to the full bitmap (FP_N_MAX), no mismatches, every base a mismatch, mismatches at bits 0 and 63 of each word and at N - 1 (an empty final run), and a score that reaches 0 with mismatches left in the same word, or on a word's last mismatch with mismatches left only in a later word. Co-authored-by: Jacob Schreiber <jacob.schreiber@umassmed.edu>
6acfdf5 to
028e7b4
Compare
976c519 to
9014651
Compare
|
@coderabbitai review |
Action performed
|
Summary
ungapped_walk_misscores the ungapped diagonal from the mismatch bitmap one mismatch at a time: the HIT path uses it for the committed score andqle, and the TIGHT path for the walk score its band is derived from. It found each next mismatch by rescanning the bitmap from the current position, masking the current word belowjand looking at that word and then later ones for the lowest set bit. This PR visits the mismatches in increasing order by walking each word's set bits instead (lowest bit withctz, thenbits &= bits - 1), loading the next word only when the current one has none left, and scores the final run of matches up toNafter the loop. Output is byte-identical. The function has no ISA-specific code, so the change applies to every build: x86 at every tier and arm64 NEON alike.N, capped at the bitmap's size (FP_MIS_NWORDS).ungapped_analyzesets no bit at or pastN, so every set bit visited is a real mismatch and the set bits come out in increasing position order. Each mismatch applies the same steps as before: add the run of matches before it, test for a strict new record (the earliest tied position keeps the record), subtract the mismatch penalty, floor at 0, and stop once the score reaches 0.Nis scored once, with the record positionN. When the last mismatch is atN - 1that run is empty and skipped. When the score reaches 0 the walk jumps past it, as the old loop'sbreakdid, so the outputs are frozen there as in the per-base walk.a > 0,b >= 0,h0 > 0, else the per-base walk), the record, floor and stop rules, and the three outputs (score, record position, final value) are as before.BWA_MEM3_DEBUG_UNGAPPED_XCHECKstill cross-checks every call against the per-base walk.Correctness
b = 0). A new test pins the boundaries the random masks reach only by chance:None short of, at, and one past representative multiples of 64 up to the full bitmap (FP_N_MAX= 512), with no mismatches, every base a mismatch, mismatches at bits 0 and 63 of each word, a mismatch atN - 1(an empty final run), and a score that reaches 0 with mismatches left in the same word, or on a word's last mismatch with mismatches left only in a later word. It runs five scoring schemes (includingb = 0andh0 = 1) over each layout and asserts that the empty-final-run case and both stop-with-mismatches-left cases were actually reached.N - 1is skipped, fails the new test, and recordingN - 1instead ofNfor the final run fails both the new test and the existing ones.src/is a single comment inungapped_ext.h, with its line count and itsxassertline unchanged. Every object that includesungapped_ext.h(bwamem.oand the seven unit-test objects) disassembles identically before and after it on arm64 and on an x86-64 build at the AVX2 tier.test/run_unit_tests.shpass natively on arm64 (bwtSeed lockstep parity 95/95, SMEM lockstep parity 56/56), as doband_cert_identity.shand the shell lint. A build with-DBWA_MEM3_DEBUG_UNGAPPED_XCHECKpasses the unit suite (618/618) and its end-to-end runs below trip no cross-check. On an x86-64 build at the AVX2 tier, the unit suite (597 passed, one AVX-512-only case skipped) and the bwtSeed and SMEM lockstep parity tests (95/95, 56/56) pass.main(excluding@PG) on arm64, on the arm64 cross-check build, and on an x86-64 build at the AVX2 tier: 100k pairs of the WGS slice below against hg38, SE and PE with default options, PE with-d 70,-w 30,--compat=bwa-mem2,-k 10,-k 30,-y 1,-L 0and--ks-dedup=off, and long reads (300 bp, 1 kbp, 3 kbp).mainon every timing run on all three hosts.Timing
Whole aligner,
mem -t 16on a 5M-pair WGS slice (HG00096, 2x150, hg38), clang 19.1.7, index in shared memory, 6 interleaved reps per build in a seeded shuffled order on the same host. This change was measured on its own againstmain. Figures are the paired per-rep delta vsmain(median, bootstrap 95% CI).Start-up included: the first 200k pairs of the same slice,
-t 16, with the index loaded from the page cache on every run, 12 interleaved reps:The walk runs on every architecture, so Graviton4 is a measurement here, not a null control. At 5M pairs only Sapphire Rapids excludes zero on both wall and CPU, at about −0.15% and −0.10%. On Zen 3 both intervals cross zero. On Graviton4 the wall interval crosses zero and the CPU interval just excludes it (upper end −0.01%). Every 5M median is negative. At 200k the Zen 3 and Graviton4 CPU intervals exclude zero; the Sapphire Rapids and Graviton4 wall intervals touch zero, the Zen 3 wall median is exactly 0 with its interval touching it, and the Sapphire Rapids CPU interval crosses it. Read the 200k figures with care. A 200k run takes a few seconds, so the timer's 10 ms resolution is 0.3–0.6% of a run and the wall medians sit on that grid. On Sapphire Rapids that batch also ran faster than
mainfor most changes measured alongside this one, so its 200k figure is not this change's own effect; the 5M-pair runs are the figure to go by. No interval excludes zero on the slow side.Stack
This is the thirteenth PR of a stacked series that reworks SMEM seeding, SA lookup and extension batching. It is based on #696 and should be reviewed after it. The next PR in the series is "perf(ext): probe non-HIT extensions up to 512 bases in the certified rung".
Split out of #656, thanks to @jmschrei for the original work.
Summary by CodeRabbit