Skip to content

Fix OOM in scm_gene_stat on large datasets (#98) - #99

Open
aviezerl wants to merge 1 commit into
masterfrom
fix-gstat-oom-98
Open

Fix OOM in scm_gene_stat on large datasets (#98)#99
aviezerl wants to merge 1 commit into
masterfrom
fix-gstat-oom-98

Conversation

@aviezerl

Copy link
Copy Markdown
Collaborator

Fixes #98.

Chunking

quant was ceiling(nrow(mat_fg)/mc_cores), so the gene chunk size grew as mc_cores shrank. Each chunk densified three matrices (mat_fg, mat_ds, mat_n) at quant x n_cells x 8B each, multiplied by every fork.

Now chunks on a fixed ~50MB dense block, so peak memory per fork is independent of mc_cores. Fixed-size chunks can leave a 1-gene tail, so the three row subsets use drop=FALSE.

I did not cap the workers at 3 as the issue suggests - mc_cores is the user's setting, and with 50MB blocks the cap isn't needed.

sz_cor

apply(mat_fg[,subs], 1, cor) coerces the whole sparse submatrix to dense. Replaced with .sparse_row_cor, a sums-of-products Pearson using one sparse matvec, no densification. Chose this over chunking the apply because it also removes a per-gene sort over all cells.

Verification

Golden-master: gene_stat is identical() to the previous implementation on

case shape exercises
small 800 x 20,000 chunk count changes 4 -> 3
wide 300 x 50,001 the >50000 sz_cor branch
tail 313 x 20,000 1-gene tail chunk

.sparse_row_cor also matches apply(mat, 1, cor) to 3 decimals including the zero-variance -> NA case. Not covered: the OOM itself at 80k cells, which needs the real dataset.

Not done

A fully sparse stat_on_genes (via sparseMatrixStats) would remove the densification and the parallelism entirely, and retire the recurring parallel-chunking bug family (#71, #37, #96). That's a refactor with a new Bioconductor dependency, out of scope for a bug fix here.

https://claude.ai/code/session_01MFLoUB1VZFB5qRNYR6BxVL

quant was ceiling(nrow(mat_fg)/mc_cores), so the gene chunk size grew as
mc_cores shrank. Each chunk densified three matrices (mat_fg, mat_ds,
mat_n), giving quant x n_cells x 8B each; on 80k cells that is multi-GiB
per chunk, multiplied by every parallel fork. Lowering mc_cores made it
worse rather than better.

Chunk on a fixed ~50MB dense block instead, so peak memory per fork no
longer depends on mc_cores. Fixed-size chunks can leave a 1-gene tail, so
the three row subsets now use drop=FALSE.

sz_cor had a second OOM: apply(mat_fg[,subs], 1, cor) coerces the whole
sparse submatrix to dense (genes x 50k cells x 8B). Replaced with
.sparse_row_cor, a sums-of-products Pearson that needs one sparse matvec
and never densifies.

Also hard-stop if the sequential retry is still short instead of
returning a truncated gene_stat, matching scm_downsamp.

Verified by golden-master: gene_stat is identical() to the previous
implementation on 800x20000 (chunk count changes 4 -> 3), 300x50001 (the
>50k sz_cor branch) and 313x20000 (1-gene tail chunk).

Claude-Session: https://claude.ai/code/session_01MFLoUB1VZFB5qRNYR6BxVL
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.

OOM Crash in scm_gene_stat() on Large Datasets (>50k cells)

1 participant