TensorRT 11 vs TensorRT 10: Porting IPluginV2 to IPluginV3 Before Your Build Breaks

TensorRT 11 vs TensorRT 10: Porting IPluginV2 to IPluginV3 Before Your Build Breaks

TensorRT 11 vs TensorRT 10: Porting IPluginV2 to IPluginV3 Before Your Build Breaks

Most major-version upgrades are a weekend of deprecation warnings. This one is not. TensorRT 11 deleted three load-bearing pieces of every computer-vision and robotics inference pipeline at once: the entire IPluginV2 plugin family, every weak-typing builder flag, and implicit INT8 quantization with its calibrator classes. These are removals, not deprecations — code that referenced them stops compiling against the 11.0 headers, and trtexec command lines that worked on Friday exit with an error on Monday. The grace period is already over: APIs deprecated in TensorRT 10.13 were retained only until August 2026.

The upgrade is genuinely worth doing, but it is a rewrite of your export pipeline rather than a version bump, and for a large slice of edge teams it is not available yet at all.

What this covers: what each removal actually did under the hood, a step-by-step IPluginV2DynamicExt to IPluginV3 port, the calibrator-to-Q/DQ conversion and its accuracy traps, the exact error strings you will paste into a search box, and an honest list of the teams that should stay on 10.x.

Context and Background

TensorRT has carried four generations of plugin interface simultaneously for years. IPluginV2 and IPluginV2Ext were deprecated back in TensorRT 8.5. IPluginV2IOExt and IPluginV2DynamicExt were deprecated in TensorRT 10.0, alongside the introduction of IPluginV3. For six minor releases the old classes kept working, so most teams did the rational thing and did nothing.

Meanwhile the builder carried a parallel duplication in precision handling. A network could be weakly typed, where you set BuilderFlag::kFP16 and the autotuner decided which layers actually ran in half precision, or strongly typed, where the graph itself declared every tensor type and the builder simply obeyed. Quantization had the same split: implicit quantization driven by an IInt8Calibrator at build time, or explicit quantization expressed as QuantizeLinear/DequantizeLinear node pairs in the ONNX graph.

TensorRT 11.0.0 collapsed all of it. Strongly typed networks became the only mode — createNetworkV2() produces one by default and weak typing is simply gone. Implicit quantization was removed outright. The V2 plugin family was removed. Sixteen built-in plugins deprecated before TensorRT 10 were deleted. The kCUBLAS, kCUBLAS_LT and kCUDNN tactic sources were removed, and cuDNN stopped being an optional TensorRT dependency at all.

The release train has moved quickly since. TensorRT 11.0 GA landed on 2 June 2026, 11.1 GA on 24 June, 11.2 GA on 4 August, and 11.3.0 shortly after, with the 11.3.0.99 wheel published to PyPI on 9 September 2026. If you build engines as part of a robotics or vision stack, the questions in our TensorRT-LLM versus llama.cpp comparison for Jetson now have a version qualifier attached to every answer.

A note on sourcing: every class name, method name, flag and version in this post was checked on 21 September 2026 against the NVIDIA TensorRT documentation build labelled 11.3.0, last updated 8 September 2026, and against the TensorRT OSS CHANGELOG on main. Where NVIDIA’s own pages disagree with each other, I say so rather than picking the tidier answer.

What TensorRT 11 Actually Removed, and the Reasoning Behind It

TensorRT 11 removes weak typing, implicit quantization and the IPluginV2 family because all three let the builder make silent decisions the model author could not see, reproduce, or constrain. In 11.x, precision lives in the ONNX graph and plugin capabilities are declared through explicit interfaces. The builder’s job shrinks from guessing well to obeying exactly.

That single sentence explains all three removals, and it is worth unpacking because the migration only makes sense once you see what the builder was previously doing on your behalf.

TensorRT 11 strong typing versus TensorRT 10 weak typing build paths

Figure 1: Where the precision decision lives in TensorRT 10.x versus TensorRT 11.x.

In the 10.x path on the left, an FP32 ONNX model enters the builder and a flag tells the autotuner that reduced precision is permitted. The autotuner then timed kernels and picked whichever ones were fastest, subject to accuracy heuristics. In the 11.x path on the right, the decision has been hoisted out of the builder entirely: either ModelOpt AutoCast rewrites the graph with explicit Cast nodes, or quantization is expressed as Q/DQ node pairs. The graph that reaches the builder already says what precision each tensor is, and the builder has no latitude to disagree.

Weak typing was a heuristic, not a contract

This is the part teams consistently underestimate. Setting BuilderFlag::kFP16 never meant “run this model in FP16”. It meant “you may consider FP16 kernels”. Which layers actually ran in half precision was an emergent property of the tactic timings on the specific GPU, driver, and TensorRT build doing the compiling.

The practical consequence is that a weakly typed engine’s numerical behaviour was not a property of your model. It was a property of your build machine. Rebuild the same ONNX file on an L40S instead of an A100, or on 10.9 instead of 10.6, and a different subset of layers lands in FP16. For a team chasing a rare accuracy regression in a perception stack, this is a miserable variable to control, and it is precisely why BuilderFlag::kOBEY_PRECISION_CONSTRAINTS and kPREFER_PRECISION_CONSTRAINTS existed — bolt-on mechanisms for clawing back determinism the design had given away.

NVIDIA’s own framing in the migration guide is blunt: weak typing made reduced-precision kernels available without per-layer types, but it could compromise accuracy and did not provide adequate control to model authors. All of those flags are gone in TensorRT 11, along with ILayer::setPrecision, ILayer::setOutputType, ITensor::setType, and INormalizationLayer::setComputePrecision. There is nothing left to tune because there is nothing left to guess.

Implicit quantization hid the calibration state outside the model

Calibrator-based INT8 had the same shape of problem, one layer deeper. You subclassed IInt8EntropyCalibrator2, fed it batches, and TensorRT computed per-tensor dynamic ranges during the build. Those ranges were then baked into the engine.

The scales therefore lived in three places at once — the calibration cache file, the engine plan, and nowhere at all in the ONNX model. Reproducing an engine six months later required the same calibration data, the same calibrator implementation, and the same TensorRT version, and if the cache file drifted from the data that produced it there was no mechanism that would tell you. The ONNX artifact in your model registry was, in a meaningful sense, not the thing you deployed.

Explicit quantization moves the scales into the graph as QuantizeLinear and DequantizeLinear operators. TensorRT imports them as IQuantizeLayer and IDequantizeLayer instances and treats them as a hard specification of where precision changes. The quantized model is now a self-describing, diffable, version-controllable artifact. That is a real engineering improvement, and it is also a real amount of work, which we will get to.

The plugin ABI had accumulated four generations of patches

IPluginV2 shipped with an implicit-batch worldview. IPluginV2Ext bolted on output data types and context attachment. IPluginV2IOExt added I/O format awareness. IPluginV2DynamicExt added dynamic shapes through IExprBuilder. Each generation inherited the previous one’s method set, so a modern V2 plugin implemented a class whose interface documented four different eras of TensorRT design, including methods like isOutputBroadcastAcrossBatch that had been meaningless since implicit batch was dropped in 10.0.

IPluginV3 restarts from a different premise, which the next section covers in detail.

Porting a Plugin from IPluginV2DynamicExt to IPluginV3

This is the part of the migration with real engineering risk, because a plugin is the one place where you, not NVIDIA, own the correctness of the generated kernels.

IPluginV2 monolithic class split into IPluginV3 capability interfaces

Figure 2: The V2 monolith becomes three capability interfaces behind a single dispatch method.

The diagram shows the structural change. Where IPluginV2DynamicExt was a single class implementing every method for every lifecycle phase, IPluginV3 is a thin dispatch object with one method — getCapabilityInterface — that hands the builder or runtime the appropriate capability: IPluginV3OneCore for identity, IPluginV3OneBuild for build-time queries, IPluginV3OneRuntime for execution. A plugin object added for the build phase must return a valid interface for all three. One added purely for the runtime phase may omit the build capability.

In Python the three capabilities are typically inherited by a single class, so the split looks cosmetic:

import tensorrt as trt

class MyPluginV3(trt.IPluginV3, trt.IPluginV3OneCore,
                 trt.IPluginV3OneBuild, trt.IPluginV3OneRuntime):
    def __init__(self):
        trt.IPluginV3.__init__(self)
        trt.IPluginV3OneCore.__init__(self)
        trt.IPluginV3OneBuild.__init__(self)
        trt.IPluginV3OneRuntime.__init__(self)
        self.num_outputs = 1
        self.plugin_namespace = ""
        self.plugin_name = "MyPlugin"
        self.plugin_version = "1"

    def get_capability_interface(self, type):
        return self

It is not cosmetic. The dispatch is what lets the runtime load a plugin that carries no build-phase code at all, which matters when you ship an engine to a device that never compiles anything.

Shape inference changed shape, not just name

The single largest source of porting bugs is getOutputDimensions becoming getOutputShapes. Three things changed simultaneously.

It moved from per-index to one-shot. V2 asked for output 0’s dimensions, then output 1’s, in separate calls. V3 asks once and expects every output’s shape back together.

It moved from return value to output parameter, and now returns an int32_t status code. This is a general pattern in V3: methods that returned void in V2, such as configurePlugin, now return a status integer, and a plugin that silently returns a nonzero value from a method it did not intend to fail will produce build errors that point nowhere useful.

It gained shape inputs. addPluginV3() accepts a separate list of shape-input tensors alongside the data inputs, and getOutputShapes receives both. V2’s addPluginV2() accepted only data inputs. This is what enables plugins whose output shape depends on the value of an input rather than its dimensions.

That last capability is the genuinely new one. IExprBuilder::declareSizeTensor() lets a plugin declare a data-dependent output extent with an upper bound and an optimal tuning value, so a non-maximum-suppression plugin can finally express “at most 1,000 boxes, typically 100” instead of always padding to the maximum. One sharp edge, called out explicitly in the 11.3.0 release notes: for a data-dependent-shape plugin, size tensors must be INT64. Using INT32 is a compilation failure, not a runtime warning.

The Python shape method looks like this:

    def get_output_shapes(self, inputs, shape_inputs, exprBuilder):
        output = trt.DimsExprs(len(inputs[0]))
        for i in range(len(inputs[0])):
            output[i] = inputs[0][i]
        return [output]

    def get_output_data_types(self, input_types):
        return [input_types[0]]

get_output_data_types is a new required method. In V2 the equivalent lived on IPluginV2Ext as getOutputDataType, was optional in practice for many plugins, and was queried per index. Forgetting it is a common early failure.

The builder now interrogates the plugin in a defined order

TensorRT 11 builder and IPluginV3 plugin lifecycle sequence

Figure 3: The build-phase conversation between the TensorRT 11 builder, the creator, and the plugin.

Reading this sequence carefully saves a lot of debugging. The builder creates the plugin through IPluginCreatorV3One::createPlugin(), passing a TensorRTPhase argument — kBUILD when constructing a network, kRUNTIME when deserializing an engine. That phase parameter is the unification of what used to be two separate creator methods, createPlugin and deserializePlugin.

It then queries shapes and data types, probes supportsFormatCombination position by position, and calls configurePlugin with DynamicPluginTensorDesc structures whose min, opt and max fields correspond to the kMIN, kOPT and kMAX values of the optimization profile currently being built. Note the type change: V2’s supportsFormatCombination and getWorkspaceSize received static PluginTensorDesc structures, while V3 gives both methods the dynamic variant. If you copy a V2 implementation across unchanged, it will compile against the wrong descriptor and access fields that have moved.

Then comes the part with no V2 equivalent at all.

Tactics and timing caching are now yours to advertise

V2 plugins picked their kernel at build time and lived with it. V3 plugins can hand the builder a set of candidate tactics through getNbTactics and getValidTactics, and the builder profiles each one and keeps the fastest, exactly as it does for native layers. IPluginV3OneRuntime::setTactic then communicates the winner before each enqueue. If your plugin ships three kernel variants for different occupancy regimes, this is where you stop choosing between them by hand.

getTimingCacheID is the companion. Implement it and repeated builds of the same network reuse cached plugin timings. Leave it unimplemented — the default — and every build re-times every tactic, which on a plugin-heavy detection network is a meaningful chunk of CI time.

This is also the most common source of post-migration performance regressions, and the direction of the surprise catches people out. A plugin tuned against V2 often gets slower on first port, because IPluginV2::initialize() and terminate() are gone and authors who used initialize() as their one-time setup site tend to relocate that work into enqueue. It belongs in the constructor, configurePlugin, or onShapeChange instead. Per-call allocations inside enqueue are the single most frequent measured regression, and requesting scratch through getWorkspaceSize rather than calling cudaMalloc internally lets TensorRT pool the allocation across the whole network.

Serialization moves from a byte buffer to a field collection

V2 serialization was getSerializationSize() plus serialize(void*), writing raw bytes, with IPluginCreator::deserializePlugin reading them back. V3 replaces both with IPluginV3OneRuntime::getFieldsToSerialize(), which returns a PluginFieldCollection, and deserialization happens through the same createPlugin call described above.

TensorRT handles serialization for the types in PluginFieldType. Custom structs go through PluginFieldType::kUNKNOWN with the length set to the byte count:

mDataToSerialize.emplace_back(
    PluginField("intScalar", &mIntValue, PluginFieldType::kINT32, 1));
mDataToSerialize.emplace_back(
    PluginField("dummyStruct", &mDummyStruct,
                PluginFieldType::kUNKNOWN, sizeof(DummyStruct)));

Two lifetime rules here have bitten real deployments. First, the PluginFieldCollection handed to createPlugin during deserialization is owned by TensorRT and valid only for the duration of that call. A plugin that stores a pointer into PluginField::data and reads it later is a use-after-free. Copy everything you need into plugin-owned storage before returning.

Second, and less intuitive, a PluginField whose data is empty could crash the V3 creator dispatch path during build or deserialization. NVIDIA tracks this as NVBug 5607435, and the documented workaround is to populate every field with a non-empty sentinel even when the value is unused:

import numpy as np

# Crashes the V3 dispatch path
fields = trt.PluginFieldCollection([
    trt.PluginField("flag", b"", trt.PluginFieldType.INT32),
])

# Safe
fields = trt.PluginFieldCollection([
    trt.PluginField("flag", np.array([0], dtype=np.int32),
                    trt.PluginFieldType.INT32),
])

A related NumPy 2.x symptom — ValueError: The truth value of an empty array is ambiguous when evaluating a default-constructed tensorrt.PluginField().data in a boolean context — was fixed in 11.3.0. If you are on 11.2.x and your plugin registration code contains assert not pfield.data, that is the bug.

attachToContext is a clone, not a mutation

IPluginV2Ext::attachToContext() mutated the existing plugin instance and handed it cuDNN and cuBLAS handles. IPluginV3OneRuntime::attachToContext() takes an IPluginResourceContext and is a clone-and-attach operation: it clones the entire IPluginV3 object and returns the new instance.

Two consequences. If you implemented the runtime capability as a separate class holding a back-pointer to the owning IPluginV3, that back-pointer must be updated in the clone — otherwise the cloned runtime capability dispatches into the original object and reads freed memory. And cuDNN/cuBLAS handles are simply not provided any more, consistent with cuDNN no longer being a TensorRT dependency. Plugins that relied on them must initialise their own, or share them through IPluginResource and the plugin registry’s key-value store.

There is no detachFromContext equivalent; move that teardown to the destructor. There is no initialize, terminate, or destroy either — a V3 plugin must be constructed in an initialised state.

Rebuilding the Precision and Quantization Pipeline

With plugins ported, the second workstream is the export pipeline. It is less intellectually demanding and more likely to cost you accuracy.

AutoCast replaces the FP16 flag

The mechanical substitution for BuilderFlag.FP16 is NVIDIA’s ModelOpt AutoCast, which rewrites an FP32 ONNX graph into mixed precision offline:

pip3 install --no-cache-dir --extra-index-url https://pypi.nvidia.com nvidia-modelopt[all]
python -m modelopt.onnx.autocast --onnx_path model.onnx --output_path model_fp16.onnx
# TensorRT 10.x
trtexec --onnx=model.onnx --saveEngine=engine.plan --fp16

# TensorRT 11.x
trtexec --onnx=model_fp16.onnx --saveEngine=engine.plan

--fp16, --bf16, --int8, --fp8, --int4, --best, --precisionConstraints, --layerPrecisions, --layerOutputTypes, --calib and --calibProfile are all removed and will make trtexec exit with an error. --stronglyTyped survives as an accepted no-op, with a small trap: passing it twice makes the argument parser reject it with [E] Unknown option: --stronglyTyped, because only the first occurrence is treated as a deprecated no-op.

Four other flags flipped from opt-in to default and are now no-ops: --useCudaGraph, --useSpinWait, --noDataTransfers and --separateProfileRun. Their inverses (--noCudaGraph, --noSpinWait, --includeDataTransfers) are what you reach for now. If you have benchmarking scripts that compare runs with and without CUDA graphs, they have been silently measuring the same configuration since 11.0.

If you prefer not to depend on ModelOpt, you can add Cast nodes to the ONNX graph directly, or use INetworkDefinition::addCast when building a network through the layer APIs. It is more control and considerably more work.

Calibrator to Q/DQ is a semantic change, not a port

This is the step teams misjudge. Replacing a calibrator with modelopt.onnx.quantization looks like swapping one tool for another:

python -m modelopt.onnx.quantization \
    --onnx_path model.onnx \
    --calibration_data data.npz \
    --output_path model_quantized.onnx
# TensorRT 11.x: Q/DQ nodes are already in the graph
builder = trt.Builder(logger)
network = builder.create_network()
config = builder.create_builder_config()
parser = trt.OnnxParser(network, logger)
with open("model_quantized.onnx", "rb") as f:
    parser.parse(f.read())
engine_bytes = builder.build_serialized_network(network, config)

But the two approaches quantize different sets of tensors. A calibrator computed dynamic ranges for activations and let TensorRT decide which layers to run in INT8. Q/DQ quantization places explicit node pairs at specific graph locations, and TensorRT then performs Q/DQ propagation — moving Quantize nodes backward and Dequantize nodes forward to maximise the low-precision region — subject to which layers commute with quantization. Max pooling commutes; average pooling does not and is instead fused with its surrounding Q/DQ pair. Where the nodes end up is a function of where you put them, not of what was fastest.

That means the accuracy you measured under implicit INT8 is not a prediction of the accuracy you will get from a Q/DQ graph, in either direction. Re-validating on your real evaluation set is not optional.

The placement rules that matter most, from NVIDIA’s explicit-quantization guidance: quantize all inputs of weighted operations (convolution, transposed convolution, GEMM); by default do not quantize their outputs, because an activation like SiLU immediately downstream needs higher-precision input; quantize the residual input in skip connections, because the precision of the first input to the element-wise add determines the fusion output’s precision, and leaving it high-precision prevents the trailing Quantize from fusing into the convolution; use per-tensor quantization for activations and per-channel for weights.

Be conservative. Performance can decrease if TensorRT cannot fuse an operation with its surrounding Q/DQ layers, and an extra Q/DQ pair in the wrong place forces a convolution apart from a following element-wise add that would otherwise have fused.

Two conversion traps worth knowing before you start

There is a per-channel transpose subtlety that silently corrupts accuracy. PyTorch exports torch.nn.Linear as an ONNX GEMM with (K, C) weights and transB enabled; TensorFlow pre-transposes to (C, K). TensorRT quantizes weights before transposing them, so per-channel quantization uses axis K = 0 for PyTorch-origin models and axis K = 1 for TensorFlow-origin models. Get the axis wrong and the model still builds and still runs — it is just wrong.

And TensorRT does not support pre-quantized ONNX models that use QLinearConv, QLinearMatmul, ConvInteger or MatmulInteger. These generate an import error. If an upstream team hands you a model quantized through ONNX Runtime’s operator-fusing path rather than a Q/DQ path, it will not parse, and the fix lives in their export script rather than yours.

The Error Messages You Will Actually See

Migration failures cluster into a handful of recognisable shapes.

Compile-time, C++. Any reference to IPluginV2, IPluginV2Ext, IPluginV2IOExt, IPluginV2DynamicExt, IPluginCreator, IPluginV2Layer or addPluginV2() fails against the 11.0 headers, with the compiler reporting the symbol as not a member of namespace nvinfer1. There is no shim and no compatibility macro.

Compile-time, third-party. The highest-profile instance is ONNX Runtime. Releases up to and including 1.24.4 cannot compile their TensorRT Execution Provider against TensorRT 11.0.0, because the provider still references IBuilder::platformHasFastFp16(), IBuilder::platformHasFastInt8() and IBuilderConfig::setInt8Calibrator(). On MSVC this surfaces as C2039 'member not found' on those symbols. ONNX Runtime 1.27 and later adopts the 11.x API. If you are pinned below that, you stay on TensorRT 10.x, patch the provider, or upgrade ONNX Runtime — there is no fourth option. Anyone weighing runtime choices here should read our ONNX, TFLite, ExecuTorch and Core ML comparison alongside this constraint.

Runtime, Python. Removed attributes surface as ordinary AttributeErrors naming the missing symbol — IInt8EntropyCalibrator2, add_plugin_v2, int8_calibrator, device_memory_size, weight_streaming_budget, plugin_creator_list. The replacements are add_plugin_v3, device_memory_size_v2, weight_streaming_budget_v2 and all_creators respectively. get_plugin_creator() becomes get_creator().

Quantization tooling. ModelOpt ONNX quantization may fail during calibration with ValueError: Too many bins for data range. Cannot create 128 finite-sized bins. This is raised in the ONNX Runtime calibration histogram path inside ModelOpt, not by the TensorRT builder — a useful distinction, because it means the fix is in your calibration data or ModelOpt configuration.

Build-time, tactic selection. Could not find any implementation for node {ForeignNode[...]} on a strongly typed network with float quantization usually means you have quantized a layer for which no low-precision kernel exists on your target. The documented remedy is to remove the Q/DQ nodes around the failing layer.

Windows deserialization. Version-compatible engines built with TensorRT 10.1 through 10.4 that use the RoiAlign plugin fail to deserialize on the 11.x runtime, reporting ERROR_MOD_NOT_FOUND (error 126) while loading nvinfer_vc_plugin.dll, surfacing as TensorRT error code 6 and a null ICudaEngine. Rebuild with 10.5 or later.

Trade-offs, Gotchas, and What Goes Wrong

The most awkward thing to report is that NVIDIA’s documentation contradicts itself on the central question of this post. The top-level 10.x-to-11.x migration page, the 11.0.0 release notes, the removed-Python-API table and the Jetson migration page all state that the V2 plugin family has been removed in 11.0. But the Python migration-patterns page, in the same 11.3.0 documentation build, says the V2 interfaces “remain present in TensorRT 11.x” and that no V2 classes are removed in this release. The Plugin API Description page hedges differently again, describing V2 only as deprecated. Four sources say removed, two say present. Plan for removed — that is what the release notes and the removed-API tables commit to, and it is the only reading that is safe if it turns out to be true.

Performance is the second honest caveat. TensorRT 11.3.0’s own release notes list several regressions against 11.2.1 that land squarely on this audience: strongly typed FP16 DeBERTa roughly 16.7% slower on an RTX 4090 and about 20% on DGX Spark, because fused multi-head attention is lost and replaced with extra GEMM and memory movement, with activation memory growing from about 105 MB to about 197 MB. A strongly typed FP16 ViT-Base patch-16 384 network about 6–7% slower on RTX PRO 6000 Blackwell. 3dUNet INT8 about 43–44% slower on DGX Spark, attributed to the CUDA 13.4 device compiler. FP8 causal attention about 11% slower on GH200. Earlier, 11.0.0 noted strongly typed FP16 networks on RTX PRO 6000 Blackwell Max-Q regressing up to roughly 45% on individual networks.

These are transient, they are version-pair specific, and none of them are reasons to avoid TensorRT 11 permanently. They are reasons to benchmark your own models rather than assuming a newer major version is faster.

Third: engine compatibility is one-directional and narrower than people expect. Engines built with 10.x run on the 11.x runtime. Engines built with 8.x or 9.x do not, because 8.x uses CUDA 11.x and TensorRT 11 requires CUDA 13.2 Update 2 or later. Within the major version, a version-compatible engine built with 11.5 runs on 11.5 or later but not on 11.0 through 11.4. The static libraries are also gone — libnvinfer_static.a and its siblings no longer ship — so anyone statically linking must move to shared libraries. Tar and zip archive filenames changed too, which quietly breaks download scripts in CI.

When NOT to Upgrade Yet

Decision flow for whether to adopt TensorRT 11 or stay on TensorRT 10.x

Figure 4: Four conditions that should keep you on TensorRT 10.x today.

For a large share of this site’s readers, the correct answer right now is “you cannot”.

If you deploy on Jetson, TensorRT 11 is not available to you. NVIDIA JetPack is not supported in TensorRT 11.3.0. Both Jetson Orin on JetPack 6.x and Jetson Orin/Thor on JetPack 7.x are listed as unsupported, and the guidance is explicit: remain on a TensorRT 10.x release supported by your JetPack version. NVIDIA’s own Jetson migration page opens by telling you not to use it as a path to install 11.3.0. JetPack 6.x users are additionally told that an upgrade to JetPack 7.x will be required before any future TensorRT 11.x JetPack release.

If you need DLA, stop at 10.7. DLA is not supported in TensorRT 11.3.0, nor in 11.3.1 for DriveOS, and TensorRT 10.7 was the last release that supported it. For mixed-criticality designs that offload a safety-relevant network to the deep learning accelerator while the GPU handles everything else, this is disqualifying on its own — a constraint worth reading alongside our analysis of GPU partitioning on Jetson Thor.

If you cross-compile, keep host and target versions aligned. Mixing 10.x headers on an x86 host with 11.x libraries on the target produces compile or link errors on the removed APIs, and the failure mode looks like a broken toolchain rather than a version mismatch.

None of this is a reason to do nothing. It is a reason to do the preparatory work now while still on 10.x, because every piece of it is independently valuable and backward-compatible.

Practical Recommendations

Start by finding out how exposed you actually are. Grep the codebase for IPluginV2, IPluginCreator, addPluginV2, add_plugin_v2, IInt8Calibrator, setInt8Calibrator, setDynamicRange, setPrecision, setOutputType, kCUBLAS, kCUDNN, and for the sixteen removed built-in plugin names — BatchedNMS_TRT, BatchedNMSDynamic_TRT, BatchTilePlugin_TRT, Clip_TRT, CoordConvAC, CropAndResize, CustomGeluPluginDynamic, EfficientNMS_ONNX_TRT, LReLU_TRT, NMS_TRT, NMSDynamic_TRT, Normalize_TRT, Proposal, SingleStepLSTMPlugin, SpecialSlice_TRT and Split. Most of these map back to standard network APIs such as addNMS(), addActivation(), addNormalizationV2(), addSlice() or addLoop().

Then do the reversible work first. Every one of these steps is valid on TensorRT 10.x and shrinks the eventual cutover:

  • Opt into strongly typed networks today with createNetworkV2(NetworkDefinitionCreationFlag::kSTRONGLY_TYPED), or builder.create_network(int(trt.NetworkDefinitionCreationFlag.STRONGLY_TYPED)) in Python, and fix whatever breaks while you still have the old path to fall back on.
  • Move quantization from calibrators to Q/DQ nodes using ModelOpt, and record the accuracy delta on your real evaluation set before anything else changes.
  • Port plugins to IPluginV3 and IPluginCreatorV3One on 10.x, where both interfaces already exist. Build strongly typed while doing so — V3 plugins in weakly typed networks can hit fusion paths that IPluginV2DynamicExt never exercised and crash.
  • While porting, add getValidTactics and getTimingCacheID rather than treating them as optional. This is the cheapest window you will get to claw back the performance the lifecycle change costs you.
  • Pin ONNX Runtime to 1.27 or later if the TensorRT Execution Provider is in your stack.
  • Audit trtexec invocations in CI for the removed precision flags and the four flags that became no-ops.
  • Benchmark your own models on 11.x before committing. Given the documented regressions, assume nothing.

Teams running inference servers rather than embedded devices have more freedom here and should weigh the serving-layer trade-offs in our vLLM, SGLang and TensorRT-LLM comparison.

Frequently Asked Questions

Is IPluginV2 deprecated or removed in TensorRT 11?

NVIDIA’s documentation is inconsistent on this point. The 11.0.0 release notes, the main 10.x-to-11.x migration page, the removed-API reference tables and the Jetson migration page all state the entire IPluginV2 family — including IPluginCreator, IPluginV2Layer and addPluginV2() — has been removed in TensorRT 11.0. The Python migration-patterns page in the same documentation build says the V2 classes remain present. Plan for removal: it is what the authoritative release notes commit to, and it is the assumption that stays safe either way.

Can I still build an FP16 engine in TensorRT 11?

Yes, but you declare it in the model rather than at build time. BuilderFlag.FP16 and trtexec --fp16 are removed. Convert the ONNX graph to mixed precision first with ModelOpt AutoCast, or insert Cast nodes manually, then build normally. Strong typing is always on, so --stronglyTyped and the STRONGLY_TYPED network flag are no longer needed. The result is that the same ONNX file now produces the same precision assignment on every GPU, which weak typing never guaranteed.

Do my existing TensorRT 10 engines still run on the TensorRT 11 runtime?

Engines built with TensorRT 10.x run on the 11.x runtime, including version-compatible engines built with kVERSION_COMPATIBLE. Engines built with 8.x or 9.x do not, because those depend on CUDA 11.x and TensorRT 11 requires CUDA 13.2 Update 2 or later. Rebuilding with the 11.x builder is still recommended to pick up the newer optimisations. One Windows exception: version-compatible engines built with 10.1 through 10.4 that use the RoiAlign plugin fail to deserialize and must be rebuilt with 10.5 or later.

Why is my ported IPluginV3 plugin slower than the V2 version?

Almost always because setup work migrated into enqueue. IPluginV2::initialize() and terminate() do not exist in V3, and code that lived in initialize() belongs in the constructor, configurePlugin or onShapeChange — not in the per-call path. Then check three things: request scratch memory through getWorkspaceSize instead of calling cudaMalloc internally, implement getNbTactics and getValidTactics so the builder can profile your kernel variants, and avoid device allocations in clone(), which the builder calls frequently.

Can I install TensorRT 11 on a Jetson Orin or Thor?

No. NVIDIA JetPack is not supported in TensorRT 11.3.0, for either JetPack 6.x on Orin or JetPack 7.x on Orin and Thor. Jetson deployments must remain on a TensorRT 10.x release matching their JetPack version until a later 11.x release restores JetPack support. DLA is a separate blocker: it is unsupported in 11.3.0, and TensorRT 10.7 was the last release that supported it. Use the waiting period to move to strong typing, Q/DQ quantization and IPluginV3, all of which work on 10.x.

What replaced the INT8 calibrator in TensorRT 11?

Explicit quantization with QuantizeLinear and DequantizeLinear node pairs in the ONNX graph. IInt8Calibrator and its subclasses, int8_calibrator, the calibration-profile methods and all dynamic-range APIs are removed. The usual path is python -m modelopt.onnx.quantization with a calibration .npz, producing a quantized ONNX file the builder consumes directly. Treat it as a re-quantization rather than a port — Q/DQ placement determines which tensors are quantized, so accuracy must be re-measured rather than assumed.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *