MyNixOS website logo
Description

Runtime 'SIMD' Dispatch Templates for 'C' Code in 'R' Packages.

Provides templates and a working example for runtime Single Instruction Multiple Data ('SIMD') dispatch in 'C' code used by 'R' packages. Packages can stage scalar and architecture-specific kernel objects during configuration, then select a compiled and CPU-supported implementation at runtime through guarded function pointers. The package also vendors the header-only 'SIMDe' library for downstream packages through the 'LinkingTo' field.

RsimdDispatch

R-CMD-check R-universe

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)
backendmedian_msmb_per_seconditerationsspeedup_vs_scalar
scalar11.3554616.141201.000
avx21.99126181.311205.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
Metadata

Version

0.1.1

License

Unknown

Platforms (79)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    uefi
    wasip1
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-uefi
  • aarch64-windows
  • aarch64_be-none
  • arc-linux
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-linux
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • sh4-linux
  • vc4-none
  • wasm32-wasip1
  • wasm64-wasip1
  • x86_64-cygwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-uefi
  • x86_64-windows