Knowing your system

Basics

System Characteristics

Understanding the system your code will be run on is an important step to developing and running R code efficiently and in parallel. This includes both hardware and software.

There are a number of ways to explore your system from within R.

Let’s open our workshop materials project and create a script to work in to explore our system from within R.

sessionInfo

The easiest place to start would be with function sessionInfo() which prints version information about R and attached or loaded packages.

Here’s the output of sessionInfo() on my laptop:

R version 4.4.0 (2024-04-24)
Platform: aarch64-apple-darwin20
Running under: macOS 15.1.1

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Europe/Athens
tzcode source: internal

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

other attached packages:
[1] pak_0.8.0        reprex_2.1.0     testthat_3.2.1.1 devtools_2.4.5  
[5] usethis_3.0.0   

loaded via a namespace (and not attached):
 [1] stringi_1.8.4     digest_0.6.37     magrittr_2.0.3    evaluate_1.0.0   
 [5] pkgload_1.4.0     fastmap_1.2.0     jsonlite_1.8.9    pkgbuild_1.4.4   
 [9] sessioninfo_1.2.2 brio_1.1.5        urlchecker_1.0.1  promises_1.3.0   
[13] purrr_1.0.2       cli_3.6.3         shiny_1.8.1.1     rlang_1.1.4      
[17] ellipsis_0.3.2    remotes_2.5.0     withr_3.0.2       cachem_1.1.0     
[21] yaml_2.3.10       tools_4.4.0       memoise_2.0.1     httpuv_1.6.15    
[25] credentials_2.0.1 vctrs_0.6.5       logger_0.3.0      R6_2.5.1         
[29] mime_0.12         lifecycle_1.0.4   stringr_1.5.1     fs_1.6.5         
[33] htmlwidgets_1.6.4 miniUI_0.1.1.1    later_1.3.2       glue_1.8.0       
[37] profvis_0.3.8     Rcpp_1.0.13       xfun_0.47         rstudioapi_0.16.0
[41] sys_3.4.3         knitr_1.48        xtable_1.8-4      htmltools_0.5.8.1
[45] rmarkdown_2.28    compiler_4.4.0    askpass_1.2.1     openssl_2.2.2    

Apart from information about packages loaded in the current R session, the function also prints some software information like the Platform and OS version, the Linear Algebra Libraries R is using (BLAS: Basic Linear Algebra Subprogram and LAPACK: Linear Algebra Package) and locale.

Note that sessionInfo() displays information about the software environment in your current R session. When working in R or Rstudio, for me the BLAS library defaults to my system Accelerate BLAS library that ships with macOS (/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib). When rendering the course material site built in Quarto though, it defaults to the BLAS library that ships with R (/Library/Frameworks/R.framework/Versions/*-arm64/Resources/lib/libRblas.0.dylib)

But the hardware R is running on is also important to assessing what performance you might be able to achieve and the strategies you might consider to achieve better performance.

benchmarkme 📦

benchmarkme is a nifty R package you can use to both access information about hardware and software available to R on your system as well as functionality to benchmark your system using benchmarks for numerical operations as well as for benchmarking I/O.

Let’s use the library to first explore our system.

The package contains a suite of functions for accessing information about your systems hardware and software relevant to R.

34.4 GB
$vendor_id
character(0)

$model_name
[1] "Apple M1 Pro"

$no_of_cores
[1] 10
$blas
[1] "/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib"

$lapack
[1] "/Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib"
$OS.type
[1] "unix"

$file.sep
[1] "/"

$dynlib.ext
[1] ".so"

$GUI
[1] "X11"

$endian
[1] "little"

$pkgType
[1] "mac.binary.big-sur-arm64"

$path.sep
[1] ":"

$r_arch
[1] ""
$platform
[1] "aarch64-apple-darwin20"

$arch
[1] "aarch64"

$os
[1] "darwin20"

$system
[1] "aarch64, darwin20"

$status
[1] ""

$major
[1] "4"

$minor
[1] "4.0"

$year
[1] "2024"

$month
[1] "04"

$day
[1] "24"

$`svn rev`
[1] "86474"

$language
[1] "R"

$version.string
[1] "R version 4.4.0 (2024-04-24)"

$nickname
[1] "Puppy Cup"

Monitoring your system

All operating systems have dedicated system activity monitors, available through a GUI or through the terminal.

GUIs

Let’s explore what’s currently going on on our systems through our OS’s dedicated GUI.

Depending on your OS,

Here’s what Activity Monitor looks like on my Mac.

CPU, Memory, Disk and Network monitoring is split across tabs but your monitor might show everything in the same tab across different graphs. Some terminology and information shown might differ but ultimately, all monitors attempt to show an overview of similar system activity.

Each row in the monitor table of activities represents a process, each process having its own PID (process ID). They are all controlled by the kernel. As new tasks are initiated (for example when we open a new application), the kernel creates a new process for it. If there are multiple cores available on your system, the kernel will allocate new processes to inactive cores. When more processes than cores are running, the kernel uses context switching to keep multiple processes running on a single core.

Terminal

top

On macOS and Linux distributions, the top command can also be run in the terminal which initiates system monitoring in the terminal. top shows a summary of system activity as well as periodically displaying a list of processes on the system in sorted order. The default key for sorting is pid, but other keys can be used instead.

Example of Activity monitoring when running R

Let me run the following matrix multiplication code on my system so you can observe what happens while monitoring through top.

n <- 4*1024
A <- matrix( rnorm(n*n), ncol=n, nrow=n )
B <- matrix( rnorm(n*n), ncol=n, nrow=n )
C <- A %*% B

The rsession process moves to the top and, running in a single thread, uses ~100% of the available CPU while running. When finished, the process drops from the top and goes back to using just 0.2% of CPU as R waits for our next command.

Benchmarking your system

As aforementioned, the benchmarkme package provides a set of benchmarks to help quantify your system. More interestingly, it allows you to compare your timings with timings crowd-sourced on other systems.

There are two groups of benchmarks:

  • benchmark_std(): this benchmarks numerical operations such as loops and matrix operations. The benchmark comprises of three separate benchmarks: prog, matrix_fun, and matrix_cal.

  • benchmark_io(): this benchmarks reading and writing a 5 / 50, MB csv file.

You can compare your results to other users by assigning the output of the benchmarking to a variable and plotting it.

std_bm <- benchmark_std()
# Programming benchmarks (5 tests):
    3,500,000 Fibonacci numbers calculation (vector calc): 0.103 (sec).
    Grand common divisors of 1,000,000 pairs (recursion): 0.194 (sec).
    Creation of a 3,500 x 3,500 Hilbert matrix (matrix calc): 0.121 (sec).
    Creation of a 3,000 x 3,000 Toeplitz matrix (loops): 0.635 (sec).
    Escoufier's method on a 60 x 60 matrix (mixed): 0.559 (sec).
# Matrix calculation benchmarks (5 tests):
    Creation, transp., deformation of a 5,000 x 5,000 matrix: 0.21 (sec).
    2,500 x 2,500 normal distributed random matrix^1,000: 0.114 (sec).
    Sorting of 7,000,000 random values: 0.615 (sec).
    2,500 x 2,500 cross-product matrix (b = a' * a): 9.69 (sec).
    Linear regr. over a 5,000 x 500 matrix (c = a \ b'): 0.82 (sec).
# Matrix function benchmarks (5 tests):
    Cholesky decomposition of a 3,000 x 3,000 matrix: 5.13 (sec).
    Determinant of a 2,500 x 2,500 random matrix: 1.74 (sec).
    Eigenvalues of a 640 x 640 random matrix: 0.417 (sec).
    FFT over 2,500,000 random values: 0.082 (sec).
    Inverse of a 1,600 x 1,600 random matrix: 1.42 (sec).
plot(std_bm)
You are ranked 1 out of 749 machines.

Press return to get next plot 
You are ranked 314 out of 747 machines.

Press return to get next plot 
You are ranked 163 out of 747 machines.

io_bm <- benchmark_io()
Preparing read/write io
# IO benchmarks (2 tests) for size 50 MB:
     Writing a csv with 6250000 values: 3.29 (sec).
     Writing a csv with 6250000 values: 3.29 (sec).
     Writing a csv with 6250000 values: 3.31 (sec).
     Reading a csv with 6250000 values: 1.18 (sec).
     Reading a csv with 6250000 values: 1.17 (sec).
     Reading a csv with 6250000 values: 1.17 (sec).
# IO benchmarks (2 tests) for size 5 MB:
     Writing a csv with 625000 values: 0.334 (sec).
     Writing a csv with 625000 values: 0.334 (sec).
     Writing a csv with 625000 values: 0.335 (sec).
     Reading a csv with 625000 values: 0.118 (sec).
     Reading a csv with 625000 values: 0.117 (sec).
     Reading a csv with 625000 values: 0.117 (sec).
plot(io_bm)
You are ranked 1 out of 119 machines.

Press return to get next plot 
You are ranked 1 out of 119 machines.

Press return to get next plot 
You are ranked 1 out of 135 machines.

Press return to get next plot 
You are ranked 1 out of 135 machines.

All in all my system seems comparatively fast! M1 chips have indeed been shown to be generally very performant. The linear algebra benchmarks could undoubtedly be improved by using an Optimised Linear Algebra library. Having said that I seriously doubt I have the most powerful system out there and it’s likely that the timings its being compared to are somewhat out of date.

Back to top