Last updated: 2022-11-17

Checks: 7 0

Knit directory: muse/

This reproducible R Markdown analysis was created with workflowr (version 1.7.0). The Checks tab describes the reproducibility checks that were applied when the results were created. The Past versions tab lists the development history.


Great! Since the R Markdown file has been committed to the Git repository, you know the exact version of the code that produced these results.

Great job! The global environment was empty. Objects defined in the global environment can affect the analysis in your R Markdown file in unknown ways. For reproduciblity it’s best to always run the code in an empty environment.

The command set.seed(20200712) was run prior to running the code in the R Markdown file. Setting a seed ensures that any results that rely on randomness, e.g. subsampling or permutations, are reproducible.

Great job! Recording the operating system, R version, and package versions is critical for reproducibility.

Nice! There were no cached chunks for this analysis, so you can be confident that you successfully produced the results during this run.

Great job! Using relative paths to the files within your workflowr project makes it easier to run your code on other machines.

Great! You are using Git for version control. Tracking code development and connecting the code version to the results is critical for reproducibility.

The results in this page were generated with repository version b2043f3. See the Past versions tab to see a history of the changes made to the R Markdown and HTML files.

Note that you need to be careful to ensure that all relevant files for the analysis have been committed to Git prior to generating the results (you can use wflow_publish or wflow_git_commit). workflowr only checks the R Markdown file, but you know if there are other scripts or data files that it depends on. Below is the status of the Git repository when the results were generated:


Ignored files:
    Ignored:    .Rhistory
    Ignored:    .Rproj.user/
    Ignored:    r_packages_4.1.2/
    Ignored:    r_packages_4.2.0/

Untracked files:
    Untracked:  analysis/cell_ranger.Rmd
    Untracked:  data/ncrna_NONCODE[v3.0].fasta.tar.gz
    Untracked:  data/ncrna_noncode_v3.fa

Note that any generated files, e.g. HTML, png, CSS, etc., are not included in this status report because it is ok for generated content to have uncommitted changes.


These are the previous versions of the repository in which changes were made to the R Markdown (analysis/parallel.Rmd) and HTML (docs/parallel.html) files. If you’ve configured a remote Git repository (see ?wflow_git_remote), click on the hyperlinks in the table below to view the files as they were in that past version.

File Version Author Date Message
Rmd b2043f3 Dave Tang 2022-11-17 Parallel computation in R

As stated in the foreach vignette:

Much of parallel computing comes to doing three things: splitting the problem into pieces, executing the pieces in parallel, and combining the results back together.

There are several packages that make it easy to run tasks in parallel:

  • The parallel package that comes with R.
  • The doParallel package is a parallel backend for the foreach package and acts as an interface between foreach and the parallel package.
  • The BiocParallel package tailored for use with Bioconductor.
  • The furrr package parallelises mapping functions from the purrr package.

system.time

From ?proc.time:

The “user time” is the CPU time charged for the execution of user instructions of the calling process.

The “system time” is the CPU time charged for execution by the system on behalf of the calling process.

Elapsed time is the amount of time that has elapsed/passed. The user and system time while sleeping is close to zero because the CPU is idly waiting and not executing anything.

system.time(
  Sys.sleep(5)
)
   user  system elapsed 
  0.000   0.000   5.005 

More information is provided on Stack Overflow:

“User CPU time” gives the CPU time spent by the current process (i.e., the current R session and outside the kernel)

“System CPU time” gives the CPU time spent by the kernel (the operating system) on behalf of the current process. The operating system is used for things like opening files, doing input or output, starting other processes, and looking at the system clock: operations that involve resources that many processes must share.

Data

Create a list of 100 data frames each with 5,000 observations across 100 variables.

create_df <- function(n, m, seed = 1984){
  set.seed(seed)
  as.data.frame(
    matrix(
      data = rnorm(n = n * m),
      nrow = n,
      ncol = m
    )
  )
}

my_list <- lapply(1:100, function(x) create_df(5000, 100, x))
length(my_list)
[1] 100

parallel

Load the parallel package.

library(parallel)

Create a summary of each variable in each data frame without parallelisation.

system.time(
  my_sum <- lapply(my_list, summary)
)
   user  system elapsed 
  4.192   0.020   4.223 

The mclapply function can be used to process a list in parallel. Note that this function uses forking, which is not available on Windows.

system.time(
  my_sum_mc <- mclapply(my_list, summary, mc.cores = params$threads)
)
   user  system elapsed 
  0.548   0.224   0.737 

Compare the two summaries.

identical(my_sum, my_sum_mc)
[1] TRUE

Another way to run the jobs in parallel is via sockets. For Windows users, you will need to use this method for parallelisation. In addition, you need to use the parLapply function instead of mclapply.

cl <- makeCluster(params$threads)
system.time(
  my_sum_sock <- parLapply(cl, my_list, summary)
)
   user  system elapsed 
  0.601   0.173   1.764 
stopCluster(cl)

identical(my_sum_mc, my_sum_sock)
[1] TRUE

doParallel

Load the doParallel package.

library(doParallel)
Loading required package: foreach
Loading required package: iterators

Using foreach.

cl <- makeCluster(params$threads)
registerDoParallel(cl)

system.time(
  my_sum_dopar <- foreach(l = my_list) %dopar% {
    summary(l)
  }
)
   user  system elapsed 
  0.860   0.381   2.475 
stopCluster(cl)

identical(my_sum_mc, my_sum_dopar)
[1] TRUE

BiocParallel

Load BiocParallel.

library(BiocParallel)

Using bplapply.

param <- SnowParam(workers = params$threads, type = "SOCK")
system.time(
  my_sum_bp <- bplapply(my_list, summary, BPPARAM = param)
)
   user  system elapsed 
  0.767   0.370   7.686 
identical(my_sum_mc, my_sum_bp)
[1] TRUE

furrr

Load required libraries.

library(furrr)
Loading required package: future
library(purrr)

Attaching package: 'purrr'
The following objects are masked from 'package:foreach':

    accumulate, when

Map without parallelisation.

system.time(
  my_sum_pur <- map(my_list, summary)
)
   user  system elapsed 
  4.188   0.254   4.453 
identical(my_sum_mc, my_sum_pur)
[1] TRUE

Map with parallelisation.

plan(multisession, workers = params$threads)
system.time(
  my_sum_fur <- future_map(my_list, summary)
)
   user  system elapsed 
  0.289   0.495   2.352 
identical(my_sum_pur, my_sum_fur)
[1] TRUE

Summary

So, which package should you use? BiocParallel and furrr are tailored for use with Bioconductor and purrr, so use those packages accordingly.

For parallelisation over a list, use parallel. The foreach function provides more flexibility when parallelising, so use the doParallel package if you have a more complicated task.


sessionInfo()
R version 4.2.0 (2022-04-22)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 20.04.4 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/liblapack.so.3

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] parallel  stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
[1] purrr_0.3.5         furrr_0.3.1         future_1.29.0      
[4] BiocParallel_1.32.1 doParallel_1.0.17   iterators_1.0.14   
[7] foreach_1.5.2       workflowr_1.7.0    

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.9        compiler_4.2.0    pillar_1.8.1      bslib_0.4.1      
 [5] later_1.3.0       git2r_0.30.1      jquerylib_0.1.4   tools_4.2.0      
 [9] getPass_0.2-2     digest_0.6.30     jsonlite_1.8.3    evaluate_0.18    
[13] tibble_3.1.8      lifecycle_1.0.3   pkgconfig_2.0.3   rlang_1.0.6      
[17] cli_3.4.1         rstudioapi_0.14   yaml_2.3.6        xfun_0.34        
[21] fastmap_1.1.0     httr_1.4.4        stringr_1.4.1     knitr_1.40       
[25] globals_0.16.1    fs_1.5.2          vctrs_0.5.0       sass_0.4.2       
[29] rprojroot_2.0.3   glue_1.6.2        listenv_0.8.0     R6_2.5.1         
[33] snow_0.4-4        processx_3.8.0    parallelly_1.32.1 fansi_1.0.3      
[37] rmarkdown_2.18    callr_3.7.3       magrittr_2.0.3    whisker_0.4      
[41] codetools_0.2-18  ps_1.7.2          promises_1.2.0.1  htmltools_0.5.3  
[45] httpuv_1.6.6      utf8_1.2.2        stringi_1.7.8     cachem_1.0.6