Skip to content

Instantly share code, notes, and snippets.

@robertknight
Created July 12, 2026 12:27
Show Gist options
  • Select an option

  • Save robertknight/08cd47a0251e0cd5953c71c575522850 to your computer and use it in GitHub Desktop.

Select an option

Save robertknight/08cd47a0251e0cd5953c71c575522850 to your computer and use it in GitHub Desktop.
Operator implementation estimates

Missing ONNX operators: implementation cost estimates

Rough estimates of the cost of implementing the ONNX operators listed as unsupported in the operator tracking issue, based on the ONNX operator specs. Last updated: 2026-07-12.

How to read the estimates

  • Complexity is Small / Medium / Large, judging spec surface area (attributes, modes, type support), how much existing rten machinery can be reused, and how performance-sensitive the op is.
  • Code size is very rough new-code line count including inline tests, but excluding the fixed per-op glue every new operator pays: an entry in rten-model-file/src/schema.fbs (plus regenerated code), rten-convert schema updates, src/op_registry.rs registration, ONNX attribute reading and shape inference. That glue is typically ~50–150 lines per op.
  • Deps lists new crate dependencies. Most ops need none; the exceptions are called out. Heavy deps would be feature-gated, like rustfft (fft feature) and fastrand (random feature) are today.

Cross-cutting infrastructure gaps

Some ops are cheap in themselves but blocked on missing infrastructure:

  • String tensors. DataType currently supports only Int32, Float, Int8 and UInt8. RegexFullMatch, StringConcat, StringNormalizer, StringSplit and (partly) TfIdfVectorizer need a string tensor element type threaded through rten-tensor, Value, the model file format and the ONNX loader. That is a Large standalone project (~1000+ lines) before any of those ops can be written.
  • Optional values. Optional / OptionalGetElement / OptionalHasElement need an "optional" value kind in the Value type system and graph plumbing. The three ops are trivial once that exists.
  • MaxPool indices output. MaxUnpool consumes the Indices output of MaxPool, which rten currently does not produce, so implementing it also means extending MaxPool.
  • Wider dtypes. BitCast between different-width types (e.g. f32 ↔ f16) is only meaningful once more dtypes exist; the same-width cases (f32 ↔ i32, i8 ↔ u8) are trivial today.

Note: BitShift, BitwiseAnd/Or/Xor/Not and wider DataType variants have already been prototyped on the doom-onnx branch.

Estimates

Operator Complexity Est. code size New deps Notes
AffineGrid Small–Medium 200–350 none Generate 2D/3D sampling grids from affine matrices; pure index math, complements existing GridSample.
Bernoulli Small 100–150 none Elementwise random draw; reuses existing random feature infra (fastrand).
BitCast Small 100–200 none Same-width reinterpret (f32↔i32, i8↔u8) is near-trivial; cross-width variants blocked on unsupported dtypes.
BitShift Small 50–100 none Fits the binary-elementwise template; prototyped on doom-onnx.
BitwiseAnd Small 30–60 none Binary-elementwise template; prototyped on doom-onnx.
BitwiseNot Small ~30 none Unary-elementwise template; prototyped on doom-onnx.
BitwiseOr Small 30–60 none As BitwiseAnd.
BitwiseXor Small 30–60 none As BitwiseAnd.
BlackmanWindow Small ~150 shared none One shared cosine-window helper covers Blackman/Hamming/Hann; complements STFT/DFT.
Celu Small 40–60 none Elementwise with alpha attribute.
CenterCropPad Small–Medium 150–250 none Per-axis crop-or-pad to a target shape; composable from existing slice/pad logic, axes handling is the fiddly part.
Col2Im Medium 250–400 none Inverse of im2col. A 2D col2im core exists inside ConvTranspose; the spec requires N-D plus dilations/pads/strides.
Compress Small 100–200 none Boolean-mask selection, optionally along an axis; output size is data-dependent (like NonZero).
CumProd Small 80–120 none Direct clone of existing CumSum.
DeformConv Large 800–1500 none Deformable conv v2: per-output-pixel offset/mask bilinear sampling feeding grouped matmul. Perf-sensitive; the biggest pure-compute item on this list.
Det Medium 200–300 none Batched determinant; needs a small hand-rolled LU decomposition (not worth a linalg dep).
GlobalLpPool Small 60–100 none Reduce machinery exists (ReduceL1/ReduceL2); generalize to arbitrary integer p.
GroupNormalization Small–Medium 150–250 none Reshape + reuse of existing instance/layer-norm kernels.
HammingWindow Small (shared) none See BlackmanWindow.
HannWindow Small (shared) none See BlackmanWindow.
Hardmax Small 80–120 none ArgMax along axis → one-hot; both building blocks exist.
ImageDecoder Medium code, Large deps 150–300 glue image codecs (image or zune-*/png crates) Decode JPEG/PNG/BMP/GIF/TIFF/WebP etc. Glue is small but codec deps are heavy; would be feature-gated. Rare in models.
LRN Small–Medium 150–250 none Sliding-window normalization across channels; legacy (AlexNet-era) op.
LpNormalization Small 60–100 none Normalize along one axis by L1/L2 norm.
LpPool Medium 150–300 none Extend existing pooling machinery with a p-norm accumulator; N-D spatial dims.
MaxRoiPool Medium 200–300 none Legacy ROI max-pooling; superseded by RoiAlign in practice.
MaxUnpool Medium 250–400 none Also requires adding the Indices output to MaxPool (currently not produced).
MeanVarianceNormalization Small 80–150 none Composable from existing reduce ops over the axes attribute.
MelWeightMatrix Small–Medium 150–250 none Generate mel filterbank matrix; formula-heavy but self-contained. Complements STFT.
Mish Small 40–60 none x * tanh(softplus(x)); add ~100 lines if a SIMD kernel in rten-vecmath is wanted.
NegativeLogLikelihoodLoss Medium 250–400 none Training-oriented: class weights, ignore_index, three reduction modes. Rare in inference models.
Optional Medium (infra) 150–300 shared none Cost is the new optional value kind in Value/graph plumbing; the op itself is trivial.
OptionalGetElement Small¹ 30–60 none Trivial once optional values exist.
OptionalHasElement Small¹ 30–60 none Trivial once optional values exist.
QLinearConv Medium 300–500 none Requantization (u8/i8 output, per-channel scales) layered on existing ConvInteger kernels; could partly be lowered at load time.
QLinearMatMul Medium 200–400 none Same story on top of MatMulInteger.
RNN Medium 200–350 none Vanilla RNN sharing the gate machinery in rnn.rs with GRU/LSTM; the activations attribute variants add some surface.
ReduceLogSum Small 60–100 none Reduce machinery exists; log(sum(x)).
ReduceLogSumExp Small 80–120 none Needs max-subtraction for numerical stability.
RegexFullMatch Large¹ ~100 + infra regex (sizeable, Unicode tables) Blocked on string tensor support; the op itself is a one-liner over regex.
RoiAlign Medium 300–450 none Bilinear ROI sampling with avg/max modes and coordinate-transform modes; well-specified.
Scan Medium–Large 400–700 none Subgraph machinery exists (Loop/If), but state variables plus scan inputs/outputs with per-tensor axes/directions are fiddly.
Selu Small 40–60 none Elementwise with alpha/gamma.
SequenceMap Medium 150–300 none Run a subgraph per sequence element; sequence and subgraph infra both exist.
Shrink Small 40–60 none Elementwise with bias/lambd.
SoftmaxCrossEntropyLoss Medium 250–400 none Training-oriented; shares its core with NegativeLogLikelihoodLoss.
Softsign Small 30–50 none `x / (1 +
SpaceToDepth Small 100–150 none Inverse of existing DepthToSpace.
StringConcat Small¹ 50–100 none Blocked on string tensor support.
StringNormalizer Medium¹ 150–300 none (or a case-folding crate) Blocked on string tensors; case-folding and locale quirks in the spec.
StringSplit Medium¹ 150–250 none Blocked on string tensors; ragged output returned as padded tensor + lengths.
TensorScatter Medium 200–300 none Opset 24 KV-cache update op: past_cache/update/write_indices, linear and circular modes. Relevant to LLM inference.
TfIdfVectorizer Large 400–700 none for int input; string infra for string input N-grams with skips, pool matching, three weighting modes; notoriously messy spec. Rare outside classical-ML exports.
ThresholdedRelu Small 30–50 none Elementwise with alpha.
Unique Medium 250–400 none Four optional outputs, sorted/unsorted order, and an axis mode that dedups whole slices.

¹ Estimate is for the op alone, assuming the blocking infrastructure (string tensors / optional values) already exists — see Cross-cutting infrastructure gaps.

Summary

  • Small (quick wins, mostly template-based): the elementwise activations (Celu, Mish, Selu, Shrink, Softsign, ThresholdedRelu), bitwise ops and BitShift, the window functions, CumProd, ReduceLogSum(Exp), GlobalLpPool, LpNormalization, Hardmax, MeanVarianceNormalization, SpaceToDepth, Compress, Bernoulli, BitCast. Roughly 30–150 lines each on top of existing templates.
  • Medium: QLinearConv/QLinearMatMul, RNN, RoiAlign, Unique, TensorScatter, Col2Im, LpPool, GroupNormalization, LRN, MaxUnpool, MaxRoiPool, Det, MelWeightMatrix, AffineGrid, CenterCropPad, SequenceMap, the loss ops.
  • Large: DeformConv (compute kernel), Scan (control-flow surface), TfIdfVectorizer (spec complexity), ImageDecoder (dependency weight), and the string-tensor infrastructure that gates the four string ops.
  • New dependencies are only needed for ImageDecoder (image codecs) and RegexFullMatch (regex); everything else is implementable with the existing dependency set.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment