Man in a Turban by Rembrandt

Artwork: Man in a Turban by Rembrandt. The Metropolitan Museum of Art · Public domain

Machine Learning

Choosing an On-Prem SLM Inference Engine for Windows

AI disclosure: This article was moderated by AI.

For a Retrieval-Augmented Generation (RAG) system running offline on Windows, the inference engine determines whether the latency budget is realistic. We evaluated ONNX Runtime GenAI, LlamaCPP, Hugging Face Optimum, and Triton. ONNX Runtime GenAI delivered the best token throughput, latency, and wall-clock efficiency for our scenario. This first part of the series covers how we evaluated the engines and what we measured.

The constraints

The system combined LangChain, an SLM, and guardrails. It also had to meet four hard requirements in an air-gapped Windows environment:

The target machine did not support Docker, which ruled out Triton. A Windows-first deployment also made an engine that required extensive custom builds, such as LlamaCPP, expensive to maintain.

The candidates

EngineWhy we considered itDealbreaker for us
ONNX Runtime GenAIFirst-class ONNX + Phi-3 support, Windows binaries—
LlamaCPPFast, portable GGUF inferenceNeeds custom builds on Windows
Hugging Face OptimumFamiliar HF ergonomicsLimited graph optimization; no Phi-3
TritonGreat serving throughputRequires Docker; blocked on-prem

We used Microsoft’s Phi-3 Mini 4k Instruct ONNX model on an Azure ND96amsr_A100_v4 instance with 1× A100 80GB. The benchmark fixed generation length at 256 tokens and ran 5 warmups followed by 10 repetitions. The driver was the benchmark script included with onnxruntime-genai.

Reproducing the benchmark

The results below come from the package’s benchmark_e2e.py. We swept several prompt lengths at batch size 1 and targeted approximately 99% GPU utilization:

python benchmark_e2e.py \
  -i ./phi3-mini-4k-instruct-onnx \
  -g 256 \            # generation length (tokens)
  -p 16,64,256,1024,2048 \  # prompt lengths swept
  -b 1 \              # batch size
  -w 5 -r 10          # 5 warmup runs, 10 repetitions

Each run captured token-generation throughput (tps), token-generation latency, wall-clock throughput, wall-clock latency, and GPU utilization. GPU utilization served as a sanity check. An engine that cannot saturate the A100 leaves throughput unused regardless of its per-token results.

Results

Token generation throughput (tokens/sec)

ONNX Runtime GenAI led at every prompt length. Its advantage grew with the prompt, which matters for RAG because retrieved context makes prompts longer.

Prompt lengthORT GenAILlamaCPPHF OptimumORT/LlamaCPPORT/Optimum
16137.59109.47108.351.26×1.27×
64136.82110.26107.141.24×1.28×
256134.45109.42105.761.23×1.36×
1024127.34105.60102.111.21×1.50×
2048122.62102.0099.351.20×1.59×

Hand-drawn line graph of the measured ONNX Runtime GenAI to Hugging Face Optimum throughput ratio rising from 1.27 times at 16 prompt tokens to 1.59 times at 2048 tokens.

Wall-clock latency (seconds)

Wall-clock latency is the metric tied directly to the 5-second requirement. ORT GenAI remained under that limit even with 2048-token prompts.

Prompt lengthORT GenAI (s)Optimum (s)LlamaCPP (s)
162.4913.5262.51
642.5023.5452.52
2562.5713.7722.71
10242.7904.0493.12
20483.0734.2793.46

Optimum’s weaker graph optimization and lack of Phi-3 support add roughly a second of latency for long prompts. At 2048 prompt tokens, it took 4.279s compared with 3.073s for ORT GenAI. That is a 1.2-second gap, or approximately 39% slower, enough to determine whether the pipeline meets the budget.

Why ONNX Runtime GenAI is faster

The performance gap follows from how the engine prepares and runs the graph:

LlamaCPP remains competitive on throughput but trails by approximately 20%. On Windows, it also requires custom builds that we did not want to maintain.

Windows vs Linux

ORT GenAI’s wall-clock throughput on Windows matched or exceeded Linux at every measured prompt length. In this benchmark, Windows did not impose the performance penalty we had assumed.

Prompt lengthWindows (tps)Linux (tps)
16142.18129.52
256259.48235.68
2048932.03892.12

Decision

ONNX Runtime GenAI had the highest throughput and lowest latency at every prompt length. It also fully optimizes Phi-3 ONNX models, which Optimum cannot do, and provides official Windows binaries without requiring a custom build. Unlike Triton, it does not depend on Docker. For offline, Windows-based SLM inference under these constraints, it is the practical default.

Part two connects the selected engine to a LangChain RAG pipeline. It includes a custom BaseLLM and the model-building step that produces the optimized ONNX graph.

References

#slm#onnx#rag#inference#windows