Pure-C runtime SIMD dispatch templates for R packages. Stage scalar, SSE, AVX, AVX-512, and NEON kernel objects during configuration; link one shared library and switch among compiled and CPU-supported implementations at runtime without unsafe overrides.
Install
install.packages( "RsimdDispatch", repos = c("https://sounkou-bioinfo.r-universe.dev", "https://cloud.r-project.org") )
From GitHub:
# install.packages("remotes") remotes::install_github("sounkou-bioinfo/RsimdDispatch")
Use in other packages
Copy the C dispatch scaffold from your package root:
RsimdDispatch::use_simd_dispatch(pkg = "MyPackage", prefix = "mypkg")
The helper updates DESCRIPTION with LinkingTo: RsimdDispatch, writes
the scaffold files, and substitutes package-specific registration and C
symbol prefixes. It does not add a runtime dependency on
RsimdDispatch. Replace the demo count_nonzero() kernels under
tools/kernels/ with your own scalar/SSE/AVX/NEON kernels. The
generated configure script probes compiler support, stages selected
kernel objects in src/rsd-kernels/, and leaves src/Makevars to link
those objects with the baseline R API, CPU detection, and dispatcher.
See the package vignettes for the downstream-package workflow and dispatch semantics.
API
library(RsimdDispatch) x <- as.raw(c(0, 1, 2, 0, 255)) count_nonzero(x) #> [1] 3 simd_backend() #> [1] "avx2" simd_set_backend("scalar") simd_backend() #> [1] "scalar" simd_set_backend("avx2") simd_backend() #> [1] "avx2" simd_set_backend("auto") simd_info()[c("compiled_backends", "cpu_supported_backends", "available_backends", "simde_native_backends")] #> $compiled_backends #> [1] "scalar" "sse2" "sse41" "avx2" "avx512" #> #> $cpu_supported_backends #> [1] "scalar" "sse2" "sse41" "avx2" #> #> $available_backends #> [1] "scalar" "sse2" "sse41" "avx2" #> #> $simde_native_backends #> [1] "sse2" "sse41" "avx2" "avx512" simde_info()[c("version", "commit")] #> $version #> [1] "0.8.4" #> #> $commit #> [1] "f3e8262173b7089db9a9d57a9ecef8dd07ad9c97" simd_dispatch_template_path() #> [1] "/usr/local/lib/R/site-library/RsimdDispatch/templates/dispatch-c"
simd_set_backend() rejects uncompiled or CPU-unsupported backends.
There is no unsafe override.
Example
count_nonzero(x) #> [1] 3 simd_info()[c("selected_backend", "available_backends")] #> $selected_backend #> [1] "avx2" #> #> $available_backends #> [1] "scalar" "sse2" "sse41" "avx2"
Switching backends is same-process because all variants are linked into one R shared library and selected through guarded function pointers.
simd_set_backend("scalar") scalar_count <- count_nonzero(x) simd_set_backend("avx2") avx2_count <- count_nonzero(x) simd_set_backend("auto") data.frame( scalar = scalar_count, avx2 = avx2_count, selected_after_auto = simd_backend() ) #> scalar avx2 selected_after_auto #> 1 3 3 avx2
Scalar vs AVX2 benchmark
set.seed(1) x <- as.raw(sample.int(256L, 50 * 1024 * 1024, replace = TRUE) - 1L) mark <- bench::mark( scalar = { simd_set_backend("scalar") count_nonzero(x) }, avx2 = { simd_set_backend("avx2") count_nonzero(x) }, iterations = 20, check = TRUE, memory = FALSE ) simd_set_backend("auto") bench <- data.frame( backend = as.character(mark$expression), median_ms = as.numeric(mark$median) * 1000, mb_per_second = length(x) * as.numeric(mark$`itr/sec`) / 1e6, iterations = mark$n_itr, row.names = NULL ) bench$speedup_vs_scalar <- bench$mb_per_second / bench$mb_per_second[bench$backend == "scalar"] knitr::kable(bench, digits = 3)
| backend | median_ms | mb_per_second | iterations | speedup_vs_scalar |
|---|---|---|---|---|
| scalar | 11.355 | 4616.141 | 20 | 1.000 |
| avx2 | 1.991 | 26181.311 | 20 | 5.672 |
Development
Updating bundled SIMDe
This is a maintainer workflow for RsimdDispatch itself, not something
downstream packages need to run. Downstream packages use
LinkingTo: RsimdDispatch and the copied dispatch template; they do not
re-vendor SIMDe.
RsimdDispatch vendors the full SIMDe header tree under
inst/include/simde. To update the pinned checkout and regenerate
bundled-code notices:
Rscript tools/vendor-simde.R Rscript tools/update-authors.R
Provenance lives in inst/vendor/simde/VERSION, inst/AUTHORS, and
inst/LICENCE.note.
Package maintenance
The top-level Makefile uses GNU make.
make rd # roxygen docs and NAMESPACE make readme # evaluate README.Rmd and write README.md make test make check