Skip to content

perf: use mimalloc as the global allocator in the binary - #137

Open
BenjaminDEMAILLE wants to merge 1 commit into
seqeralabs:mainfrom
BenjaminDEMAILLE:perf/mimalloc-allocator
Open

perf: use mimalloc as the global allocator in the binary#137
BenjaminDEMAILLE wants to merge 1 commit into
seqeralabs:mainfrom
BenjaminDEMAILLE:perf/mimalloc-allocator

Conversation

@BenjaminDEMAILLE

@BenjaminDEMAILLE BenjaminDEMAILLE commented Aug 13, 2026

Copy link
Copy Markdown

What

Uses mimalloc as the #[global_allocator] for the rustqc binary, behind a default-on mimalloc feature.

Why

The single-pass pipeline allocates constantly (per-read buffers, the mate-pair HashMap, per-chromosome vectors that grow during counting) and does so from several rayon workers at once. That pattern is dominated by allocator lock contention under the system allocator.

Measurement

Synthetic dataset (a real one would be better, happy to re-run against the RustQC-benchmarks pipeline):

  • BAM: 4,000,000 reads, coordinate-sorted, 24 contigs (~40-58 Mb each), 15% duplicate-flagged, 25% spliced
  • GTF: 1,212,002 lines / 60,000 genes / 150,000 transcripts
  • aarch64 macOS, --threads 4, hyperfine -w 1 -r 5
wall clock user CPU
main 22.122 s ± 0.197 s 22.567 s
this PR 20.540 s ± 0.223 s 19.105 s

1.08 ± 0.02x faster, and user CPU time drops 15%. The gap should widen with higher --threads since the win is contention-related.

Confirmed on a second dataset (same size but with random read sequences, so the dedup maps are fully populated), 5 interleaved runs, medians:

real user
main 24.43 s 25.76 s
this PR 23.10 s 21.57 s

Same shape: 1.06x wall clock, user CPU down 16%.

Parity

All data outputs are byte-identical before/after: featureCounts.tsv, tin.xls, lc_extrap.txt, samtools stats/flagstat/idxstats, Qualimap, and all RSeQC outputs. cargo test --release passes (200 + 12 + 18 + 2).

Notes / trade-offs

  • Declared in src/main.rs, not src/lib.rs. #[global_allocator] is process-wide, so a library must not impose one on its dependents. Library users who want it can declare it themselves.
  • --no-default-features restores the system allocator, which is what you want when profiling with an external heap profiler.
  • mimalloc builds C via cc, so no new system requirement beyond the toolchain rust-htslib already needs. It cross-compiles to all 8 release targets (linux gnu / darwin, x86_64 / aarch64).
  • Binary size grows by roughly 100 KB.

Unrelated observation

While diffing outputs I noticed dupradar/*_duprateExpDens.{svg,png} is not reproducible run-to-run on main alone: the same points are emitted in a different order between two runs of the same binary on the same input. Data outputs are all stable; it looks like plot points are iterated straight out of a HashMap. Happy to open a separate issue.


🤖 Generated with Claude Code

Part of #143.

The RNA-Seq pipeline is allocation-heavy (per-read buffers, mate-pair
maps, per-chromosome vectors that grow during counting) and performs
those allocations from several rayon worker threads concurrently, so it
is sensitive to allocator lock contention. mimalloc uses per-thread free
lists and avoids most of that contention.

Measured on a synthetic 4M-read coordinate-sorted BAM (24 contigs) with a
1.2M-line GTF (60k genes), aarch64 macOS, --threads 4, 5 runs:

  before: 22.122 s ± 0.197 s   (user 22.567 s)
  after:  20.540 s ± 0.223 s   (user 19.105 s)

That is 1.08x wall clock and a 15% drop in user CPU time.

All data outputs (featureCounts TSV, TIN, preseq, samtools stats,
Qualimap, RSeQC) are byte-identical before and after.

The allocator is declared in src/main.rs rather than src/lib.rs on
purpose: `#[global_allocator]` is process-wide, so the library must not
impose one on its dependents. It sits behind a default-on `mimalloc`
feature so it can be disabled with --no-default-features, e.g. when
profiling with an external heap profiler.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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