GAlignments read speedup was shown as 4.03x against a measured 2.50x, and
coverage to an in-memory RleList as 3.29x against 2.49x. Read-pattern
speedups are 1.94-2.50x, ATAC fragment-size QC 4.39x end-to-end, and coverage
to bigWig 1.52x.bam_coverage().bam_coverage() and bam_coverage_bigwig() no longer allocate memory
proportional to the genome declared in the BAM header. The coverage core
previously built a dense per-base difference array for every header sequence
before reading a single alignment, so a BAM carrying few alignments still cost
several GB whenever its header declared a whole genome -- 12.6 GB for a 1.4 MB
file with a GRCh38 header -- and the collapse pass then walked every base of
every contig. Coverage is now accumulated as per-thread interval endpoints and
swept per contig, so memory and time scale with the alignments actually
present rather than with the reference. Contigs without alignments cost
nothing and still emit the correct full-length zero run.
Measured on a 2.5 GB ATAC-seq BAM (195 contigs): peak RSS 14.2 GB -> 2.5 GB
and 71.0 s -> 14.9 s at 24 threads. On a 1.4 MB BAM with a whole-genome
header: 12.6 GB -> 0.55 GB (R's baseline) and 17.6 s -> 7.8 s. Output is
unchanged and remains identical() to coverage(readGAlignments()) at every
thread count.
Removing the shared difference array also removes the atomic updates from the read loop, which is where most of the speedup comes from.
BamScale.ga_fastpath is now BamScaleR.ga_fastpath.The entries below document this package's development under its former name, BamScale, before the rename described above. Those version numbers belong to the former name's series: BamScale was accepted into Bioconductor devel on 4 May 2026 and never shipped in a numbered release, so version numbering restarts at 0.99.0 under the new name. The code described below is carried forward into BamScaleR unchanged.
fragment_sizes(): paired-end fragment-size (insert-size) distribution;
identical to table(abs(scanBam(isize))). drop_mate_unmapped = TRUE
(default) replicates scanBam's isize = NA rule for mate-unmapped reads.mapq_dist(): mapping-quality distribution; identical to
table(scanBam(mapq)) (MAPQ 255 reported as 255, where scanBam uses NA).bam_coverage(): per-base coverage as a SimpleRleList; identical() to
GenomicAlignments::coverage(readGAlignments(...)), including the CIGAR
conventions (M/=/X/D cover, N gaps, drop.D.ranges = FALSE) and the
readGAlignments record filter (only unmapped reads dropped).bam_coverage_bigwig(): single-pass BAM -> coverage bigWig written natively
via the bundled libBigWig -- no R-side coverage object, no
rtracklayer::export.bw round-trip. Data and zoom blocks are compressed
across the OpenMP threads (parallel = TRUE, default); the parallel output
is bit-for-bit identical to the serial writer. A compress_level argument
exposes the zlib deflate level, and verbose = TRUE prints a per-phase
timing breakdown.bam_read(as = "GAlignments") now uses a dedicated fast path that assembles
the GAlignments slots in C++: seqnames/strand accumulated as run-length
pairs inside the parallel region, the cigar column materialised through a
CHARSXP cache that exploits CIGAR-string redundancy, and qname decoded only
when actually requested. Output is identical() to
GenomicAlignments::readGAlignments() (verified at 226M-read scale) and to
the previous construction path. options(BamScale.ga_fastpath = FALSE)
restores the old path.src/libBigWig/ (built with
-DNOCURL, local files only). Local modifications, each commented in
bwWrite.c: a tunable zlib deflate level, deferred parallel block
compression, and zoom-level construction from in-memory coverage runs instead
of re-reading the just-written data section.reserve() calls copied all accumulated data on every batch.
Buffers now grow geometrically. This affects every read path -- e.g. on a
229M-read BAM at 48 threads, as = "GAlignments" dropped from ~300 s to
~56 s, and plain data-frame reads improved 2.8-4.4x.std::to_string.bam_coverage() 7.2x vs coverage(readGAlignments());
bam_coverage_bigwig() 2.6x vs megadepth at equal compression;
fragment_sizes() 5.4x vs scanBam + table(); as = "GAlignments" 4.8x
vs readGAlignments() (and faster even single-threaded).bam_coverage_bigwig() now works on Windows: the output stream is opened in
binary mode ("w+b"; text mode corrupted the bigWig), and zoom levels are
always built from the in-memory coverage runs instead of re-reading the
just-written stream (Windows' C runtime is strict about write-to-read
transitions on update streams; the in-memory path is byte-identical to the
re-read path, verified at 226M-read scale). Vendored libBigWig diagnostics now
go through REprintf(), clearing the compiled-code check NOTE.^src/Makevars$ entry from .Rbuildignore (dating to
the pkgdown setup): built tarballs -- including the Bioconductor builds --
shipped without src/Makevars and therefore compiled WITHOUT OpenMP (and
without the explicit -lz), silently running single-threaded when installed
from a tarball. In-place R CMD INSTALL . was unaffected, which is why local
installs always threaded. Tarball builds now carry the full build
configuration (OpenMP, zlib, -DNOCURL, and the bundled libBigWig objects).run_workflow_benchmark.R gains first-class arms for the four new functions
(--include-fastcov/-bigwig/-fragsize/-mapq), each behind an identical()
correctness gate, with an optional megadepth context arm (--megadepth-bin).run_server_benchmark.R: BiocParallel PSOCK clusters are now started and
their namespaces pre-loaded outside the timed region in all multi-file arms
(previously cluster spin-up was charged to every iteration, diluting
comparator ratios at high worker counts); added --include-single so single
and multi scenarios can run in separate stages.select_encode_atac.R
(ENCODE portal selection -> accession/md5 manifest), manifest-driven
download_atac_data.R with md5 verification, samtools_reference.sh,
merge_runs.R, and the staged run_manuscript_final.sh orchestrator.fragment_sizes() / mapq_dist() /
bam_coverage() against their Rsamtools/GenomicAlignments equivalents;
bam_coverage_bigwig() value-identity after re-import plus bit-for-bit
parallel-vs-serial output; six GAlignments fast-path identity gates
(threads 1/4, mcols order, use.names, flag/mapq filters, fast == slow,
gctorture).seq and qual are now built directly in C as
DNAStringSet / PhredQuality objects from the packed BAM byte buffer (via the
IRanges/XVector C-callable constructors), avoiding a character round-trip. Output
remains byte-identical to Rsamtools::scanBam.inst/benchmarks/
(coverage → bigWig and ATAC fragment-size QC) and a self-contained
benchmark-results vignette.GAlignments output now carries the BAM-header sequence lengths in its Seqinfo.
Previously the seqlengths were NA, so coverage() extended each seqlevel only to
its maximum end and export.bw() wrote truncated chromosome sizes, diverging from
GenomicAlignments::readGAlignments.S4Vectors, IRanges, XVector, Biostrings)
and GenomicAlignments in .onLoad, so the compatible seq/qual and GAlignments
paths resolve their constructors and classes on fresh SnowParam SOCK workers.