Skip to content

Add an intra_threads option to Ortex.load - #51

Open
pshoukry wants to merge 2 commits into
elixir-nx:mainfrom
pshoukry:session-thread-count
Open

pshoukry wants to merge 2 commits into
elixir-nx:mainfrom
pshoukry:session-thread-count

Conversation

@pshoukry

@pshoukry pshoukry commented Sep 19, 2026

Copy link
Copy Markdown

Adds an intra_threads option to Ortex.load/4, passed to ort's
SessionBuilder::with_intra_threads.

Ortex.load("./models/tinymodel.onnx", [:cpu], 3, intra_threads: 2)

Why

ONNX Runtime gives a session one intra-op thread per physical core, so one
model stream can take most of a machine and starve a second model. Ortex has
no way to change that. ORT_INTRA_OP_NUM_THREADS works only from the OS
environment at process start and applies to every session in the VM.

One stream, RF-DETR at 576x576, CPU provider, ONNX Runtime 1.30.0, 15-core
Apple M5 Pro:

intra_threads inferences/s median ms cores busy
1 5.3 182.3 1.06
4 9.2 106.7 4.04
5 10.4 94.9 5.08
7 (default here) 9.4 85.8 6.37

Past 5 threads latency still falls, but each inference costs more CPU.

Relation to #50

#50 passes session config entries through. On ONNX Runtime 1.28+ the key
session.intra_op_num_threads sizes the pool. On the 1.19.2 runtime Ortex
bundles, that key does not exist and is silently ignored. This PR calls
with_intra_threads directly, so it works on every runtime Ortex links;
checked by counting OS threads (intra_threads: 1 adds 0 workers, 6 adds 5).

Both PRs take the fourth argument of load/4. If #50 lands first I can rebase
this as one more key in the same keyword list.

Details

  • nil, the default, leaves the builder untouched; existing callers are
    unchanged.
  • A positive integer or nil, else ArgumentError. Keyword.validate!/2
    refuses an unknown option.
  • No new dependency; Cargo.lock unchanged.
  • Tests cover a capped session running, the option being optional, bad values,
    and a misspelt key.

ONNX Runtime sizes a session's intra-op thread pool at one thread per
physical core unless told otherwise, and keeps every thread busy while
the session runs. One model stream can then occupy most of a machine,
which leaves nothing for a second model or for the rest of the program.

ort exposes SessionBuilder::with_intra_threads; this passes it through
as an optional fourth argument to Ortex.load/4:

    Ortex.load(path, [:cpu], 3, intra_threads: 4)

Unset, the session is built exactly as before, so ONNX Runtime's own
default and its ORT_INTRA_OP_NUM_THREADS environment fallback still
apply. A value that is not a positive integer, or an unknown option,
raises ArgumentError before the NIF is called.
Matching the whole validated keyword list meant a second option would break
every clause. Read the key out of the validated options instead.
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.

1 participant