This vignette shows how to speed up RunCanek() with the ncores parameter, which
parallelizes the mutual nearest neighbor (MNN) search.
We use the same ifnb dataset as Seurat's own integration
tutorial —
stimulated vs. control PBMCs — so if you've worked through that one, this data
will look familiar. For a general walkthrough of RunCanek() itself, see the
main Seurat vignette; this one focuses specifically on ncores.
ifnb is loaded from the SeuratData
package rather than CRAN, so it needs a one-time install the first time you use
it:
if (!requireNamespace("SeuratData", quietly = TRUE)) { install.packages("remotes") remotes::install_github("satijalab/seurat-data") } SeuratData::InstallData("ifnb")
library(Canek) library(Seurat) #> Loading required package: SeuratObject #> Loading required package: sp #> #> Attaching package: 'SeuratObject' #> The following objects are masked from 'package:base': #> #> intersect, t library(SeuratData) #> ── Installed datasets ──────────────────────────────── SeuratData v0.2.2.9002 ── #> ✔ ifnb 3.1.0 ✔ panc8 3.0.2 #> ────────────────────────────────────── Key ───────────────────────────────────── #> ✔ Dataset loaded successfully #> ❯ Dataset built with a newer version of Seurat than installed #> ❓ Unknown version of Seurat installed
Standard log-normalization workflow, same preprocessing as the main Seurat vignette.
ifnb <- LoadData("ifnb") #> Validating object structure #> Updating object slots #> Ensuring keys are in the proper structure #> Warning: Assay RNA changing from Assay to Assay #> Ensuring keys are in the proper structure #> Ensuring feature names don't have underscores or pipes #> Updating slots in RNA #> Validating object structure for Assay 'RNA' #> Object representation is consistent with the most current Seurat version #> Warning: Assay RNA changing from Assay to Assay5 ifnb[["RNA"]] <- split(ifnb[["RNA"]], f = ifnb$stim) ifnb <- NormalizeData(ifnb, verbose = FALSE) ifnb <- FindVariableFeatures(ifnb, verbose = FALSE) ifnb <- ScaleData(ifnb, verbose = FALSE) ifnb <- RunPCA(ifnb, verbose = FALSE)
ncores parameterFinding MNN pairs requires two independent k-nearest-neighbor searches — one
from reference to query, one from query to reference. Neither depends on the
other's result, so they can run at the same time instead of one after the
other. Setting ncores > 1 does exactly that, via parallel::mclapply().
ncores defaults to 1 (fully sequential) — parallelism is opt-in only,
never automatic. Canek never tries to detect how many
cores are "available" and use them on its own to avoid any issues when using a shared cluster or
HPC job. Silently using them could oversubscribe your
allocation and could affect other jobs sharing the same node. Set ncores
explicitly to however many cores you know are free to use.
Two things ncores > 1 checks for you, rather than silently doing something
unexpected:
parallel::mclapply() relies on fork(), which Windows
doesn't support. Requesting ncores > 1 there raises an error rather than
quietly running sequentially anyway and leaving you wondering why it isn't
any faster.ncores exceeds
parallel::detectCores(), RunCanek() errors instead of silently reducing
it to whatever's available.set.seed(42) system.time( ifnb <- RunCanek(ifnb, "stim") ) #> user system elapsed #> 25.965 0.120 21.738
set.seed(42) system.time( ifnb <- RunCanek(ifnb, "stim", ncores = 3, integration.name = "CanekParallel") ) #> Warning: Key 'Canek_' taken, using 'canekparallel_' instead #> user system elapsed #> 24.511 0.925 14.221
How much this actually helps depends on how much of the total runtime is spent in the parallelized MNN search versus everything else (clustering, fuzzy correction, etc.).
Parallelizing the two searches doesn't change what they compute. unname() clears each reduction's
column names before comparing: Seurat renames a reduction's key prefix when
two reductions would otherwise share one (here, "Canek_" becomes
"canekparallel_" for the second run), which would otherwise make
all.equal() return FALSE even though every number in the embeddings matches.
isTRUE(all.equal( unname(Embeddings(ifnb, "canek")), unname(Embeddings(ifnb, "canekparallel")) )) #> [1] TRUE
sessionInfo() #> R version 4.4.3 (2025-02-28) #> Platform: x86_64-conda-linux-gnu #> Running under: Ubuntu 22.04.2 LTS #> #> Matrix products: default #> BLAS/LAPACK: /home/mloza/miniconda3/envs/canek-check/lib/libopenblasp-r0.3.33.so; LAPACK version 3.12.0 #> #> locale: #> [1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8 #> [4] LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8 #> [7] LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C #> [10] LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C #> #> time zone: Asia/Tokyo #> tzcode source: system (glibc) #> #> attached base packages: #> [1] stats graphics grDevices utils datasets methods base #> #> other attached packages: #> [1] panc8.SeuratData_3.0.2 ifnb.SeuratData_3.1.0 SeuratData_0.2.2.9002 #> [4] Seurat_5.5.1 SeuratObject_5.4.0 sp_2.2-1 #> [7] Canek_0.3.1 #> #> loaded via a namespace (and not attached): #> [1] deldir_2.0-4 pbapply_1.7-4 gridExtra_2.3.1 #> [4] rlang_1.3.0 magrittr_2.0.5 RcppAnnoy_0.0.23 #> [7] otel_0.2.0 spatstat.geom_3.8-1 matrixStats_1.5.0 #> [10] ggridges_0.5.7 compiler_4.4.3 flexmix_2.3-20 #> [13] png_0.1-9 vctrs_0.7.3 reshape2_1.4.5 #> [16] stringr_1.6.0 crayon_1.5.3 pkgconfig_2.0.3 #> [19] fastmap_1.2.0 promises_1.5.0 numbers_0.9-2 #> [22] purrr_1.2.2 xfun_0.60 modeltools_0.2-24 #> [25] bluster_1.16.0 jsonlite_2.0.0 goftest_1.2-3 #> [28] later_1.4.8 spatstat.utils_3.2-4 fpc_2.2-14 #> [31] BiocParallel_1.40.0 irlba_2.3.7 parallel_4.4.3 #> [34] prabclus_2.3-5 cluster_2.1.8.2 R6_2.6.1 #> [37] ica_1.0-3 spatstat.data_3.1-9 stringi_1.8.7 #> [40] RColorBrewer_1.1-3 reticulate_1.46.0 spatstat.univar_3.2-0 #> [43] parallelly_1.48.0 lmtest_0.9-40 diptest_0.77-2 #> [46] scattermore_1.2 Rcpp_1.1.2 knitr_1.51 #> [49] tensor_1.5.1 future.apply_1.20.2 zoo_1.8-15 #> [52] sctransform_0.4.3 FNN_1.1.4.1 httpuv_1.6.17 #> [55] Matrix_1.7-5 splines_4.4.3 nnet_7.3-20 #> [58] igraph_2.3.3 tidyselect_1.2.1 abind_1.4-8 #> [61] spatstat.random_3.4-5 spatstat.explore_3.8-0 codetools_0.2-20 #> [64] miniUI_0.1.2 listenv_1.0.0 plyr_1.8.9 #> [67] lattice_0.22-9 tibble_3.3.1 shiny_1.14.0 #> [70] S7_0.2.2 ROCR_1.0-12 evaluate_1.0.5 #> [73] Rtsne_0.17 future_1.70.0 fastDummies_1.7.6 #> [76] survival_3.8-9 polyclip_1.10-7 fitdistrplus_1.2-6 #> [79] mclust_6.1.3 kernlab_0.9-33 pillar_1.11.1 #> [82] KernSmooth_2.23-26 stats4_4.4.3 plotly_4.12.0 #> [85] generics_0.1.4 RcppHNSW_0.7.0 S4Vectors_0.44.0 #> [88] ggplot2_4.0.3 scales_1.4.0 globals_0.19.1 #> [91] xtable_1.8-8 class_7.3-23 glue_1.8.1 #> [94] lazyeval_0.2.3 tools_4.4.3 BiocNeighbors_2.0.0 #> [97] robustbase_0.99-7 data.table_1.18.4 RSpectra_0.16-2 #> [100] RANN_2.6.2 dotCall64_1.2 cowplot_1.2.0 #> [103] grid_4.4.3 tidyr_1.3.2 nlme_3.1-169 #> [106] patchwork_1.3.2 cli_3.6.6 rappdirs_0.3.4 #> [109] spatstat.sparse_3.2-0 spam_2.11-4 viridisLite_0.4.3 #> [112] dplyr_1.2.1 uwot_0.2.4 gtable_0.3.6 #> [115] DEoptimR_1.2-0 digest_0.6.39 progressr_1.0.0 #> [118] BiocGenerics_0.52.0 ggrepel_0.9.8 htmlwidgets_1.6.4 #> [121] farver_2.1.2 htmltools_0.5.9 lifecycle_1.0.5 #> [124] httr_1.4.8 mime_0.13 MASS_7.3-66
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.