Skip to content

feat(rust): select the ONNX Runtime execution provider (CUDA) with a fallback policy and diagnostics - #25

Open
seo-rii wants to merge 2 commits into
upstream/pr-46-session-threadsfrom
feat/rust-execution-providers
Open

seo-rii wants to merge 2 commits into
upstream/pr-46-session-threadsfrom
feat/rust-execution-providers

Conversation

@seo-rii

@seo-rii seo-rii commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Stacked on adobe#46 ("let callers set ONNX Runtime session threads", by another contributor). Its commit is mirrored here as upstream/pr-46-session-threads, so review only db4470a. This extends adobe#46's SessionOptions instead of adding a parallel options type, and does not duplicate the thread API.

Problem

The crate always runs on ONNX Runtime's CPU provider. There was no way to request a GPU, to choose what happens when the GPU can't be used, or to find out which provider actually ran.

Change

  • SessionOptions gains two fields; it is still Copy/Eq, and defaults are unchanged:
    • execution_provider: ExecutionProvider, default Cpu;
    • fallback: ProviderFallback, default Error.
  • ExecutionProvider { Cpu, Cuda { device_id } } is #[non_exhaustive] and implements Display.
    • The CUDA path needs the new opt-in cargo feature cuda = ["ort/cuda"].
    • Without the feature, requesting Cuda fails when the model loads, with a message that names the feature.
  • ProviderFallback { Error, Cpu } chooses between failing and falling back to the CPU when the provider can't be registered.
  • Diagnostics: Trustmark::encoder_provider() / decoder_provider() return ProviderInfo { requested, registered, fallback_reason }.
  • New error: Error::ExecutionProvider { provider, source }.
    • If building the session with the provider fails, the model is retried on the CPU before the provider is blamed. A missing model file therefore still reports Error::Ort.
  • Documented limit: registered means the provider was registered. It does not prove every node runs on it; ORT only logs node placement. The README explains how to see it with ORT_LOG=verbose and a tracing subscriber.
  • The session mutexes and the default behaviour are unchanged.
  • CoreML and DirectML are not added because they can't be tested here; non_exhaustive leaves room for them.

GPU verification (NVIDIA A100 80GB, driver 580.105.08)

  • Setup: CUDA 12.9 and cuDNN 9.27 runtime libraries came from the nvidia-*-cu12 wheels via LD_LIBRARY_PATH. The binaries were built with --features cuda (ORT 1.22, CUDA 12 build).
  • Tests: 31/31 pass with --include-ignored.
  • ORT verbose log, for both models: "Successfully registered CUDAExecutionProvider" and "All nodes placed on [CUDAExecutionProvider]" (52 encoder nodes, 125 decoder nodes); MemcpyTransformer modified 0 nodes.
  • nvidia-smi: the bench process held 778 MiB in 45 of 48 samples.
  • Bad device id: rejected when the provider is registered ("Invalid device ID: 9").

Timings (median of 20 warm runs, Q/BCH_5, same host; CPU with 8 intra-op threads):

Image Step CUDA CPU
ghost 512×512 encode 26.7 ms 88.2 ms
ghost 512×512 decode 5.0 ms 47.5 ms
ripley 4032×2268 encode 877.5 ms 932.3 ms
ripley 4032×2268 decode 36.9 ms 74.3 ms
  • Loading took 442 ms on CUDA and 290 ms on CPU.
  • The first CUDA encode took 1106 ms, from CUDA start-up and cuDNN's algorithm search.
  • Encoding large images is dominated by image processing on the CPU; a separate PR targets that.

Deployment note (added to the README): a standalone binary does not find libonnxruntime_providers_shared.so next to itself. It must be on LD_LIBRARY_PATH; cargo run and cargo test set this up automatically.

Local checks

  • cargo fmt --check passes.
  • clippy with -D warnings passes, both with default features and with --all-features.
  • cargo test --workspace: 29 lib tests plus 2 doctests pass.
  • --features cuda on a machine without a GPU: 28 pass and the 3 GPU tests are ignored. The fallback test hit a real registration failure (missing libcublasLt.so.12) and fell back as expected.
  • cargo doc and cargo publish --dry-run --locked (default features) pass, and so does the MSRV 1.88 check, also with --features cuda --lib.

Compatibility

  • The new Error variant breaks exhaustive matches downstream, because Error is not #[non_exhaustive]. The next release should therefore be 0.5.0.
  • CI's clippy step uses --all-features, so it will now also download the ~170 MB CUDA build of ORT.
  • Only Linux x86-64 was tested.

🤖 Generated with Claude Code

Trustmark always ran both sessions on ONNX Runtime's CPU provider, so a
host with an NVIDIA GPU could not use it without forking the crate.

Extend `SessionOptions` with `execution_provider` (`ExecutionProvider`,
default `Cpu`) and `fallback` (`ProviderFallback`, default `Error`). The
new opt-in `cuda` feature enables `ort/cuda`, which downloads ONNX
Runtime 1.22 built for CUDA 12, and lets `ExecutionProvider::Cuda {
device_id }` register the CUDA provider. If the requested provider can't
be used (feature missing, CUDA/cuDNN libraries missing, bad device, or
session creation failing with it), `ProviderFallback::Error` returns the
new `Error::ExecutionProvider` and `ProviderFallback::Cpu` loads the
model on the CPU instead. A model that fails to load on the CPU as well
is reported as the usual `Error::Ort`, not blamed on the provider.

`Trustmark::encoder_provider()` and `decoder_provider()` return a
`ProviderInfo` with the requested provider, the one the session was
created with, and the fallback reason. ONNX Runtime does not expose
per-node placement through its API, so this does not claim every node
runs on the provider; the docs point to `ORT_LOG=verbose` for that.

`Trustmark::new` and `SessionOptions::default()` keep their behaviour
(CPU, 8 intra-op threads), and the session Mutexes are unchanged. The
session construction moves to a new `session` module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 21, 2026 04:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants