knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library(glmbayes)
The glmbayes package constructs envelopes for accept–reject sampling by evaluating the negative log posterior (f2) and its gradient (f3) at each point of a tangency grid. The grid size grows with dimension (e.g., (3^\ell) points for (\ell) coefficients), and each grid point can be evaluated independently. These evaluations are embarrassingly parallel and are a natural target for GPU acceleration.
The optional OpenCL implementation accelerates these evaluations by running them on a GPU (or OpenCL-capable CPU). OpenCL is vendor-neutral and works across NVIDIA, AMD, and Intel hardware. When OpenCL is available and use_opencl = TRUE, the package uses GPU-accelerated evaluation inside EnvelopeEval, which is called by EnvelopeBuild and EnvelopeDispersionBuild. If OpenCL is not available or use_opencl = FALSE, the CPU path (f2_f3_non_opencl) is used instead. The package runs correctly in either case.
This chapter describes the OpenCL implementation: the two-layer design (kernel wrapper and kernel runner), how the OpenCL program is assembled from ported nmath/rmath functions, and how the pilot and safeguards integrate with the envelope build. For installation and availability checks, see Chapter 16.
The OpenCL path consists of two layers:
| Layer | Function | Location | Role |
|-------|----------|----------|------|
| Wrapper | f2_f3_opencl | kernel_wrappers.cpp | Flatten R inputs, select kernel, load sources, call runner, reshape outputs |
| Runner | f2_f3_kernel_runner | kernel_runners.cpp | Platform/device setup, buffer creation, kernel launch, readback |
EnvelopeEval (EnvelopeEval.cpp)
|-- if use_opencl && pilot/check passes:
| f2_f3_opencl() -> f2_f3_kernel_runner()
\-- else:
f2_f3_non_opencl() -> CPU famfuncs (f2_f3_binomial_logit, etc.)
EnvelopeEval receives the grid G4 (coefficients at each tangency point), the design matrix, prior parameters, and family/link. It chooses the GPU or CPU path based on use_opencl and the outcome of the pilot (when applicable). The GPU path calls f2_f3_opencl, which assembles the OpenCL program and invokes f2_f3_kernel_runner; the CPU path calls f2_f3_non_opencl, which dispatches to the appropriate C++ family function.
OpenCL kernels are not stored as single monolithic files. Instead, the package builds the program by concatenating several source components in a fixed order. This mirrors a C/C++ build where headers and libraries are included before the main source.
Runtime GPU assembly uses load_likelihood_subgradient_program in src/kernel_loader.cpp (see inst/cl/README.md). Prelude, shims, and selective nmath/ are loaded from nmathopencl; entry kernels from glmbayes (inst/cl/src/f2_f3_*.cl).
For interactive exploration, mirror that split with opencltools:
all_src = opencltools::load_kernel_source("OPENCL.cl", package = "nmathopencl")
+ opencltools::load_kernel_library("libR_shims", package = "nmathopencl")
+ … (R_ext_types, R_shims, R_ext_runtime, R_ext_internals, System)
+ opencltools::load_library_for_kernel(
system.file("cl/src/f2_f3_binomial_logit.cl", package = "glmbayes"),
system.file("cl/nmath", package = "nmathopencl"),
depends_tag = "all_depends_nmath")
+ opencltools::load_kernel_source("src/f2_f3_binomial_logit.cl", package = "glmbayes")
@all_depends_nmath on the entry kernel; files from nmathopencl inst/cl/nmath/.inst/cl/src/.opencltools::load_kernel_source(relative_path, package = …) – Loads a single .cl file via system.file("cl", …).opencltools::load_kernel_library(subdir, package = …) – Loads all .cl files in a subdirectory with @provides / @depends topological sort.opencltools::load_library_for_kernel() – Minimal nmath subset from @all_depends_nmath tags (see ?opencltools::load_library_for_kernel).The @provides and @depends tags allow modular, maintainable kernel code. See ?opencltools::load_kernel_library for details.
The likelihood and gradient computations require statistical functions that match R's behavior. Because OpenCL C does not include these, the package ports a core set from R's nmath and rmath libraries to OpenCL C.
| Module | Provides | Purpose | |--------|----------|---------| | nmath.cl | ML_, ME_, ISNAN, R_FINITE, ML_ERROR, etc. | Constants, error handling, validation macros | | log1p.cl | Rlog1p | Log(1+x) for numerical stability | | expm1.cl | expm1 | exp(x)-1 for numerical stability | | bd0.cl | bd0, ebd0, etc. | Poisson/binomial deviance terms | | stirlerr.cl | stirlerr | Stirling error for factorial terms | | lgamma.cl | lgammafn | Log-gamma | | gamma.cl | gammafn | Gamma function | | dbinom.cl | dbinom, dbinom_raw | Binomial density | | dpois.cl | dpois, dpois_raw | Poisson density | | dgamma.cl | dgamma | Gamma density | | dnorm.cl | dnorm4 | Normal density | | pnorm.cl | pnorm5, pnorm_both | Normal CDF (probit) | | pgamma.cl | pgamma | Gamma CDF (inverse link) | | d1mach.cl | d1mach | Machine constants | | chebyshev.cl | chebyshev_eval | Polynomial evaluation |
Rmath.cl provides additional constants (M_E, M_PI, M_LN2, etc.) and distribution functions consistent with R's Rmath library.
dpq.cl and dpq_prelude.cl provide macros for density and CDF handling with give_log and lower_tail, matching R's DPQ (density, probability, quantile) conventions.
These ports ensure that the OpenCL kernels produce results numerically consistent with the CPU path and with R itself.
Each supported family/link has a dedicated kernel file that implements the f2/f3 logic for that model.
| Family | Link | Kernel file |
|--------|------|-------------|
| binomial | logit | f2_f3_binomial_logit.cl |
| binomial | probit | f2_f3_binomial_probit.cl |
| binomial | cloglog | f2_f3_binomial_cloglog.cl |
| poisson | log | f2_f3_poisson.cl |
| Gamma | inverse | f2_f3_gamma.cl |
| gaussian | identity | f2_f3_gaussian.cl |
Each kernel follows the same pattern:
int j = get_global_id(0); one work item per grid point (j).qf[j].dbinom_raw, dpois_raw, lgamma, pnorm5, etc.) as needed.qf[j] = negative log posterior; grad[k*m1 + j] = gradient (column-major layout).The kernels use a fixed MAX_L2 for local arrays (e.g., 64); this limits the number of coefficients when using OpenCL. See the source for current limits.
The wrapper f2_f3_opencl in kernel_wrappers.cpp performs:
(family, link) to kernel_name and kernel_file (e.g., binomial + logit → f2_f3_binomial_logit, src/f2_f3_binomial_logit.cl).all_src.f2_f3_kernel_runner(all_src, kernel_name, l1, l2, m1, ...) with flattened inputs and output buffers.qf_flat into an R vector; wrap grad_flat as an Armadillo matrix (m1 × l2) for return.The returned list has components qf (negative log posterior per grid point) and grad (gradient matrix).
The runner in kernel_runners.cpp handles the low-level OpenCL API:
clGetPlatformIDs, clGetDeviceIDs (CL_DEVICE_TYPE_DEFAULT).clCreateContext, clCreateCommandQueueWithProperties.clCreateProgramWithSource with the concatenated string, clBuildProgram.clCreateKernel with the kernel name.clEnqueueNDRangeKernel with global = m1 (one work item per grid point).clEnqueueReadBuffer for qf and grad.The runner is GLM-specific and lives in the glmbayes::opencl namespace; the general OpenCL utilities (kernel loading, device enumeration) are in openclPort.
For large grids (e.g., (m_1 > 50{,}000)), EnvelopeEval runs f2_f3_opencl_pilot before the full evaluation to estimate runtime.
f2_f3_opencl on a small grid slice.refined_est_total_sec = per_grid_sec × m1.refined_est_total_sec > 300:"Do you want to continue? [y/N]: ".This mirrors the parallel sampling pilots in Chapter A09. The envelope build phase cannot be interrupted once the full OpenCL run starts, so the pilot gives users a chance to decline long runs.
The pilot runs when m1_total > 50000 and use_opencl is true. For smaller grids, the full evaluation runs without a pilot.
OpenCL support is optional. The package compiles and runs without it; in that case, use_opencl is ignored and the CPU path is always used.
To enable OpenCL:
Use has_opencl() to check availability and diagnose_glmbayes() for a full diagnostic report. See Chapter 16 for platform-specific installation instructions (Windows, Linux, macOS) and AMD ROCm notes.
has_opencl, diagnose_glmbayes?EnvelopeBuild, ?EnvelopeEval – use_opencl argument?opencltools::load_kernel_library, ?opencltools::load_kernel_source – Program assembly?glmb, ?rglmb – use_opencl argumentAny scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.