Skip to content

Refactor finemap2anndata: major improvements in performance and memory usage#120

Open
ariannalandini wants to merge 1 commit into
mainfrom
finemap2anndata_fast
Open

Refactor finemap2anndata: major improvements in performance and memory usage#120
ariannalandini wants to merge 1 commit into
mainfrom
finemap2anndata_fast

Conversation

@ariannalandini

@ariannalandini ariannalandini commented Apr 28, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces a refactored implementation of finemap2anndata function -finemap2anndata_fast()- that significantly improves runtime performance and memory efficiency, while preserving the original functionality.

The new implementation is designed to scale to large finemapping datasets (e.g. thousands of RDS files and millions of SNPs) without exhausting memory or incurring large slowdowns.


Key Improvements

  1. Eliminated full preloading of RDS files

Before: loaded all RDS files into memory upfront → very high RAM usage.
Now: files are read sequentially inside the loop, memory footprint is reduced from total dataset → single file

  1. Efficient sparse matrix construction (major speedup)

Before: repeated assignment into sparse matrices is very slow and memory-inefficient.
lABF_matrix_sparse[row_index, col_indices] <- lABF_values

Now: uses triplet (i, j, x) construction
Matrix::sparseMatrix(i = i_vec, j = j_vec, x = values)

@edg1983 edg1983 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, looks good. I've added some suggestions. Take a look to see if thereis anything useful to incorporate.

Comment thread bin/s07_rds2anndata.R
suppressPackageStartupMessages(library(data.table))
suppressPackageStartupMessages(library(anndata))
suppressPackageStartupMessages(library(Matrix))
suppressPackageStartupMessages(library(dplyr))

@edg1983 edg1983 May 4, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think dplyr is not needed anymore. We can remove this import and save time/memory

Comment thread bin/s07_rds2anndata.R

success_count <- success_count + 1
# Keep only SNPs belonging to the credible set
fm_cs <- fm[is_cs == TRUE]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes a full in-memory copy of the object. We can reduce memory footprint by getting the row index where is_cs == TRUE (using which) and then passing the indexes when we perform other operations.

Comment thread bin/s07_rds2anndata.R
df = 1,
lower.tail = FALSE
)
top_pvalue <- min(p_values, na.rm = TRUE)

@edg1983 edg1983 May 4, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, the min p-value should equal the largest chi-square, so we can simply take

top_pvalue <- stats::pchisq(
        max((fm_cs$bC / fm_cs$bC_se)^2),
        df = 1,
        lower.tail = FALSE
      ))

This avoids allocating memory for p_values vector and should be slightly faster

Comment thread bin/s07_rds2anndata.R
position
)]
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may add an explicit rm(rds_obj) here to be sure the rds_obj memory is freed before allocating a new object. It would be good to test the trade-off between memory advantage and speed since cleaning takes some time (short, but still may impact if this cycle is repeated many times)

Comment thread bin/s07_rds2anndata.R
all_snps <- snp_chr_pos$snp
labf_vec <- unlist(labf_list, use.names = FALSE)
beta_vec <- unlist(beta_list, use.names = FALSE)
se_vec <- unlist(se_list, use.names = FALSE)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently, we collect as lists and then flatten them without any processing, right? In this case, can we just collect as a vector directly by appending the data so we avoid double memory allocation and unlist operation?

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.

2 participants