CUDA
jdistlib-cuda uses JCuda Driver and JNvrtc. NVRTC compiles device code without nvcc or MSVC, but a compatible NVIDIA driver, NVRTC, and nvrtc-builtins are required.
Optional acceleration · 0.8.4+
JDistlib can route large vector, dense/sparse linear-algebra, and batched likelihood work through optional CUDA, OpenCL, or Vulkan providers. Version 0.10.0 also exposes prepared operands and reusable factorizations to downstream libraries. Small operations stay on the deterministic CPU reference path by default.
Unified FP64/FP32 linear algebra is available in JDistlib 0.10.0One-file installation
The recommended jdistlib-all.jar contains core JDistlib, all three providers, and their Java/JNI dependencies for Windows, Linux, and macOS x86-64. The stable filename always resolves to the newest GitHub release and runs on CPU when no compatible GPU runtime is present.
Selection contract
| Compute choice | Behavior | If unavailable |
|---|---|---|
Compute.AUTO | Discover available providers and route eligible large operations according to their reported capabilities. | Use CPU and record the selection. |
Compute.CPU | Use the deterministic CPU reference implementation. | Always available. |
Compute.ONEMKL | Require the system Intel oneMKL CBLAS/LAPACKE runtime. | Fail immediately. |
Compute.OPENBLAS | Require the system OpenBLAS CBLAS/LAPACKE runtime. | Fail immediately. |
Compute.GPU | Require any supported GPU provider. | Fail immediately; never fall back. |
Compute.CUDA | Require CUDA specifically. | Fail immediately. |
Compute.OPENCL | Require OpenCL specifically. | Fail immediately. |
Compute.VULKAN | Require the LWJGL Vulkan FP64 compute provider. | Fail immediately. |
The current automatic router uses conservative JVM-heap thresholds: 32,768 vector elements and approximately one million multiply/evaluation terms for matrix multiplication or logistic likelihood batches. These are implementation defaults, not performance promises. Prepared inputs remain resident on the selected device, which can make regular batched workflows profitable sooner.
Reusable numerical engine · 0.10.0
The matching FP64/FP32 contracts cover dense BLAS, CSR products and triangular solves, batched work, and Cholesky, LU, LDL', QR, ordinary/generalized symmetric eigen, and thin-SVD decompositions. Prepared dense handles keep a fixed operand in native CPU or provider device storage; prepared sparse Cholesky separates symbolic analysis from repeated numeric refactorization.
Capability records and execution plans distinguish provider-native work from the portable Java fallback. This makes the same API usable on a CPU-only machine without claiming that every decomposition is a native GPU kernel. See the linear-algebra overview or the complete contract.
Programmatic Java API
SamplingOptions follows the existing builder pattern. The shorter backend method is an alias for computeBackend.
SamplingOptions options = SamplingOptions.builder()
.warmupIterations(1000)
.sampleIterations(2000)
.backend(Compute.AUTO)
.nutsBackend(ComputeNuts.AUTO)
.build();
try (AcceleratedLogisticRegression target =
AcceleratedLogisticRegression.forNuts(
options, design, outcomes, priorPrecision)) {
Fit fit = Inference.fit(
new NoUTurnSampler(), target, initialStates,
options, 20260828L, 4);
System.out.println(fit.manifest().computeBackend());
System.out.println(fit.manifest().computeDevice());
}
To require CUDA target evaluation for NUTS, replace the two policy lines:
.backend(Compute.CUDA)
.nutsBackend(ComputeNuts.FORCE)
A strict policy throws if the provider is missing, the device lacks FP64, or the target is not accelerator-aware. It never quietly changes the request to CPU.
try (ComputeSelection selection =
ComputeBackends.select(Compute.AUTO)) {
ComputeBackend compute = selection.backend();
double dot = compute.dot(x, y);
double[][] product = compute.matrixMultiply(a, b);
System.out.println(selection.deviceInfo().description());
System.out.println(selection.plan(LinearAlgebraOperation.GEMM,
NumericPrecision.FP64, rows, columns, shared).description());
try (PreparedTransposeProduct scores =
compute.prepareTransposeProduct(design)) {
double[][] xtResidual = scores.multiply(residualBatches);
}
}
The optional modules implement double-precision unary vector functions, AXPY, dot products, dense matrix multiplication, and batched logistic likelihoods and gradients. CUDA and OpenCL also keep a prepared matrix resident for repeated transpose products such as high-dimensional residual scores; Vulkan uses the CPU fallback for this primitive. The modular core artifact remains native-free.
Command-line applications
Applications embedding JDistlib can pass their argument array through InferenceCliOptions. Unknown arguments are returned to the host application.
InferenceCliOptions cli = InferenceCliOptions.parse(args);
SamplingOptions options = cli.applyTo(
SamplingOptions.builder()).build();
String[] applicationArguments = cli.remainingArguments();
| Switch | Meaning |
|---|---|
--compute=auto|cpu|onemkl|openblas|gpu|cuda|opencl|vulkan | Select automatic routing, Java CPU, a native CPU runtime, any required GPU, or an exact GPU provider. |
--nuts-offload=off|auto|force | Disable, automatically choose, or require accelerated NUTS target evaluation. |
--gpu-nuts | Convenience alias for --compute=gpu --nuts-offload=force. |
The equivalent JVM properties work even when an application does not expose those switches:
java -Djdistlib.compute.backend=cuda \
-Djdistlib.compute.nuts=force \
-cp "..." your.application.Main
Backend selection is emitted once through java.util.logging. Command-line applications normally show it on the console, for example:
JDistlib compute: policy=auto, backend=cuda,
device=NVIDIA GeForce RTX 2080 (sm_75), thresholded CPU fallback enabled
RunManifest also retains the requested policy, concrete backend, device, and NUTS offload mode so a run does not depend on a transient console message.
NUTS boundary
NUTS tree depths differ by chain and U-turn decisions stop each tree at different times. JDistlib therefore keeps tree construction, adaptation, candidate selection, and U-turn control on CPU. An accelerator-aware target may execute its likelihood, gradient, vector math, and dense linear algebra on a GPU.
| NUTS choice | Contract |
|---|---|
ComputeNuts.OFF | Require CPU target evaluation. |
ComputeNuts.AUTO | Use the configured backend and automatic thresholds. This is the default. |
ComputeNuts.FORCE | Require a CUDA, OpenCL, or Vulkan-backed target and bypass automatic CPU thresholds. |
The whole-NUTS GPU decision remains provisional until representative end-to-end effective-samples-per-second measurements include warmup, synchronization, divergences, and transfer costs.
Measured smoke test · 2026-08-27
The repeatable CUDA smoke used an NVIDIA GeForce RTX 2080 and an 8,192-row, 32-predictor double-precision logistic regression. Times are five-trial medians after context and NVRTC compilation warmup.
| States | CPU | CUDA resident | Resident speedup | CUDA with copies | End-to-end speedup |
|---|---|---|---|---|---|
| 1 | 0.7165 ms | 0.8915 ms | 0.804× | 1.8337 ms | 0.391× |
| 4 | 2.6209 ms | 0.8043 ms | 3.259× | 1.8411 ms | 1.424× |
| 16 | 10.6519 ms | 1.4382 ms | 7.406× | 2.3092 ms | 4.613× |
| 64 | 45.6427 ms | 2.7388 ms | 16.665× | 3.6493 ms | 12.507× |
The largest CPU/CUDA likelihood or gradient difference was below 3.1e-11. These measurements establish a batching boundary on one machine, not a portable speed guarantee. Re-run gradlew :jdistlib-cuda:cudaSmokeBenchmark on the deployment hardware and review the checked-in benchmark record.
Providers and reproducibility
jdistlib-cuda uses JCuda Driver and JNvrtc. NVRTC compiles device code without nvcc or MSVC, but a compatible NVIDIA driver, NVRTC, and nvrtc-builtins are required.
jdistlib-opencl uses JOCL and selects an OpenCL GPU with FP64 support.
jdistlib-vulkan uses LWJGL Vulkan and shaderc, selects a compute queue with shaderFloat64, and compiles maintained GLSL kernels to cached SPIR-V pipelines at runtime. Version 0.10.0 can retain prepared FP64/FP32 dense operands in provider buffers. The provider remains explicit about operations that use portable fallback.
jdistlib-nativecpu dynamically loads a system oneMKL or OpenBLAS runtime through JNA. The all-in-one JAR includes the adapter, not Intel or OpenBLAS binaries. Use Compute.ONEMKL or Compute.OPENBLAS for a strict choice.
The unified JAR carries the x86-64 JNI bindings, but it cannot carry vendor drivers. CUDA still needs an NVIDIA driver plus NVRTC, OpenCL needs a vendor OpenCL implementation, and Vulkan needs a Vulkan loader/driver (MoltenVK is bundled through LWJGL on x86-64 macOS). On other architectures, use the modular artifacts so the dependency manager can select matching natives.
Caller-provided random streams remain deterministic. Parallel reductions can differ in their final floating-point bits across hardware and providers, so JDistlib does not promise bit-identical chains across CPU, CUDA, OpenCL, or Vulkan devices. Use CPU for the cross-machine reference path; for accelerated reproduction, retain the backend/device manifest and use the same software and hardware configuration.