[Rd] R 4.5.3 macOS arm64: severe matrix-product performance regression vs R 4.5.2 due to libRblas.dylib selecting libRblas.0.dylib instead of libRblas.vecLib.dylib

Jeffrey Racine r@c|nej@ @end|ng |rom gm@||@com
Fri Mar 27 00:48:31 CET 2026


Greetings,

I am reporting a substantial performance regression on macOS arm64 when
moving from `R 4.5.2` to `R 4.5.3`.

This first appeared while benchmarking the `np` package, where a
conditional-density least-squares cross-validation route (`npcdensbw(...,
bwmethod="cv.ls")`) became roughly 5x slower under `R 4.5.3`. After
investigation, the package itself appears not to be at fault: the
regression can be reproduced directly with base matrix-product operations
and seems to be caused by a change in which BLAS library `R 4.5.3` uses on
macOS arm64.

### System

- Hardware: Apple Silicon (`aarch64`)
- OS: macOS Tahoe 26.4
- `R 4.5.2`: `R version 4.5.2 (2025-10-31)`
- `R 4.5.3`: `R version 4.5.3 (2026-03-11)`

### Summary of findings

Using the official macOS arm64 installers:

- `R-4.5.2-arm64.pkg`
- `R-4.5.3-arm64.pkg`

I unpacked both installers and compared their framework payloads.

Key visible result:

- In `R 4.5.2`, `Resources/lib/libRblas.dylib` points to
`libRblas.vecLib.dylib`
- In `R 4.5.3`, `Resources/lib/libRblas.dylib` points to `libRblas.0.dylib`

Both payloads still contain both files:

- `libRblas.0.dylib`
- `libRblas.vecLib.dylib`

### Payload evidence

`R 4.5.2` unpacked payload:

```text
lrwxr-xr-x ... libRblas.dylib -> libRblas.vecLib.dylib
-rwxr-xr-x ... libRblas.0.dylib
-rwxr-xr-x ... libRblas.vecLib.dylib
```

`R 4.5.3` unpacked payload:

```text
lrwxr-xr-x ... libRblas.dylib -> libRblas.0.dylib
-rwxr-xr-x ... libRblas.0.dylib
-rwxr-xr-x ... libRblas.vecLib.dylib
```

Additional payload comparison:

- `Makeconf`, `ldpaths`, `Renviron`, `bin/R`, and installer scripts were
byte-identical between `4.5.2` and `4.5.3`
- the executable `__text` sections of `libRblas.0.dylib` and
`libRlapack.dylib` were identical
- the executable `__text` section of `libR.dylib` differed

### How the runtimes were tested

I ran both R versions directly from the unpacked installer payloads using
`Resources/bin/exec/R`, with `R_HOME` and `DYLD_LIBRARY_PATH` pointed at
each unpacked framework. This avoids relying on the live installed system
framework.

Example invocation:

```bash
R_HOME=/tmp/R452_pkg/R-fw.pkg/Payload/R.framework/Versions/4.5-arm64/Resources
\
DYLD_LIBRARY_PATH=/tmp/R452_pkg/R-fw.pkg/Payload/R.framework/Versions/4.5-arm64/Resources/lib
\
/tmp/R452_pkg/R-fw.pkg/Payload/R.framework/Versions/4.5-arm64/Resources/bin/exec/R
--vanilla -q -f /tmp/r_blas_probe2.R
```

and similarly for `R 4.5.3`.

### Minimal reproducer

```r
set.seed(1)
n <- 1000000L
x <- rnorm(n)
y <- rnorm(n)
A <- matrix(rnorm(2000L*2000L), 2000L, 2000L)
B <- matrix(rnorm(2000L*2000L), 2000L, 2000L)

cat(R.version.string, "\n")
cat("crossprod vec\n")
print(system.time(for(i in 1:40) z <- crossprod(x, y)))
cat("matmult\n")
print(system.time(C <- A %*% B))
```

### Observed timings

`R 4.5.2`:

- `crossprod()` elapsed about `0.032s`
- `%*%` elapsed about `0.034s`

Observed output:

```text
R version 4.5.2 (2025-10-31)
crossprod vec
user system elapsed
0.031 0.002 0.032
matmult
user system elapsed
0.058 0.003 0.034
```

`R 4.5.3`:

- `crossprod()` elapsed about `0.072s`
- `%*%` elapsed about `2.443s`

Observed output:

```text
R version 4.5.3 (2026-03-11)
crossprod vec
user system elapsed
0.071 0.000 0.072
matmult
user system elapsed
2.381 0.057 2.443
```

This is a very large regression for `%*%` and a clear regression for
`crossprod()`.

### Loaded libraries during runtime

For the unpacked `R 4.5.2` process, `lsof` showed:

```text
... /tmp/R452_pkg/.../Resources/lib/libRblas.vecLib.dylib
... /tmp/R452_pkg/.../Resources/lib/libR.dylib
... /tmp/R452_pkg/.../Resources/lib/libRlapack.dylib
```

`vmmap` also showed that Apple Accelerate / vecLib libraries were mapped.

For the unpacked `R 4.5.3` process, `lsof` showed:

```text
... /tmp/R453_pkg/.../Resources/lib/libRblas.0.dylib
... /tmp/R453_pkg/.../Resources/lib/libR.dylib
... /tmp/R453_pkg/.../Resources/lib/libRlapack.dylib
```

### Critical confirmation

In a temporary copy of the unpacked `R 4.5.3` framework, I changed only
this symlink:

- from:

```text
libRblas.dylib -> libRblas.0.dylib
```

- to:

```text
libRblas.dylib -> libRblas.vecLib.dylib
```

After that single change, the `R 4.5.3` runtime regained the fast timings:

```text
R version 4.5.3 (2026-03-11)
crossprod vec
user system elapsed
0.032 0.002 0.033
matmult
user system elapsed
0.055 0.003 0.032
```

`lsof` then showed:

```text
... /tmp/R453_test/.../Resources/lib/libRblas.vecLib.dylib
... /tmp/R453_test/.../Resources/lib/libR.dylib
... /tmp/R453_test/.../Resources/lib/libRlapack.dylib
```

So the regression appears to be directly tied to the BLAS target selected
by `libRblas.dylib` in the macOS arm64 `R 4.5.3` distribution.

### Why this matters

This caused a large apparent regression in package benchmarks, but the
package code was not the source. The underlying issue is visible in base
matrix-product operations.

For performance-sensitive code relying on repeated dot products or matrix
multiplies, the effect is substantial.

One affected package-level example was `np::npcdensbw(..., bwmethod =
"cv.ls")`,
whose hot path repeatedly uses BLAS-backed dot products inside
conditional-density CVLS block computations.

### Expected behavior

I would expect `R 4.5.3` on macOS arm64 not to regress dramatically
relative to `R 4.5.2` for basic matrix products on the same hardware.

At minimum, if the BLAS target changed intentionally, I would expect that
change not to impose such a severe runtime penalty.

### Request

Could you please confirm whether the `libRblas.dylib -> libRblas.0.dylib`
change in the macOS arm64 `R 4.5.3` installer was intentional?

If intentional, is there a recommended way to restore the previous fast
vecLib-backed behavior?

If not intentional, this appears to be a macOS arm64 packaging/runtime
regression in `R 4.5.3`.

I can provide exact command transcripts and additional package-level
evidence if helpful.

## Local investigation notes

This report was motivated by a package-level timing cliff in `np`, but the
package code appears exonerated:

- the relevant `np` hot path remains unchanged in current source
- the same slowdown occurs across different `np` source snapshots when run
under `R 4.5.3`
- the same `np` source and same compiled package are fast again under `R
4.5.2`

The key practical interpretation is:

- not a package source regression
- likely an R macOS BLAS-linkage/runtime regression in `4.5.3`

## Relevant local files

- [R-4.5.2-arm64.pkg](/Users/jracine/Development/R-4.5.2-arm64.pkg)
- [R-4.5.3-arm64.pkg](/Users/jracine/Development/R-4.5.3-arm64.pkg)

	[[alternative HTML version deleted]]



More information about the R-devel mailing list