CPU (nd4j-native)
Setting up the nd4j-native CPU backend — Maven dependencies, AVX2/AVX512 optimizations, OpenBLAS, MKL, and multi-threading
nd4j-native is the CPU backend for ND4J. It executes all array operations through libnd4j, a C++ compute engine that calls into BLAS routines and uses SIMD vector instructions where available. This page covers Maven setup, AVX optimization classifiers, BLAS configuration, threading controls, and memory tuning.
Maven Dependencies
Recommended: -platform artifact
The simplest setup uses the -platform artifact, which bundles natives for all supported operating systems and CPU architectures in one dependency block:
<properties>
<dl4j.version>1.0.0-rewrite</dl4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.deeplearning4j</groupId>
<artifactId>nd4j-native-platform</artifactId>
<version>${dl4j.version}</version>
</dependency>
</dependencies>This is appropriate for most projects. Maven resolves the native JARs for Linux x86_64, Windows x86_64, macOS x86_64, macOS ARM64, Linux ARM64, and Linux ppc64le simultaneously. At runtime JavaCPP extracts only the library matching the current OS and CPU.
Minimal: current-platform only
To reduce JAR size for a known deployment target, omit -platform and add an explicit classifier:
Using the DL4J BOM
If you are using the broader DL4J stack (DataVec, DL4J training layers, etc.), import the BOM to keep all version numbers consistent:
With the BOM the <version> tag on individual dependencies is optional — the BOM pins them.
Platform Classifiers
Available native classifiers for nd4j-native in M2.1:
linux-x86_64
Linux
x86-64
Baseline SSE4.1
linux-x86_64-avx2
Linux
x86-64
AVX2 SIMD (Haswell+)
linux-x86_64-avx512
Linux
x86-64
AVX-512 (Skylake-X+, Ice Lake+)
linux-arm64
Linux
AArch64
AWS Graviton, Raspberry Pi 64-bit
linux-ppc64le
Linux
POWER8/9
IBM Power Systems
windows-x86_64
Windows
x86-64
Baseline SSE4.1
windows-x86_64-avx2
Windows
x86-64
AVX2 SIMD
macosx-x86_64
macOS
x86-64
Intel Mac
macosx-arm64
macOS
AArch64
Apple Silicon (M1/M2/M3)
-compile classifiers (1.0.0-rewrite)
The 1.0.0-rewrite release adds -compile variants for each platform. These bundle the DSP JIT compilation stack (Triton, MLIR) into the native binary, enabling kernel fusion and JIT-compiled execution. The base classifiers above run standard ops and CUDA graph capture/replay but do not include JIT fusion.
linux-x86_64-compile
Linux
x86-64
Triton + MLIR + oneDNN
linux-arm64-compile
Linux
AArch64
MLIR
macosx-arm64-compile
macOS
AArch64
MLIR + MLX
android-arm64-compile
Android
AArch64
MLIR
android-arm64-compile-nnapi
Android
AArch64
MLIR + NNAPI
Trade-off: -compile classifiers produce a larger binary with more native dependencies (LLVM/Triton), but enable the full DSP JIT pipeline for maximum performance. The base classifiers are smaller and simpler to deploy. See Hardware Backends — Classifier Variants for the complete trade-off guide.
When using -platform, Maven pulls all of these classifiers. When specifying one manually, pick the classifier that matches your deployment target.
AVX2 and AVX-512 Optimizations
libnd4j is compiled in multiple variants corresponding to SIMD instruction sets. The -avx2 and -avx512 classifier variants contain a libnd4j compiled with those instruction sets enabled, delivering significant throughput improvements for element-wise and reduction operations compared to the baseline SSE4.1 build.
Selecting AVX via Maven classifier
Replace the plain classifier with the AVX-enabled one:
Selecting AVX via system property (with -platform)
When using the -platform artifact all classifier variants are on the classpath. JavaCPP picks the correct native at startup, but you can steer it toward the AVX variant with:
or
Set this property on the JVM command line before the application starts. The value is appended to the detected platform string (e.g., linux-x86_64) to form linux-x86_64-avx2, and JavaCPP loads that native library instead of the baseline one.
Verification: check that the AVX library was loaded:
If the property is set but the classifier is not on the classpath, JavaCPP falls back to the baseline library and logs a warning.
Which AVX level to use
Baseline (SSE4.1)
Any x86-64 since ~2007
—
AVX2
Intel Haswell (2013), AMD Ryzen (2017)
1.5–2× for float ops
AVX-512
Intel Skylake-X (2017), Ice Lake (2019)
2–4× for float ops
Run lscpu | grep -o 'avx[^ ]*' on Linux to check which instruction sets your CPU supports. Do not specify -avx512 on a machine that lacks AVX-512 — the process will crash with SIGILL.
BLAS Libraries
BLAS (Basic Linear Algebra Subprograms) is used for matrix multiplication (mmul), dot products, and similar level-3 operations. ND4J ships with OpenBLAS bundled inside libnd4j and uses it by default.
OpenBLAS (bundled, default)
No additional configuration is required. OpenBLAS is statically linked into libnd4j. It auto-detects the number of physical CPU cores and sets its internal thread count accordingly.
Intel MKL (optional, higher performance)
On Intel hardware, Intel MKL typically delivers 10–40% higher throughput for matrix operations compared to OpenBLAS. MKL is not bundled with ND4J but can be provided on the LD_LIBRARY_PATH and activated automatically.
Steps to use MKL:
Install Intel oneAPI Math Kernel Library (MKL). The free
intel-mklpackage is available via the Intel apt/yum repository or themklconda package.Add the MKL library path to
LD_LIBRARY_PATHbefore launching the JVM:
Tell ND4J to prefer MKL by setting the BLAS library name:
Verify MKL is in use at runtime:
If MKL is not found on LD_LIBRARY_PATH, libnd4j silently falls back to the bundled OpenBLAS.
Disabling multi-threaded BLAS
Some workloads perform many small matrix multiplications in parallel (e.g., batched inference on a thread pool). In this case it is more efficient to use single-threaded BLAS and let your own thread pool provide parallelism:
or equivalently:
Thread Configuration
OMP_NUM_THREADS
libnd4j uses OpenMP for parallelizing element-wise operations and reductions. By default it uses all available logical CPUs. Set OMP_NUM_THREADS to restrict it:
Keeping OMP_NUM_THREADS at or below the number of physical CPU cores (not hyperthreads) usually yields better sustained throughput.
Nd4j.setNumThreads()
Thread count can also be set programmatically at any point before or during computation:
The programmatic setting overrides the OMP_NUM_THREADS environment variable.
Thread recommendations by workload
Single large matmul or FFT
Use all physical cores (OMP_NUM_THREADS = core count)
Many small ops in a Java thread pool
Set OMP_NUM_THREADS=1, parallelize at the Java level
Training a neural network (DL4J)
Default (all cores); DL4J manages its own parallelism
Inference server under load
Tune empirically; often OMP_NUM_THREADS=2 or 4
Memory Configuration
ND4J uses off-heap memory (native C heap) for array data, managed by JavaCPP. The following JVM system properties control the memory limits.
Maximum off-heap allocation
This caps the total off-heap memory JavaCPP will allocate before triggering garbage collection to reclaim unused native arrays. Values accept k, m, g suffixes. Default is unlimited (or a JVM-version-specific default).
Maximum physical bytes
Sets a hard ceiling on the combined Java heap + off-heap usage. If the process exceeds this limit, JavaCPP raises OutOfMemoryError. Set this slightly below the machine's available RAM to prevent swapping.
Typical production invocation
Here the Java heap is capped at 4 GB and the ND4J off-heap pool is allowed up to 8 GB, for a combined maximum of 12 GB of physical memory.
Workspace-based memory management
For training loops and repeated inference, enable ND4J workspaces to reuse off-heap buffers without waiting for garbage collection. See Memory and Workspaces for the full guide.
Verifying the CPU Backend Is Active
If initialization fails, the most common causes are:
No
nd4j-nativeornd4j-native-platformon the classpath. Add the dependency.The AVX classifier was specified but the CPU does not support it. Use the baseline classifier or check
lscpu.A conflicting native library (
libopenblas.so) onLD_LIBRARY_PATHthat mismatches the bundled version. Remove or reorderLD_LIBRARY_PATH.
Quick Reference
Add CPU backend (all platforms)
org.nd4j:nd4j-native-platform:1.0.0-rewrite
Add CPU backend (Linux x86_64 only)
org.nd4j:nd4j-native:1.0.0-rewrite:linux-x86_64
Enable AVX2 via system property
-Djavacpp.platform.extension=-avx2
Enable AVX-512 via system property
-Djavacpp.platform.extension=-avx512
Set OpenMP thread count
export OMP_NUM_THREADS=N or Nd4j.setNumThreads(N)
Limit off-heap memory
-Dorg.bytedeco.javacpp.maxbytes=Ng
Limit total physical memory
-Dorg.bytedeco.javacpp.maxphysicalbytes=Ng
Use Intel MKL instead of OpenBLAS
-Dorg.bytedeco.openblas.load=mkl_rt
Check active backend
Nd4j.getBackend().getClass().getName()
See Also
Backends Overview — SPI mechanism, backend discovery, classpath rules
CUDA Backend (nd4j-cuda) — GPU setup, multi-GPU, VRAM management
Memory and Workspaces — off-heap memory, workspace scopes
Last updated
Was this helpful?