Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Performance Improvements

This page covers the performance work carried in bwa-mem3 on top of upstream bwa-mem2. Almost every change listed here is a throughput, memory, or supporting (test/hardening/cleanup) change that preserves the aligner’s output; the one exception is the deterministic tie-break ordering in #123, which can reorder equal-scoring alignments relative to upstream (see Equivalence with bwa-mem2 for the full, audited list of where bwa-mem3 output diverges).

For a reader-friendly grouping of what drives the speedup — by machine architecture, hot-path rewrite, indexing, allocation/I/O, and build-time — see Performance → Overview. For current benchmark numbers across architectures and workloads, see bwa-mem3-bench, the canonical source of truth for benchmark methodology and results.

Lockstep SMEM batching (PR #33)

Seeding in bwa-mem2 advances one read’s SMEM walk at a time. Because each forward/backward extension step issues a random access into the cp_occ checkpoint array (~4 GB for human genome), the CPU stalls on cache misses between steps. Lockstep batching advances SMEM_LOCKSTEP_N reads’ SMEM walks in slot-interleaved round-robin order so that the out-of-order engine can overlap the cp_occ cache-miss loads for read i+N with the compute-bound walk of read i.

Each read slot (BatchSlot) carries its own prev[] walk buffer and match_buf[] reorder buffer. A tight recycling loop assigns finished slots to the next unprocessed read immediately. The match-emit cursor enforces input-index order so output is byte-identical to scalar. SMEM_LOCKSTEP_N is compile-time tunable; N=1 dispatches to the unchanged scalar path for bisection.

Measured improvement on 150 bp NovaSeq WGS (1M pairs, hg38, Graviton3 r7g.4xlarge, 8 threads): −6.1% wall time (82 s → 77 s). The backwardExt hot cp_occ load share dropped from 65.5% to 53.3% of function time — direct evidence that the OoO engine is overlapping cross-slot loads. On 300 bp MiSeq reads the workload is SW-dominated (~85% of cycles in kswv kernels) and the SMEM improvement is within noise; parity holds.

Supersedes PR #15 (cross-read _mm_prefetch shape), which regressed on Graviton3.

Batched -H header ingestion (PR #49, closes issue #37)

Passing a large header file via -H <file> re-ran strlen on the growing header string and called realloc on every input line, making ingestion O(n²) in the number of header lines. For a ~70 MB / ~1.5 M-line header (reported in upstream bwa-mem2#204) this caused runtimes exceeding 10 minutes before alignment started.

The fix introduces bwa_insert_header_file, a batched helper that determines the file size with fseek/ftell, allocates a single buffer, copies all @-prefixed lines in one pass, and calls bwa_insert_header once. The fix also addresses four correctness gaps in the upstream PR #204: the return-value assignment was dropped (leaving hdr_line stale after realloc), const FILE* caused compiler warnings, empty files were not guarded, and each fgets was not bounded by remaining buffer. A regression test (test/header_insert_test.cpp) diffs the batched path against the pre-patch per-line baseline across eight edge cases.

libsais FM-index construction (PR #57)

bwa-mem3 index now builds the FM-index using libsais v2.9.1 (Ilya Grebnov) instead of the sais-lite (Yuta Mori saisxx) library that bwa-mem2 inherited. libsais is actively maintained, supports OpenMP-parallel induced sorting, and produces a byte-identical FM-index. No changes are required to existing indexes — bwa-mem3 reads index files built by bwa-mem2 index without re-indexing.

For a human reference (GRCh38 + decoys), libsais reduces indexing wall time and peak memory vs sais-lite. Exact numbers depend on thread count and available RAM; see the PR body for measurements on Graviton3.

Consolidated mapping speedups (PR #58)

PR #58 is a multi-phase performance audit of bwa-mem2’s hot path, squashed and rebased onto main. It incorporates improvements across five subsystems:

  • ksw2 banded SW — tuned the band extension loop to reduce redundant computation in the common case.
  • SMEM lockstep batching — additional refinements on top of PR #33.
  • SAL prefetch — prefetch hints for the suffix array lookup hot path.
  • SAM record building — reduced per-record allocation in the text formatting path.
  • PGO build — the opt-in profile-guided optimization target (see also Performance → PGO build) is included in this suite.

On the smoke-1M workload (1M PE 150 bp reads, hg38, Graviton3 r7g.4xlarge, 16 threads, warm page cache), this PR contributed the largest single-step wall time reduction in the main branch’s performance history. Benchmark details are maintained at bwa-mem3-bench.


Changes catalog

The mechanism column says, in one line, why it is faster. The stage column groups changes by where in the pipeline they act: seed (SMEM/FM-index walks), sw (Smith–Waterman / banded kernels), index (bwa-mem3 index), i/o (read decompression + header ingestion), mem (allocation), dispatch (SIMD/runtime selection), sort, or prof (off-by-default profiling). A /test suffix (e.g. sw/test) marks a test or contract-locking change for that stage; a slash between two stages (e.g. dispatch/sw) means the change spans both.

Merged

ItemStageMechanismbwa-mem3 PRUpstream
mimalloc by defaultmemVendored + statically linked allocator; avoids glibc ptmalloc lock contention at high thread counts#19
Lockstep SMEM batchingseedInterleaves several reads’ seed walks so the OoO engine overlaps cp_occ cache misses across reads#33
Batched -H header ingestioni/oOne-pass buffered read; fixes O(n²) strlen+realloc per line (>10 min → <1 s on large headers)#49bwa-mem2#204 (open); closes #37
libsais FM-index constructionindexLinear-time suffix-array/BWT build; less wall time + memory; byte-identical index#57
Consolidated mapping speedupsseed/swMulti-phase hot-path audit across ksw2 band loop, SMEM batching, SAL prefetch, SAM building#58
kswv per-strip L1 prefetchesswAdds the per-strip L1 prefetches that kswv512_16 already had to the four kswv kernels that lacked them; stops first-touch L1 stalls#70
SMEM_LOCKSTEP_N 8 → 16seedWider lockstep batch exposes more memory-level parallelism on modern cores#75
Closed-form ungapped HITswReplaces the per-base Kadane walk with a direct store when total_mis == 0#77
On-stack ksort buffersortRemoves a per-call malloc for small arrays (typical n = 5–30 alignment regions)#78
Skip zero-init in libsais buildindexDrops value-initialization of unpack + SA buffers later fully overwritten (tens of GiB of zero-fill avoided)#80
Single-binary SIMD dispatchdispatchReplaces the multi-binary execv launcher with in-process per-host kernel selection#83
x86 baseline → avx2dispatch/swRestores 256-bit auto-vec on non-kernel TUs (~+15% wgs / +11% wes vs the sse41 baseline)#84
Cap avx512bw autovec at 256-bitdispatch/swAvoids the 512-bit auto-vec frequency downclock on some x86 parts (the PR also adds an unrelated bwa_shm /dev/shm preflight)#86
Inline backwardExtseedForces inlining at the hot SMEM call sites; kills a struct-by-value ABI pass gcc 12+ couldn’t elide#88
SIMD host-floor precheckdispatchFails fast with a clear message when a host lacks the build’s required ISA (safe multi-arch deployment)#95
Stable tie-breaks + pdqsortsortDeterministic alnreg ordering and pdqsort at the dedup-patch sort sites (see equivalence note above)#123
FASTQ reader fast pathi/oContent-detecting reader over libdeflate BGZF for faster input decompression/parse#128
Recover 8-bit banded SW (≥128 bp)swKeeps long reads in the cheaper 8-bit lane width where valid#140
Gotoh gaps from HswDerives extension gaps from H (standard Gotoh) rather than M in the recovered 8-bit path#141
Drop dead qlen[] paramswRemoves an unread parameter from the three 8-bit kernels (cleanup; compile-output-identical)#143
Long-read kernel parity testsw/testPromotes the 8-bit/16-bit byte-identity harness into a CI doctest#144
Short-circuit re-baseline scanswSkips the inert per-row re-baseline scan in the banded kernel#147
Remove dead SW code pathsswDeletes unreachable SORT_PAIRS / non-CORE / SSE2-polyfill code (no behavior change)#148
Vectorize epilogue side-channelswVectorizes the per-row epilogue side-channel loop#149
Bound getScores prefetch readsswBounds getScores8/16 prefetch reads to the padding contract (hardening)#150
Unsigned 8-bit h0-prefix seedswWidens the 8-bit h0-prefix seed to unsigned [0,255]#151
--profile stage timingprofOff-by-default read/proc/write + disk/decompress/parse breakdown for diagnosing pipeline scaling (no overhead when off)#152
zlib-ng inflate + 3rd workeri/oVendored zlib-ng inflate path with chunk cap and an added pipeline worker (~2.2× faster read stage; up to −7.8% wall at 96 cores)#153
Right-size SA staging buffersindexSizes the pos_ar/map_ar staging buffers to the actual min(s, max_occ) write count instead of the uncapped SA-interval sum#157
gtle contract testsw/testEnforces gtle byte-identity when gscore > 0; documents the gscore == 0 query-end tail divergence#158
NEON SW tuningswReplaces sse2neon all-zero movemask tests with single-instruction vmaxvq horizontal reductions in the hot SW scans#160
AVX2 SW tuningswRelieves the port-5 vpblendvb/vpshufb bottleneck in the two SW kernels (byte-identical, Zen3-verified)#161
AVX2 16-bit kswv256_16swAdds the AVX2 16-bit mate-rescue kernel so AVX2 hosts no longer fall back to scalar ksw_align2 for 16-bit rescue#162
NEON movemask parity testsw/testScalar-vs-NEON unit test for the movemask helpers; guards the #160 rewrite against silent mask collapse#164

Open / in progress

Not yet merged to main:

ItemStageMechanismbwa-mem3 PR
AVX2 8-bit wrapper prefetchswAdds the missing next-batch ref/query software-prefetch to smithWatermanBatchWrapper8 — the lone SW wrapper that lacked it (follow-up to #161)#163

Several correctness and crash fixes underpin the long-read SW, indexing, and high-throughput work rather than adding speed themselves: SMEM read positions widened int16_tint32_t to stop a long-read SIGSEGV (#142, merged); a persistent kt_for worker pool that fixes a multi-chunk SIGSEGV under mimalloc v3 (#154, merged); and mem_lim widened to int64 to stop an SA-staging buffer overflow on highly repetitive seeds (#156, merged).


See also: Performance → Overview · Performance → PGO build · Correctness fixes · Build & infrastructure · bwa-mem3-bench