Skip to content

bam: --outBAMsortingBinsN spills the coordinate sort to disk bins - #151

Open
BenjaminDEMAILLE wants to merge 2 commits into
scverse:mainfrom
BenjaminDEMAILLE:bd/bam-sort-bins
Open

bam: --outBAMsortingBinsN spills the coordinate sort to disk bins#151
BenjaminDEMAILLE wants to merge 2 commits into
scverse:mainfrom
BenjaminDEMAILLE:bd/bam-sort-bins

Conversation

@BenjaminDEMAILLE

@BenjaminDEMAILLE BenjaminDEMAILLE commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

--outBAMsortingBinsN spills the coordinate sort to disk bins.

What changed

When --limitBAMsortRAM is set and the buffer exceeds it, records are partitioned by reference sequence into --outBAMsortingBinsN bins written to temporary BAMs, then read back one bin at a time, sorted and appended.

The bins are coordinate-disjoint and already ordered relative to one another, so no k-way merge is needed: concatenating the sorted bins is the sorted output. Only one bin is resident during the sort, so peak usage is the largest bin rather than the whole run. Unmapped records sort last and get their own bin.

Spilling requires both a non-zero --outBAMsortingBinsN and a --limitBAMsortRAM the estimate exceeds; without a bound there is nothing to respect and temporary files would be pure cost. --outBAMsortingBinsN 0 disables spilling, and exceeding the limit is then still fatal — there is no way to honour the bound, so the run stops rather than quietly using more memory than allowed.

Bin files are written uncompressed: they are read back immediately, so compressing them would cost time and save nothing.

Why

--limitBAMsortRAM was a threshold the sort died on, not a bound it respected. Every record stayed resident until finish(), and exceeding the limit aborted the run.

Verification

Binned and in-memory paths produce byte-identical decoded BAM on 1161 records:

in-memory  b52a23436e3e939e98160dffed89c1448c5da4da
binned     b52a23436e3e939e98160dffed89c1448c5da4da

561 lib + 26 integration green, clippy -D warnings clean, cargo fmt --check clean, MSRV 1.89.

Related negative result

The other half of this theme was dropped. Swapping to noodles_bgzf::MultithreadedWriter measured ~37% slower at every size tested, up to 44 MB of BAM output. The premise was wrong: noodles-bgzf is declared here with its libdeflate feature and both writers go through the same deflate::encode, so the single-threaded path was not the naive-deflate bottleneck the STAR-rs comparison describes. Numbers on #143. This PR contains none of that work.

🤖 Generated with Claude Code

BenjaminDEMAILLE and others added 2 commits July 29, 2026 00:23
`--limitBAMsortRAM` was a threshold the sort died on, not a bound it respected:
every record stayed resident until `finish()`, and exceeding the limit aborted
the run with a message telling the user to raise it or give up on sorting.

Now, when a bound is set and the buffer exceeds it, records are partitioned by
reference sequence into `--outBAMsortingBinsN` bins written to temporary BAMs,
then read back one bin at a time, sorted and appended. Because the bins are
coordinate-disjoint and already in order relative to one another, no k-way
merge is needed. Only one bin is resident during the sort, so peak usage is the
largest bin rather than the whole run. Unmapped records sort last and get their
own bin.

Spilling is off unless it buys something: it needs both a non-zero
`--outBAMsortingBinsN` and a `--limitBAMsortRAM` that the estimate actually
exceeds. Without a bound there is nothing to respect and temporary files would
be pure cost. `--outBAMsortingBinsN 0` disables it outright.

The bin files are written uncompressed: they are read back immediately, so
compressing them would cost time and save nothing.

Verified on 1161 records that the binned and in-memory paths produce
byte-identical decoded BAM:

    in-memory  b52a23436e3e939e98160dffed89c1448c5da4da
    binned     b52a23436e3e939e98160dffed89c1448c5da4da

An earlier version of this partitioned in memory, which bounded the sort's
working set but not residency — every bucket was live at once. The comment
claiming reduced peak usage would have been false, so it spills for real.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The test asserted the old contract: exceeding the bound was fatal. With binning
it is not, because the sort can now respect the bound instead of dying on it.

Both halves are covered: with bins available the sort succeeds, and with
--outBAMsortingBinsN 0 there is no way to honour the bound, so the run still
stops rather than quietly using more memory than it was allowed.

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