Accelerators (device families)#
NEML2 targets any device family torch itself supports. Today that means CPU,
CUDA, XPU (Intel GPU), HIP (AMD GPU via native torch.hip), and MPS (Apple
Silicon). The compile CLI, the on-disk artifact layout, the Python shim, and
the C++ AOTI loader + dispatcher all speak the same lowercase family names
(cpu, cuda, xpu, hip, mps) — the runtime is device-opaque, so adding
a new family is a small change at a handful of choke points.
Note
CPU and CUDA are the two families NEML2 exercises end-to-end in CI. XPU, HIP (native), and MPS are wired and unit-tested at the string-parsing and metadata boundaries, but not yet validated on the hardware they target. If you run on one of those, please open an issue with the outcome — the code paths share the same generic runtime, so parity is expected, but real-hardware confirmation is welcome.
Accepted --device values#
neml2-compile --device and neml2-run --device accept any of:
Family |
Meaning |
|---|---|
|
Host CPU (always available) |
|
NVIDIA GPU via CUDA |
|
Intel GPU via |
|
AMD GPU via native |
|
Apple Silicon via |
neml2-compile --device takes bare family names (one artifact per family);
pass more than one to build side-by-side artifacts (--device cpu cuda xpu).
neml2-run --device additionally accepts indexed forms (cuda:1, xpu:0)
because it sets torch.set_default_device and a specific index is meaningful.
Note
ROCm-built PyTorch aliases torch.cuda to HIP. If you installed
PyTorch from the ROCm wheel index, your AMD GPU is addressed as "cuda" (not
"hip") and works under the existing CUDA path with no changes. "hip" in
the accepted list is for the newer, distinct torch.hip / at::kHIP
surface.
Per-family requirements#
Each family needs the matching torch build. neml2-compile runs a per-family
preflight (neml2._accelerator’s check_toolchain) as soon as example
inputs on that device reach the compile step; if the toolchain is missing you
get a recipe rather than a cryptic Inductor error deep in the graph.
CUDA. Inductor’s CUDA backend needs nvcc at compile time (the default
torch wheel ships the CUDA runtime but not the compiler). Install
nvidia-cuda-nvcc (pip) or the system CUDA toolkit; the error message includes
the exact recipe.
XPU. Install the XPU-enabled torch build:
pip install torch --index-url https://download.pytorch.org/whl/xpu
Verify with python -c 'import torch; print(torch.xpu.is_available())'. NEML2’s
current XPU toolchain check only confirms runtime availability; if you hit a
compiler error inside Inductor’s XPU codegen, please open an issue with the
message so we can tighten the preflight.
HIP. Requires a torch build with native torch.hip. If you have ROCm
PyTorch (which routes torch.cuda to HIP), just use --device cuda.
MPS. Available on Apple Silicon with a stock torch build. No separate compiler is needed.
On-disk layout#
neml2-compile writes one subfolder per targeted family under the artifact
root, named by the lowercase family string:
<output-dir>/<name>/
metadata.json
cpu/float64/ *.pt2
cuda/float64/ *.pt2
xpu/float64/ *.pt2
The C++ loader (neml2::aoti::Model, neml2::aoti::DispatchedModel) picks the
folder from at::Device(dev).type() at load time, so the Python writer and the
C++ reader always agree on the spelling.
Dispatcher: mixing families#
neml2::aoti::DispatchedModel accepts any mix of families in its WorkScheduler
configuration — the scheduler chunks a batched call across the pool. The
per-family rules (from MPISimpleScheduler and StaticHybridScheduler):
At most one CPU worker (concurrent CPU workers would oversubscribe the intra-op thread pool).
Within each accelerator family, indices must be unique; a bare family entry (e.g.
cuda) cannot mix with pinned ones (cuda:0,cuda:1) from the same family, because bare could alias one of the pinned devices.Different families are independent —
{"cpu", "cuda:0", "xpu:0"}is legal.
See cpp-dispatch — dispatching across devices for the full scheduler surface.
Adding a new accelerator family#
Every new torch accelerator (MTIA, PrivateUse1-backed hardware, etc.) is a one-line extension at two places:
Python: append the family name to
KNOWN_FAMILIESinneml2._acceleratorand add aTOOLCHAIN_CHECKSentry (atorch.<x>.is_available()probe is enough to start).C++: add the enum to
is_accelerator()inneml2/csrc/aoti/DeviceLayout.h.c10::DeviceTypeName(..., true)already knows how to spell every torch-known device type, so the folder-name mapping needs no change.
No changes are needed in the loader, ops, solve, jacobian, or dispatcher code —
they route at::Device opaquely.