Installing and configuring BLAS thread control
sommer development team
2026-10-06
Source:vignettes/sommer.blas.threads.Rmd
sommer.blas.threads.RmdWhat the error means
An explicit request such as
mmes(..., blasThreads="auto") can produce:
Error: Runtime BLAS control is unavailable. Install flexiblas for a FlexiBLAS R installation, or RhpcBLASctl for a supported BLAS; alternatively configure BLAS before launching R and leave blasThreads=NULL.
This is a thread-control availability error, not an error in the model formula or covariance structures. Three separate components are involved:
- The BLAS library used by R. A threaded implementation, such as OpenBLAS or Intel MKL, performs dense numerical work. FlexiBLAS is a wrapper that selects a BLAS backend; it is not itself a guarantee of a threaded backend.
-
An R package that controls that library. The
current
sommerimplementation usesflexiblasfor an available FlexiBLAS installation, orRhpcBLASctlfor a recognized supported BLAS installation. -
Allocation-aware CPU detection.
parallellyis needed specifically forblasThreads="auto". An explicit positive integer does not require it.
These packages are optional dependencies. Installing
sommer does not necessarily install them, and installing a
control package does not replace R’s BLAS library. No separate CHOLMOD
installation is needed for solver="cholmod"; that solver
uses the CHOLMOD bundled with R’s Matrix package.
Identify the R installation and BLAS
Run these commands in the same R session that you use to fit the model:
R.version.string
#> [1] "R version 4.6.1 (2026-06-24)"
R.home()
#> [1] "/opt/R/4.6.1/lib/R"
.libPaths()
#> [1] "/home/runner/work/_temp/Library" "/opt/R/4.6.1/lib/R/site-library"
#> [3] "/opt/R/4.6.1/lib/R/library"
extSoftVersion()["BLAS"]
#> BLAS
#> "/usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3"
sessionInfo()[c("BLAS", "LAPACK")]
#> $BLAS
#> [1] "/usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3"
#>
#> $LAPACK
#> [1] "/usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so"Look for flexiblas, openblas, or
mkl in the BLAS path or description. On a cluster, a
different R module, terminal, RStudio session, or batch job can use a
different R installation and package library. Installing the adapter
into one installation does not make it available to another.
The current implementation recognizes non-FlexiBLAS paths containing
openblas, mkl, acml, or
goto for the RhpcBLASctl route. A generic name
such as libRblas.so or Rblas.dll may conceal
the actual backend; the current detection does not infer its identity
from that name. It also does not currently recognize a standalone BLIS
or Apple Accelerate backend through this route. A control package’s
capabilities alone do not override these detection rules.
Install the appropriate R packages
Route A: R uses FlexiBLAS
In the R installation used for fitting, run:
install.packages(c("flexiblas", "parallelly"),
repos="https://cloud.r-project.org")Restart R after installation, then check:
library(flexiblas)
stopifnot(flexiblas_avail())
flexiblas_current_backend()
flexiblas_get_num_threads()
parallelly::availableCores()flexiblas_avail() must be TRUE. If it is
FALSE, loading this R package has not made the running R
installation use FlexiBLAS. Use the correct R installation or ask your
administrator for an R build linked to FlexiBLAS.
If FlexiBLAS is available but its selected backend is a serial reference implementation, list the available backends:
flexiblas::flexiblas_list()
flexiblas::flexiblas_list_loaded()
# Only use OPENBLAS if that name appears in the available backend list.
backend <- flexiblas::flexiblas_load_backend("OPENBLAS")
flexiblas::flexiblas_switch(backend)
flexiblas::flexiblas_current_backend()
flexiblas::flexiblas_get_num_threads()Backend names and availability depend on the system configuration.
Switching backends changes BLAS/LAPACK for the whole R process; do it
before fitting models and record the choice for reproducibility.
mmes() changes the thread setting when requested, but does
not choose or install a FlexiBLAS backend.
For a FlexiBLAS R installation, installing only
RhpcBLASctl is not a substitute for this route: the current
sommer controller deliberately requires the FlexiBLAS
interface when the reported BLAS is FlexiBLAS.
Route B: R uses a recognized OpenBLAS or MKL library directly
Run:
install.packages(c("RhpcBLASctl", "parallelly"),
repos="https://cloud.r-project.org")Restart R, then check:
library(RhpcBLASctl)
extSoftVersion()["BLAS"]
blas_get_num_procs()
parallelly::availableCores()The getter alone does not prove that the backend supports changing
its thread count. Some unsupported configurations return one. The small
mmes() example below checks the complete path; an explicit
request that the backend does not accept results in an error rather than
a silent fallback.
If R still uses a reference BLAS
Installing RhpcBLASctl, flexiblas, or an
operating-system OpenBLAS package does not by itself guarantee that the
running R process uses a threaded BLAS. R must actually load or be
linked to that library.
On Linux, use a distribution-supported R/BLAS configuration or an administrator provided R module linked to OpenBLAS, MKL, or FlexiBLAS. Some distributions use a BLAS alternatives mechanism, while other installations require a different R build. Follow the instructions for your distribution and R installation; there is no universal library-switch command. For source builds, consult the R Installation and Administration manual.
On Windows, RhpcBLASctl may be available as a binary,
but installing it does not replace the BLAS shipped with R. Source
packages need the Rtools version matching your R release. On macOS,
source packages need the applicable command line compiler tools and,
when required, a compatible Fortran compiler. Use a supported R
distribution or build with a known threaded BLAS if the current backend
cannot be controlled by this implementation. Do not manually overwrite
R’s BLAS/LAPACK files with an arbitrary downloaded library.
If installation fails with missing headers or libraries, install the development files for the already selected numerical stack, or request them from your administrator. On a managed cluster, using its matching compiler and R modules is preferable to mixing incompatible system and user-installed libraries.
Verify control with a small model
First confirm that this session is loading a sommer
build containing the argument. Older installed builds can coexist with
the current source folder:
library(sommer)
packageVersion("sommer")
find.package("sommer")
stopifnot("blasThreads" %in% names(formals(mmes)))The following example runs only when the required controller and
parallelly are available. It uses small synthetic data,
rather than requiring your DT or MMTi objects.
It tests a one-thread request first, then allocation-aware automatic
sizing. Both are short verification fits, not convergence or performance
benchmarks.
blas <- unname(extSoftVersion()["BLAS"])
hasFlexiBLAS <- requireNamespace("flexiblas", quietly=TRUE) &&
isTRUE(tryCatch(flexiblas::flexiblas_avail(), error=function(error) FALSE))
hasDirectControl <- !is.na(blas) &&
!grepl("flexiblas", blas, ignore.case=TRUE) &&
grepl("openblas|mkl|acml|goto", blas, ignore.case=TRUE) &&
requireNamespace("RhpcBLASctl", quietly=TRUE)
if(requireNamespace("sommer", quietly=TRUE) &&
"blasThreads" %in% names(formals(sommer::mmes)) &&
requireNamespace("parallelly", quietly=TRUE) &&
(hasFlexiBLAS || hasDirectControl)){
local({
suppressPackageStartupMessages(library(sommer))
getThreads <- if(hasFlexiBLAS) flexiblas::flexiblas_get_num_threads else
RhpcBLASctl::blas_get_num_procs
before <- getThreads()
exampleData <- expand.grid(Name=factor(seq_len(12L)),
Env=factor(letters[1:3]), replicate=seq_len(2L))
exampleData$Yield <- as.integer(exampleData$Env) +
sin(as.integer(exampleData$Name)) + cos(seq_len(nrow(exampleData)))
for(request in list(1L, "auto")){
fit <- sommer::mmes(Yield~Env, random=~Name, rcov=~units,
data=exampleData, solver="cholmod", nIters=2,
dateWarning=FALSE, verbose=FALSE, blasThreads=request)
print(fit$threadingDiagnostics[c("blasBackend", "controller",
"blasThreadsRequested", "blasThreadsConfigured", "availableCPUs",
"openmpMaxThreads", "numericalPath", "averageCPUs")])
stopifnot(getThreads() == before)
}
})
}else{
message("Verification skipped: install the matching controller and parallelly, and load a sommer build with blasThreads.")
}
#> Solver selected: cholmod
#> $blasBackend
#> [1] "/usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3"
#>
#> $controller
#> [1] "RhpcBLASctl"
#>
#> $blasThreadsRequested
#> [1] 1
#>
#> $blasThreadsConfigured
#> [1] 1
#>
#> $availableCPUs
#> [1] 4
#>
#> $openmpMaxThreads
#> [1] 4
#>
#> $numericalPath
#> [1] "block Schur"
#>
#> $averageCPUs
#> [1] 1
#> Solver selected: cholmod
#> $blasBackend
#> [1] "/usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3"
#>
#> $controller
#> [1] "RhpcBLASctl"
#>
#> $blasThreadsRequested
#> [1] 4
#>
#> $blasThreadsConfigured
#> [1] 4
#>
#> $availableCPUs
#> [1] 4
#>
#> $openmpMaxThreads
#> [1] 4
#>
#> $numericalPath
#> [1] "block Schur"
#>
#> $averageCPUs
#> [1] 1.375blasThreadsConfigured should show the setting used
during the fit. The controller restores the previous BLAS setting on
return, including on errors. Therefore, querying BLAS threads
after mmes() returns can show the old count; that
does not mean the request was ignored. Use the returned diagnostics to
inspect the fit’s configuration.
Retry your model
After completing the route matching your R installation, the original model can be fitted without changing its statistical specification:
library(sommer)
mixm2 <- mmes(value~envf, nIters=5,
random=~vsm(fam(envf, 1), ism(id), Gu=MMTi) +
vsm(dsm(envf), ism(repf)),
rcov=~vsm(dsm(envf), ism(units)),
data=DT, blasThreads="auto")
mixm2$threadingDiagnosticsHere DT and MMTi are your existing data and
inverse relationship matrix. Alternatively, request an explicit count
such as blasThreads=4L. Installing parallelly
alone only resolves automatic CPU detection; it does not supply BLAS
control.
Cluster allocations and startup configuration
"auto" uses parallelly::availableCores()
rather than blindly using every CPU visible on the host. Scheduler
limits, affinity and container quotas can make this count smaller than
parallel::detectCores(). It does not request additional
CPUs from a scheduler or distribute a fit across nodes.
For example, a Slurm job requesting eight CPUs for one R process can start with:
#!/bin/bash
#SBATCH --nodes=1
#SBATCH --ntasks=1
#SBATCH --cpus-per-task=8
# Load your site's compatible R/numerical-library modules here.
export OMP_NUM_THREADS=1
Rscript fit.RWith a working runtime controller, fit.R can use
blasThreads="auto". Setting OpenMP to one isolates BLAS
threading and avoids many OpenMP workers each launching a BLAS team; it
is a starting point to benchmark, not a universally optimal setting.
BLAS and Sommer’s OpenMP loops are separate threading layers. Some BLAS
builds themselves use OpenMP, so startup variables and runtime setters
can interact.
If runtime control is unavailable but your BLAS supports startup
thread configuration, leave blasThreads=NULL and configure
it before starting R. For a pthread-based OpenBLAS
backend, for example:
For MKL, use its documented MKL_NUM_THREADS setting;
OpenMP-based OpenBLAS uses OMP_NUM_THREADS. Follow the
selected backend’s documentation. These variables cannot make a serial
reference BLAS threaded, and changing them with
Sys.setenv() after BLAS initializes may have no effect.
Without a supported query interface, the BLAS thread count can remain
unknown to Sommer even when startup configuration works.
Troubleshooting and interpreting usage
| Symptom | Check or action |
|---|---|
Runtime BLAS control is unavailable |
Check the BLAS identity in this R session; install its matching
adapter in an active .libPaths() library. |
blasThreads='auto' requires ... parallelly |
Install parallelly, or supply a positive integer
instead. |
flexiblas_avail() is FALSE
|
Use R linked to FlexiBLAS; installing its R interface does not relink R. |
| Adapter installed but BLAS has a generic name | Current detection may not recognize that installation; use startup
settings with blasThreads=NULL or an explicitly identified
supported R build. |
| Backend did not accept the requested setting | Check whether it is serial or has a thread limit; select a supported threaded backend or use an accepted count. |
unused argument (blasThreads=...) |
The loaded Sommer is older than the source tree; install the current tree and restart R. |
| Packages available interactively but missing in a job | Check the job’s R module, .libPaths() and
R_LIBS_USER; install or expose packages for that R
installation. |
Configured counts are not actual utilization.
openmpMaxThreads is an OpenMP limit, not a CHOLMOD or BLAS
usage count. averageCPUs measures aggregate process CPU
seconds divided by elapsed seconds, not an instantaneous number of
active threads. Factorization measurements are in
threadingDiagnostics$factorizations; their
measurement fields distinguish numeric CHOLMOD/LDLT work
from the broader block-Schur workload.
Even solver="cholmod" can select a structure-aware Schur
engine for an eligible model. Small blocks, serial work, memory
bandwidth and thread startup overhead can prevent speedups. Confirm the
numerical path and benchmark several counts on representative data
rather than assuming that "auto" is fastest. The
repository’s benchmarks/thread-scaling.R and accompanying
README provide a repeated scaling benchmark with numerical-agreement
checks.