Skip to content

Backends

All model execution — the CRAFT detector and every gen2 recognizer — goes through one trait: inference::ModelBackend. The detection, recognition, and engine code call this trait only; they never call ort, tract, or candle APIs directly. config, types, and the geometry code stay entirely backend-agnostic, so a config or result type is never conditionally compiled per backend.

This keeps the backend choice non-load-bearing: a new backend implements the trait and slots in without touching the pipeline. See ADR 0004.

Backend Feature Target Notes
ort ort, ort-bundled, ort-dynamic Desktop / server Native ONNX Runtime, and the fastest by a wide margin. ort-bundled fetches a prebuilt runtime at build time (zero-config, and the CLI default); ort-dynamic dlopens libonnxruntime at runtime via ORT_DYLIB_PATH, which is the path on targets with no prebuilt.
tract tract WASM / Android, and targets with no prebuilt ONNX Runtime Pure-Rust ONNX interpreter — no native library to ship or load. CPU-only.
candle candle, candle-metal, candle-cuda GPU, or a build with no ONNX Runtime Does not interpret the ONNX graph: it runs hand-written CRAFT and CRNN networks over the weights read from the same ONNX files. Runs on the CPU, Apple Metal, or CUDA.

Pick ort for CPU/RSS-optimized execution on desktop or server; pick tract when you need a pure-Rust build with no native dependency — WASM, Android, or any target ONNX Runtime ships no prebuilt for; pick candle when you want a GPU without provisioning a provider-specific ONNX Runtime, or a native build with no ONNX Runtime at all. See ADR 0012 for the ort-bundled/ort-dynamic provisioning tradeoff, ADR 0029 for why the CLI defaults to ort-bundled and how the accelerator is selected, and ADR 0031 for how candle runs these models and what it costs.

The active backend is selected through ModelConfig::backend (Backend::Ort / Backend::Tract / Backend::Candle), or --backend on the CLI.

ModelConfig::accelerator (--accelerator on the CLI) chooses the hardware the backend runs the graph on: cpu (the default), auto, coreml, directml, metal, or cuda. The vocabulary is named for the seam rather than for ONNX Runtime, but not every backend answers every value — ort reaches hardware through ONNX Runtime execution providers while candle addresses devices directly:

Backend Accelerators Cargo feature
ort coreml, directml, cuda ort-coreml, ort-directml, ort-cuda
tract — (CPU only)
candle metal, cuda candle-metal, candle-cuda

cuda appears on both because it names hardware rather than an execution provider. coreml and metal are not interchangeable: both drive the same Apple GPU, but through different frameworks with different numerics, so each belongs to one backend. Any other pairing is rejected at config-validation time, with the equivalent on your backend named in the error. See ADR 0032.

The default is cpu rather than auto deliberately. The same ONNX graph produces different numeric output on different providers, and sceptre’s published parity and benchmark figures are CPU figures; defaulting to auto would silently change a user’s results on upgrade.

runtime_info() (and sceptre env --format json) reports the backend, the requested and the registered accelerator, the ONNX Runtime provisioning strategy and version, and the target arch — the environment any parity or benchmark artifact has to record alongside its numbers.

Parallelism flows through a single ConcurrencyConfig::max_threads budget. Rayon’s thread pool and the backend’s own intra-op threads (once wired) draw from the same budget, so nested parallelism — Rayon fanning out across images while the backend also parallelizes inside a single model call — cannot oversubscribe the CPU. max_threads defaults to num_cpus, capped at 8. See Configuration.