ONNX

Overview

The smile.onnx module provides an idiomatic Java API for running ONNX models with the ONNX Runtime inference engine. It wraps the ORT C API through Panama FFM (smile.onnx.foreign); applications use only the high-level classes described here.

Shared tensor / deep-learning APIs live on Deep Learning. HTTP serving of .onnx models is covered on SMILE Serve.

┌──────────────────────────────────────┐
│           Your Application           │
├──────────────────────────────────────┤
│          smile.onnx                  │
│  InferenceSession  OrtValue  ...     │
├──────────────────────────────────────┤
│   smile.onnx.foreign  (Panama FFM)   │
├──────────────────────────────────────┤
│    onnxruntime shared library        │
└──────────────────────────────────────┘

Prerequisites

RequirementMinimum
Java22+ (Panama FFM; SMILE targets Java 25)
ONNX Runtime native library1.18+ (API version 22)
JVM flag--enable-native-access=ALL-UNNAMED

Download pre-built packages from the ORT releases page and place libonnxruntime.so / libonnxruntime.dylib / onnxruntime.dll on the OS library search path:

Bash
# Linux
export LD_LIBRARY_PATH=/path/to/onnxruntime/lib:$LD_LIBRARY_PATH

# macOS
export DYLD_LIBRARY_PATH=/path/to/onnxruntime/lib:$DYLD_LIBRARY_PATH

# Windows (PowerShell)
$env:PATH = "C:\path\to\onnxruntime\lib;" + $env:PATH

java --enable-native-access=ALL-UNNAMED -jar myapp.jar

Package Structure

Class / enumRole
InferenceSessionLoads a model and runs inference
OrtValueTensor container (input and output)
SessionOptionsConfigures a session before loading
RunOptionsConfigures a single inference run
EnvironmentShared OrtEnv (thread pools, logging)
ModelMetadataProducer, version, custom metadata
NodeInfo / TensorInfoName, type, and shape for a node
ElementType / OnnxTypeTensor element types and value kinds
GraphOptimizationLevel / ExecutionModeOptimization and execution mode enums
LoggingLevel / OnnxExceptionORT logging severity and errors

Quick Start

Java
import smile.onnx.*;
import java.util.Map;

// 1. Load the model
try (var session = InferenceSession.create("resnet50.onnx")) {

    // 2. Inspect inputs
    session.inputNames().forEach(System.out::println);
    // → "data"

    // 3. Build an input tensor  (batch=1, channels=3, height=224, width=224)
    float[] pixels = preprocessImage(...);   // your image preprocessing
    long[]  shape  = { 1, 3, 224, 224 };

    try (OrtValue input = OrtValue.fromFloatArray(pixels, shape)) {

        // 4. Run inference
        OrtValue[] outputs = session.run(Map.of("data", input));

        // 5. Read the result
        float[] scores = outputs[0].toFloatArray();
        int classId = argmax(scores);
        System.out.println("Predicted class: " + classId);

        // 6. Release output tensors
        for (OrtValue v : outputs) v.close();
    }
}

InferenceSession

InferenceSession loads an ONNX model, optimizes its graph, and executes inference. It implements AutoCloseable — always use try-with-resources or call close(). run is thread-safe; multiple threads may call it concurrently on the same session.

Java
import smile.onnx.*;
import java.nio.file.Files;
import java.nio.file.Path;

// From a file path (default options)
InferenceSession session = InferenceSession.create("model.onnx");

// From a file path with custom options
try (var opts = new SessionOptions()) {
    opts.setIntraOpNumThreads(4)
        .setGraphOptimizationLevel(GraphOptimizationLevel.ENABLE_ALL);
    session = InferenceSession.create("model.onnx", opts);
}

// From a byte array (database / JAR resource)
byte[] modelBytes = Files.readAllBytes(Path.of("model.onnx"));
session = InferenceSession.create(modelBytes);

// Run all outputs (most common)
OrtValue[] outputs = session.run(inputs);

// Selected outputs + per-call options
try (var runOpts = new RunOptions()) {
    runOpts.setLogTag("my-run");
    outputs = session.run(inputs, new String[]{"output_0"}, runOpts);
}

OrtValue — Tensors

OrtValue wraps an ORT tensor with Java-friendly factories and extractors. It implements AutoCloseable. Shape is a long[]; -1 means a dynamic dimension.

Java
import smile.onnx.OrtValue;
import smile.onnx.TensorInfo;
import smile.onnx.ElementType;
import smile.onnx.OnnxType;
import java.util.Arrays;

// Create from Java arrays
OrtValue v = OrtValue.fromFloatArray(
        new float[]{1f, 2f, 3f, 4f}, new long[]{2, 2});
OrtValue d = OrtValue.fromDoubleArray(new double[]{1.0, 2.0}, new long[]{2});
OrtValue i = OrtValue.fromIntArray(new int[]{10, 20}, new long[]{2});
OrtValue l = OrtValue.fromLongArray(new long[]{100L, 200L}, new long[]{2});

// Extract / inspect outputs
TensorInfo ti = outputs[0].tensorInfo();
System.out.println(ti.elementType());           // e.g. FLOAT
System.out.println(Arrays.toString(ti.shape())); // e.g. [1, 1000]
float[] scores = outputs[0].toFloatArray();

OnnxType kind = outputs[0].onnxType();   // TENSOR, SEQUENCE, MAP, …
boolean isDynamic = ti.isDynamic();

SessionOptions

SessionOptions is a fluent builder for session-level configuration. Close it after the session is created.

Java
import smile.onnx.*;

try (var opts = new SessionOptions()) {
    // Thread counts (0 = let ORT decide)
    opts.setIntraOpNumThreads(4)   // within one operator
        .setInterOpNumThreads(2);  // across independent ops

    opts.setGraphOptimizationLevel(GraphOptimizationLevel.ENABLE_ALL);
    opts.setOptimizedModelFilePath("resnet50_opt.onnx");

    opts.setExecutionMode(ExecutionMode.SEQUENTIAL); // default
    // opts.setExecutionMode(ExecutionMode.PARALLEL);

    opts.enableCpuMemArena();
    opts.setLogId("my-session");
    opts.setLogSeverityLevel(LoggingLevel.WARNING);

    opts.appendCudaExecutionProvider(0);
    opts.addConfigEntry("session.disable_prepacking", "1");

    try (var session = InferenceSession.create("model.onnx", opts)) {
        // ...
    }
}
GraphOptimizationLevelDescription
DISABLE_ALLNo optimizations; useful for debugging
ENABLE_BASICConstant folding, redundant node elimination
ENABLE_EXTENDEDComplex operator fusions
ENABLE_LAYOUTMemory layout transformations
ENABLE_ALLAll optimizations (recommended for production)
ExecutionModeWhen to use
SEQUENTIALLow-latency single-request serving (default)
PARALLELHigh-throughput batch workloads with parallelizable subgraphs

RunOptions

RunOptions configures a single session.run() call. setTerminate() signals ORT to abort the current run (useful for request timeouts).

Java
import smile.onnx.*;
import java.util.concurrent.*;

try (var runOpts = new RunOptions()) {
    runOpts.setLogTag("request-42")
           .setLogSeverityLevel(LoggingLevel.WARNING);

    OrtValue[] outputs = session.run(inputs, outputNames, runOpts);
}

// Cancel from another thread
var runOpts = new RunOptions();
scheduler.schedule(() -> runOpts.setTerminate(), 5, TimeUnit.SECONDS);
try {
    OrtValue[] outputs = session.run(inputs, outputNames, runOpts);
} catch (OnnxException ex) {
    System.out.println("Run was cancelled: " + ex.getMessage());
} finally {
    runOpts.close();
}

Environment

By default each InferenceSession.create(…) creates a private OrtEnv. When you have multiple sessions in the same process, share one Environment to reduce thread-pool overhead.

Java
import smile.onnx.*;
import java.util.List;

try (var env = new Environment(LoggingLevel.WARNING, "my-app")) {
    try (var s1 = env.createSession("model_a.onnx");
         var s2 = env.createSession("model_b.onnx");
         var s3 = env.createSession("model_c.onnx")) {
        // All sessions share the same OrtEnv thread pools
    }
}

System.out.println(Environment.buildInfo());
List<String> providers = Environment.availableProviders();
// e.g. ["CPUExecutionProvider", "CUDAExecutionProvider"]

env.setLoggingLevel(LoggingLevel.VERBOSE);

Model Introspection

Java
import smile.onnx.*;
import java.util.Arrays;

try (var session = InferenceSession.create("model.onnx")) {
    System.out.println("Inputs  (" + session.inputCount() + "):");
    for (NodeInfo ni : session.inputInfos()) {
        System.out.println("  " + ni.name() + " : " + ni.onnxType());
        if (ni.isTensor()) {
            TensorInfo ti = ni.tensorInfo();
            System.out.println("    element type : " + ti.elementType());
            System.out.println("    shape        : " + Arrays.toString(ti.shape()));
            System.out.println("    dynamic?     : " + ti.isDynamic());
        }
    }

    ModelMetadata meta = session.metadata();
    System.out.println("Producer : " + meta.producerName());
    System.out.println("Graph    : " + meta.graphName());
    System.out.println("Version  : " + meta.version());
    System.out.println("Custom   : " + meta.customMetadata());
}

Inputs  (1):
  data : TENSOR
    element type : FLOAT
    shape        : [-1, 3, 224, 224]
    dynamic?     : true
      

Data Types

ElementTypeJavaNotes
FLOAT / DOUBLEfloat / doubleIEEE 754
INT8 … INT64byte … longSigned integers
UINT8 … UINT64same widthsUse unsigned helpers in Java
BOOLbyteStored as 0 / 1
STRINGStringUTF-8
FLOAT16 / BFLOAT16 / …—No direct Java primitive mapping

OnnxType describes the kind of an OrtValue: TENSOR, SEQUENCE, MAP, SPARSE_TENSOR, OPTIONAL, OPAQUE.

Execution Providers

Execution providers offload work to GPUs or accelerators. Configure them on SessionOptions; ORT tries them in registration order and falls back to CPU. Register GPU providers before relying on CPU (CPU is always available).

Java
import smile.onnx.*;
import java.util.Map;

try (var opts = new SessionOptions()) {
    opts.appendCudaExecutionProvider(0);
    opts.appendTensorRTExecutionProvider(0);
    opts.appendRocmExecutionProvider(0);
    opts.appendDirectMLExecutionProvider(0);   // Windows GPU
    opts.appendCoreMLExecutionProvider(Map.of(
            "ModelFormat", "MLProgram",
            "MLComputeUnits", "ALL"));
    opts.appendVitisAiExecutionProvider(Map.of(
            "cache_dir", "C:\\temp\\vaip_cache",
            "log_level", "info"));

    try (var session = InferenceSession.create("model.onnx", opts)) {
        // Runs on the first available registered provider
    }
}

System.out.println(Environment.availableProviders());

Errors, Resources & Logging

ORT errors surface as unchecked OnnxException with an ORT error code. Always close InferenceSession, OrtValue, SessionOptions, RunOptions, and Environment (try-with-resources). Session logging uses SessionOptions.setLogSeverityLevel; process-wide logging uses Environment / LoggingLevel.

Performance Tuning

ConcernRecommendation
First-load latencyENABLE_ALL + setOptimizedModelFilePath
ThroughputExecutionMode.PARALLEL + setInterOpNumThreads
LatencySEQUENTIAL (default) + setIntraOpNumThreads
Many modelsShare one Environment
MemorydisableCpuMemArena() / disableMemPattern()
BenchmarkingenableProfiling("profile_run") → Chrome trace

Tutorials

Image Classification with ResNet-50

Java
import smile.onnx.*;
import java.util.Map;

float[] pixels = preprocess("cat.jpg", 224, 224);  // NCHW float[1*3*224*224]
long[] shape = {1, 3, 224, 224};

try (var session = InferenceSession.create("resnet50.onnx");
     OrtValue input = OrtValue.fromFloatArray(pixels, shape)) {

    System.out.println("Input  : " + session.inputInfos().getFirst());
    System.out.println("Output : " + session.outputInfos().getFirst());

    String inputName = session.inputNames().getFirst();
    OrtValue[] outputs = session.run(Map.of(inputName, input));
    try {
        float[] logits = outputs[0].toFloatArray();
        int classId = argmax(softmax(logits));
        System.out.printf("Predicted class: %d%n", classId);
    } finally {
        for (OrtValue v : outputs) v.close();
    }
}

NLP with a BERT Tokenizer Model

Many NLP models take INT64 input IDs. Pass tokenized text as long tensors:

Java
import smile.onnx.*;
import java.util.*;

try (var session = InferenceSession.create("bert-base-uncased.onnx")) {
    for (NodeInfo ni : session.inputInfos()) {
        System.out.println(ni.name() + " -> " + ni.tensorInfo());
    }

    int batchSize = 1, seqLen = 128;
    long[] inputIds = new long[batchSize * seqLen];
    long[] attentionMask = new long[batchSize * seqLen];
    long[] tokenTypeIds = new long[batchSize * seqLen];
    Arrays.fill(attentionMask, 1L);
    long[] shape = {batchSize, seqLen};

    try (OrtValue ids = OrtValue.fromLongArray(inputIds, shape);
         OrtValue mask = OrtValue.fromLongArray(attentionMask, shape);
         OrtValue types = OrtValue.fromLongArray(tokenTypeIds, shape)) {

        Map<String, OrtValue> inputs = Map.of(
                "input_ids", ids,
                "attention_mask", mask,
                "token_type_ids", types);
        OrtValue[] outputs = session.run(inputs);
        try {
            float[] hidden = outputs[0].toFloatArray();
            System.out.println("Hidden state size: " + hidden.length);
        } finally {
            for (OrtValue v : outputs) v.close();
        }
    }
}

Sharing an Environment across Sessions

Java
import smile.onnx.*;

try (var env = new Environment(LoggingLevel.WARNING, "model-server")) {
    System.out.println("ORT build : " + Environment.buildInfo());
    System.out.println("Providers : " + Environment.availableProviders());

    try (var opts = new SessionOptions()) {
        opts.setGraphOptimizationLevel(GraphOptimizationLevel.ENABLE_ALL)
            .setIntraOpNumThreads(4);

        try (InferenceSession detector = env.createSession("detector.onnx", opts);
             InferenceSession classifier = env.createSession("classifier.onnx", opts)) {
            // Both sessions share the OrtEnv thread pools
        }
    }
}

GPU Inference with CUDA

Java
import smile.onnx.*;

try (var opts = new SessionOptions()) {
    opts.appendCudaExecutionProvider(0)
        .setGraphOptimizationLevel(GraphOptimizationLevel.ENABLE_ALL);

    try (var session = InferenceSession.create("model.onnx", opts)) {
        // Prefer CUDA when the provider is compiled into ORT
    }
}

Additional recipes in the module guide cover loading a model from a JAR resource, enabling Chrome-trace profiling with enableProfiling, and cancelling long-running inference via RunOptions.setTerminate().

Serving

SMILE Serve loads .onnx files from smile.onnx.model and exposes /api/v1/onnx/{id}. See SMILE Serve — ONNX Inference.

See Also