Running R in Parallel on DCC
This guide explains how to run R with multi-threaded linear algebra on the DCC using the R-openblas module, how to verify and control the number of BLAS threads, and how to install your own R packages.
The code for this example is available at: https://github.com/DukeRC/code/tree/main/Parallel-R-on-DCC
Many common R operations, such as matrix multiplication (%*%, crossprod), Cholesky decomposition (chol), solving linear systems (solve), and eigendecomposition (eigen), are executed by the BLAS/LAPACK libraries R is linked against. If R is built against a single-threaded BLAS, these operations run on one core no matter how many CPUs your Slurm job requests. Workloads dominated by dense linear algebra (e.g., Gaussian process regression, which requires factorizing dense \(n \times n\) covariance matrices at every iteration) can be an order of magnitude slower on a single-threaded BLAS.
The standard R modules on the DCC use R's reference BLAS, which is single-threaded. The R-openblas module is built against a multi-threaded OpenBLAS, so dense linear algebra automatically uses the cores allocated to your job through shared-memory (OpenMP/pthread) parallelism within a single node.
Loading the module
Request an interactive Slurm session with multiple cores (-c <num_cores>) and load the module,
Verifying BLAS threads
The RhpcBLASctl package (pre-installed in the module) reports and controls the number of threads available to the BLAS,
blas_get_num_procs() should match the number of cores requested with -c. You can also confirm which BLAS/LAPACK R is linked against with sessionInfo(), which for R-openblas reports an OpenBLAS library under Matrix products,
sessionInfo()
R version 4.6.1 (2026-06-24)
Platform: x86_64-pc-linux-gnu
Running under: AlmaLinux 9.7 (Moss Jungle Cat)
Matrix products: default
BLAS/LAPACK: /admin/apps/rhel9/OpenBLAS-0.3.33/build/lib/libopenblas_sapphirerapidsp-r0.3.33.so; LAPACK version 3.12.0
If instead you see libRblas.so (the reference BLAS), your session is using a single-threaded build.
OpenBLAS picks up the cores available to the job automatically, but it is good practice to set the thread count explicitly in batch jobs so it always matches your Slurm allocation,
The thread count can also be changed at runtime from within R,
Example: benchmarking dense linear algebra
The benchmark script and a Slurm submission template for this example are available in the DCC example-code repository: https://github.com/DukeRC/code/tree/main/Parallel-R-on-DCC.
The benchmark is intentionally small and user-facing. It checks the R version, BLAS/LAPACK backend, and thread-related environment variables, then sweeps BLAS thread counts over 1, 2, 4, 8, 16, 32 threads, capped by the cores available in the Slurm allocation. For each thread count, it measures the median elapsed time across three repetitions for four common dense linear algebra operations,
| Routine | R operation | BLAS/LAPACK workload |
|---|---|---|
| Matrix multiplication | A %*% B |
DGEMM |
| Cross-product | crossprod(A) |
symmetric rank-k / DGEMM-like work |
| Cholesky factorization | chol(K) |
dense SPD factorization |
| Linear solve | solve(K, b) |
Cholesky-based solve for dense SPD-like systems |
The script constructs two dense random matrices A and B, a vector b, and a symmetric positive-definite matrix K = crossprod(A) + I. It runs one warm-up evaluation before timing each routine, reports median elapsed time, and computes speedup and parallel efficiency relative to the single-threaded baseline.
Run it with optional arguments for matrix size, timed repetitions, and the thread sweep,
For example,
The output should first confirm that R is using the OpenBLAS-backed module,
R version: R version 4.6.1 (2026-06-24)
BLAS: /admin/apps/rhel9/OpenBLAS-0.3.33/build/lib/libopenblas_sapphirerapidsp-r0.3.33.so
LAPACK: /admin/apps/rhel9/OpenBLAS-0.3.33/build/lib/libopenblas_sapphirerapidsp-r0.3.33.so
Cores detected: 32
If the BLAS line shows libRblas.so, then you are using R's reference BLAS instead of the R-openblas module and should not expect threaded dense linear algebra.
Representative results for n = 6000, three timing repetitions, and a 32-core Slurm allocation are shown below.
| Operation | 1 thread (s) | 2 threads (s) | 4 threads (s) | 8 threads (s) | 16 threads (s) | 32 threads (s) | 32-thread speedup |
|---|---|---|---|---|---|---|---|
A %*% B |
3.972 | 2.017 | 1.076 | 0.679 | 0.634 | 0.392 | 10.13x |
crossprod(A) |
2.090 | 1.146 | 0.671 | 0.476 | 0.469 | 0.357 | 5.85x |
chol(K) |
1.017 | 0.633 | 0.422 | 0.460 | 0.342 | 0.284 | 3.58x |
solve(K, b) |
1.796 | 1.098 | 0.709 | 0.747 | 0.557 | 0.517 | 3.47x |
The matrix multiplication workload scales best, reaching about a 10x speedup on 32 threads. The Cholesky and solve workloads still improve, but the benefit is smaller and not perfectly monotonic: for this matrix size, 8 threads were slightly slower than 4 threads for chol(K) and solve(K, b), while 16 and 32 threads recovered additional performance. This is normal for threaded BLAS workloads. Scaling depends on the operation, matrix size, CPU architecture, memory bandwidth, cache behavior, and socket layout.
The practical takeaway is that the R-openblas module can materially accelerate dense linear algebra without changing the R code, but the best core count is problem-dependent. Benchmark your own workload with several -c <num_cores> values before scaling production jobs.
Batch jobs
A typical Slurm submission script is shown below. The example-code repository linked above includes a ready-to-use version that runs the benchmark script.
#!/bin/bash
#SBATCH -J parallel-R # Job name
#SBATCH -p common # Partition name
#SBATCH -c 8 # Cores for the (single) task
#SBATCH --mem=64G # Memory
#SBATCH -t 04:00:00 # Walltime limit (hh:mm:ss)
module load R-openblas/4.6.1
export OPENBLAS_NUM_THREADS=$SLURM_CPUS_PER_TASK
export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
Rscript my_analysis.R
Submit with,
Note that BLAS threading is shared-memory parallelism, i.e. all threads must be on the same node, so request cores with -c (cpus-per-task) rather than -n (tasks).
Installing R packages
You can install R packages yourself. By default they are installed in a version-specific library under your home directory, which R searches first,
> .libPaths()
[1] "/hpc/home/<netid>/R/x86_64-pc-linux-gnu-library/4.6"
[2] "/admin/apps/rhel9/R-4.6.1-openblas/site-library"
[3] "/admin/apps/rhel9/R-4.6.1-openblas/lib64/R/library"
Install a package from within R,
or directly from the shell,
You can also pass a list of multiple packages to be installed,
The general syntax to install a package is,
This instructs R to search for package x in repository z and to install it in local directory y.
If a package you need fails to build or you would like it added to the site library, contact rescomputing@duke.edu.
RStudio on Open OnDemand
The RStudio server Apptainer containers on Open OnDemand are also built against a multi-threaded OpenBLAS, so interactive RStudio sessions get the same threaded linear algebra out of the box. For batch workloads, prefer the R-openblas module with a Slurm script as shown above, which avoids the overheads of an interactive session and lets you queue long-running jobs.