Skip to contents

Downstream packages use RsimdDispatch as a template and header provider. The usual workflow is:

  1. call use_simd_dispatch() from the package root;
  2. replace the demo count_nonzero() and convolve1d() kernels with package-specific kernels;
  3. let configure stage the dispatch core, CPU feature detection, and kernel objects;
  4. link everything into one shared library via the generated src/Makevars.

use_simd_dispatch() updates DESCRIPTION, adding RsimdDispatch to LinkingTo, and copies package-specific scaffold files. It does not add a runtime dependency on RsimdDispatch. The LinkingTo entry makes bundled SIMDe headers available to C code:

#include <simde/x86/avx2.h>
#include <simde/arm/neon.h>

Copy the scaffold

From the downstream package root:

RsimdDispatch::use_simd_dispatch(pkg = "MyPackage", prefix = "mypkg")

If pkg is omitted, the package name is read from DESCRIPTION. If prefix is omitted, a lowercase C identifier prefix is derived from the package name. The helper performs the important substitutions, including @useDynLib, R_init_<Package>(), sd_, SD_, and RC_ symbols.

The copied scaffold includes:

configure
configure.win
cleanup
tools/configure-simd-dispatch.sh
tools/simdDispatch/kernels/kernel_scalar.c
tools/simdDispatch/kernels/kernel_sse2.c
tools/simdDispatch/kernels/kernel_sse41.c
tools/simdDispatch/kernels/kernel_avx2.c
tools/simdDispatch/kernels/kernel_avx512.c
tools/simdDispatch/kernels/kernel_neon.c
tools/simdDispatch/kernels/kernel_wasm_simd128.c
tools/simdDispatch/kernels/kernel_common.h
src/Makevars.in
src/Makevars.win.in

Replace the demo kernels

The demo operations are registered by each backend file. For example, the AVX2 staged source registers both demo operations:

static const SdKernelDef sd_avx2_kernels[] = {
    {SD_OP_COUNT_NONZERO, SD_SIG_RAW_COUNT, sd_count_nonzero_avx2_invoke},
    {SD_OP_CONVOLVE1D, SD_SIG_F64_CONVOLVE, sd_convolve1d_avx2_invoke},
    SD_KERNEL_DEF_END
};

void sd_register_avx2(SdDispatchBuilder *builder) {
    sd_register_kernel_table(builder, sd_avx2_kernels);
}

SSE2 and SSE4.1 currently register only count_nonzero(), which demonstrates operation-level backend support. A backend intentionally omits an operation by not registering that slot. Explicitly selecting such a backend is allowed, but calling the unsupported operation errors clearly; "auto" skips that backend for the unsupported operation.

For a real package, replace the demo operation signatures with your own and keep the same structure. Staged kernel files include tools/simdDispatch/kernels/kernel_api.h, not private headers from src/; the dispatch core implements that kernel-facing registration ABI.

R API wrapper        ordinary src/Makevars compilation
R registration       ordinary src/Makevars compilation
dispatch core        staged baseline object under src/rsd-lib/
CPU feature checks   staged baseline object under src/rsd-lib/
scalar kernel        staged by configure under src/rsd-kernels/
SSE/AVX/NEON/wasm    staged by configure as optional objects under src/rsd-kernels/

Do not put -mavx2, -mavx512*, -msimd128, or -march=native in global package flags. The configure helper keeps ISA flags local to optional staged kernel objects, and the dispatcher remains safe on baseline CPUs.

Extending the number of operations

The staged build model already scales across operations: if you add another function to each existing tools/simdDispatch/kernels/kernel_*.c file, the same staged object for that backend contains the new implementation. For a small number of operations, keep the dispatch code explicit and add:

  1. add a row to tools/simdDispatch/kernels/operations.def (operation ID, lower name, signature ID, call-frame type); if the operation uses a new call-frame struct, also add the struct to kernel_api.h and a row to signatures.def;
  2. one implementation plus a small void * call-frame thunk per backend file under tools/simdDispatch/kernels/, following the naming convention sd_<operation>_<backend>();
  3. one SdKernelDef row in each backend file that implements the operation;
  4. the .Call wrapper in src/r_api.c, which marshals R objects into the operation call frame and calls sd_dispatch_invoke();
  5. the native registration entry in src/registration.c;
  6. the R wrapper, tests, and documentation.

Backend metadata and kernel availability are table-driven, similar in spirit to R’s native routine registration tables. Operation implementation ownership stays close to each backend source file, while tools/simdDispatch/simd_dispatch.c consumes only opaque operation IDs and generic kernel-definition rows.

For packages with many public operations, keep a single operation catalog that can generate the repetitive dispatch metadata, .Call registration, and R wrappers. Optimized backend kernels can remain hand-written, but operation names, argument lists, and exported wrapper metadata should not be duplicated by hand.

Build

The generated configure checks compiler support, writes src/config.h plus src/Makevars, and stages selected kernel objects in src/rsd-kernels/. Runtime CPU support is checked later by the installed package:

R CMD INSTALL .