Files
nvidia--tensorrt/.agents/skills/trt-torch-quickstart/SKILL.md
T
wehub-resource-sync c8a779b1bb
Docker Image CI / build-ubuntu2004 (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:36:55 +08:00

15 KiB
Raw Blame History

name, description, license, metadata
name description license metadata
trt-torch-quickstart Compile a PyTorch model to a TensorRT engine via Torch-TensorRT — AOT or JIT — under the new strong-typing default. Use when the user compiles PyTorch to TensorRT without ONNX, hits "enabled_precisions should not be used when use_explicit_typing=True", sees Dynamo graph breaks or PyTorch fallback, debugs ABI errors at import torch_tensorrt, or needs the compatible torch / torch_tensorrt / tensorrt-cu13 version pins for TensorRT 11. Triggers: torch_tensorrt, torch_tensorrt.dynamo.compile, torch.compile backend torch_tensorrt, pytorch to tensorrt, ExportedProgram, Dynamo graph break, use_explicit_typing, enabled_precisions, torch_tensorrt.Input, min_block_size, truncate_double, tensorrt-cu13, version pinning, version compatibility. Adjacent skills: `trt-onnx-quickstart`, `trt-cpp-runtime-quickstart`. LLM token generation belongs in TensorRT-LLM. Apache-2.0
author version tags
NVIDIA Corporation 1.0
torch-tensorrt
dynamo
pytorch
quickstart
fp16

Torch-TensorRT Quickstart

Convert a PyTorch nn.Module to a TensorRT engine using torch_tensorrt (the Torch-TensorRT frontend). Covers the AOT path (for production and C++ deploy) and the JIT path (for Python-only inference).

When to Use

Scenario Use this skill?
PyTorch nn.Module; want a TensorRT engine without writing an ONNX intermediate Yes
Model uses ops that don't export cleanly to ONNX (custom autograd, dynamic control flow) Yes
Want Torch-TensorRT's automatic fallback of unsupported subgraphs to PyTorch Yes
Need a serialized engine for C++ deploy from a PyTorch source Yes — use AOT path here, then trt-cpp-runtime-quickstart for the C++ load
Have a working ONNX file already No — use trt-onnx-quickstart
LLM token generation (Llama, Mistral, Qwen text gen) No — route to TensorRT-LLM
Have a .plan file already and want to run it from C++ No — use trt-cpp-runtime-quickstart
Migrating an existing weakly-typed TRT network to strongly-typed No — use trt-strong-typing-migration

Prerequisites

Torch-TensorRT versions are tightly coupled to a (torch, tensorrt-cu13, CUDA) triple; mismatches produce ABI errors at import torch_tensorrt. Pin matrix source of truth: the pytorch/TensorRT releases page — each release lists the exact torch / tensorrt / CUDA versions it was built against. Do not invent pins from memory.

For TensorRT 11.0:

Component Required Notes
TensorRT 11.x (tensorrt-cu13 wheel) Mixing -cu12 and -cu13 wheels breaks imports.
CUDA toolkit 13.x Driver R590+.
Python ≥ 3.10 TRT 11 dropped 3.9 and earlier.
torch Match the torch_tensorrt release notes for the chosen torch_tensorrt version.
torch_tensorrt The release matching the TRT 11.0 RC — see pytorch/TensorRT releases.

Recommended environment: NGC TensorRT container with PyTorch (nvcr.io/nvidia/pytorch:<tag> or nvcr.io/nvidia/tensorrt:<tag> with torch_tensorrt pip-installed on top). Run with --gpus all.

Verification:

python3 -c "import torch, torch_tensorrt, tensorrt; print(torch.__version__, torch_tensorrt.__version__, tensorrt.__version__)"

Step 1 — Load and prepare the model

Use a torch.nn.Module in eval mode. Torch-TensorRT will trace through it.

import torch
import torchvision.models as models

model = models.resnet50(weights=None).eval().cuda()
example = torch.randn(1, 3, 224, 224, device="cuda")

Notes:

  • weights=None skips the download — fine for shape/perf testing. For accuracy work, load real weights.
  • Model must be on CUDA before compile. Torch-TensorRT does not move it for you.
  • .eval() matters: BatchNorm and Dropout behave differently in train mode and can produce different engines.

Step 2 — Compile to a TensorRT engine (AOT)

Two compile paths — pick by deployment target:

Goal Path API
Serialized engine (production, C++ deploy, reuse) AOT — this section torch_tensorrt.dynamo.compiletorch_tensorrt.save
In-process Python callable only (no serialized engine) JIT — end of this section torch.compile(backend="torch_tensorrt")

The AOT path uses torch_tensorrt.dynamo.compile and returns a serializable ExportedProgram.

Important — strong typing is the default in torch_tensorrt ≥ 2.12. With use_explicit_typing=True (the default), engine precision is inferred from the exported model's dtype — passing enabled_precisions={torch.float16} raises AssertionError. To compile in FP16, cast the model and example tensor to FP16 before exporting:

import torch
import torch_tensorrt

model = MyModel().eval().cuda().half()                          # cast to FP16
example = torch.randn(1, 3, 224, 224, device="cuda", dtype=torch.float16)

trt_gm = torch_tensorrt.dynamo.compile(
    torch.export.export(model, (example,)),
    inputs=[example],
    # No enabled_precisions — precision comes from the model dtype.
    truncate_double=True,
    min_block_size=1,
)

# Serialize for later / C++ loading — use torch_tensorrt.save, NOT raw bytes.
torch_tensorrt.save(trt_gm, "resnet50_trt.ep", inputs=[example])

Do not extract raw engine bytes via submod.engine and write them with open(...).write(...). That bypasses Torch-TensorRT's metadata wrapper: the blob will not round-trip through torch_tensorrt.load(...) and silently drops the dispatch graph that handles partial-fallback subgraphs. torch_tensorrt.save(trt_gm, path, inputs=...) is the only supported serialization path.

For mixed precision or to override the model dtype, set use_explicit_typing=False and enabled_precisions applies as before (the weakly-typed path, deprecated in TRT 11):

trt_gm = torch_tensorrt.dynamo.compile(
    torch.export.export(model, (example,)),
    inputs=[example],
    use_explicit_typing=False,                # weakly-typed (deprecated)
    enabled_precisions={torch.float16, torch.float32},
    truncate_double=True,
)

Key arguments:

  • truncate_double=True: silently downcasts FP64 constants. Without it, any FP64 op forces a partition boundary.
  • min_block_size: smallest subgraph (in node count) worth handing to TRT. 1 is aggressive; default 5 avoids tiny TRT subgraphs that don't pay back their launch overhead.
  • workspace_size: bytes of scratch memory the TRT builder can use. Leave unset to let TRT decide.

JIT alternative (Python-only inference): if you just want a callable and not a serialized engine, use torch.compile with the Torch-TensorRT backend. Under the strong-typing default, precision comes from the model dtype — .half() the model for FP16 rather than passing enabled_precisions:

model = model.half()  # FP16 inferred from model dtype (strong typing default)
example = example.half()
trt_model = torch.compile(model, backend="torch_tensorrt")
out = trt_model(example)  # compiles lazily on the first call, then runs through TRT in-process

This skips torch.export and gives a torch.compile-wrapped callable. It cannot be loaded from C++ — use the AOT path above if you need that.

Step 3 — Dynamic shapes

Wrap inputs in torch_tensorrt.Input with min_shape/opt_shape/max_shape:

dynamic_input = torch_tensorrt.Input(
    min_shape=(1, 3, 224, 224),
    opt_shape=(8, 3, 224, 224),
    max_shape=(32, 3, 224, 224),
    dtype=torch.float16,
)
trt_gm = torch_tensorrt.dynamo.compile(
    torch.export.export(model, (example,), dynamic_shapes={"x": {0: torch.export.Dim("batch", min=1, max=32)}}),
    inputs=[dynamic_input],
    # Strong typing: FP16 comes from the input/model dtype, not enabled_precisions.
)

Pitfalls:

  • The dynamic_shapes= argument to torch.export.export and the Input ranges must agree. A mismatch produces a builder error 23 minutes into compilation, not at export time.
  • opt_shape is what TRT tunes kernels for. Set it to the most common runtime shape, not the midpoint.
  • Only mark dimensions dynamic that actually vary at runtime; unnecessary dynamism degrades performance.

Step 4 — Run inference and verify

Raw-tensor comparison (assert_close) is fine for FP32 but misleading for FP16 — a few outlier positions exceed the tolerance even when downstream behavior is identical. Use a semantic check appropriate to the model type:

with torch.inference_mode():
    torch_out = model(example)
    trt_out = trt_gm(example)

Image classification — top-1 (or top-5) class match, plus mean-softmax-distance:

assert torch.equal(torch_out.argmax(-1), trt_out.argmax(-1)), "top-1 mismatch"
mean_prob_diff = (torch.softmax(torch_out.float(), -1) - torch.softmax(trt_out.float(), -1)).abs().mean()
assert mean_prob_diff < 1e-3

Sentence / token embeddings — cosine similarity per token, mean across the batch:

cos = torch.nn.functional.cosine_similarity(torch_out.flatten(0, -2), trt_out.flatten(0, -2), dim=-1)
assert cos.mean() > 0.999

Detection / regression heads — bounded raw-tensor closeness on the meaningful sub-tensor (e.g., bbox coordinates), softmax check on the class logits.

FP32 sanity — for any model, an FP32 (no .half()) run should pass torch.testing.assert_close(trt_out, torch_out, rtol=1e-4, atol=1e-4). If that fails, the issue is in the compilation, not numeric precision.

Loading the serialized engine

From Python:

import torch_tensorrt
loaded = torch_tensorrt.load("resnet50_trt.ep").module()
out = loaded(example)

From C++: this skill does NOT cover C++ loading. The .ep file torch_tensorrt.save produces by default is a torch.export archive for the Python torch_tensorrt.load(...) path — it is not directly consumable by the plain TensorRT C++ runtime (IRuntime / deserializeCudaEngine), which expects a serialized TensorRT engine (.plan). Two supported C++ routes:

  • Save with output_format="torchscript" and deploy the TorchScript module with libtorch + the Torch-TensorRT C++ runtime.
  • If you only need the raw TensorRT engine in C++, build it through the ONNX path (trt-onnx-quickstart) and load the resulting .plan with trt-cpp-runtime-quickstart (modern IRuntime + enqueueV3 + setTensorAddress).

Do not inline a C++ snippet here — it will diverge from the canonical pattern.

Common issues

RuntimeError: Trying to create tensor with negative dimension during export — the model has a shape-dependent control flow path that torch.export can't trace. Either rewrite with torch.cond / torch.where, or fall back to ir="dynamo" + min_block_size=1 so the unsupported region runs in PyTorch.

Unsupported operator warnings — Torch-TensorRT partitions around them and runs those nodes in PyTorch. Set require_full_compilation=True to turn this into an error; use during development to find what's actually falling back.

Diagnosing excessive PyTorch fallback

If the compile completes but most of the model fell back to PyTorch (only a few subgraphs run on TRT), three knobs in order:

  1. Identify what fell back. Run with TORCH_LOGS="graph_breaks" and require_full_compilation=True to escalate fallbacks to errors that name the offending op. Without this you don't know what the autotuner rejected.
    TORCH_LOGS="graph_breaks" python3 your_compile.py
    
  2. Reconsider min_block_size. This is a real trade-off — not just a knob.
    • Default min_block_size=5: Torch-TensorRT only hands a subgraph to TRT if it has ≥5 nodes. Tiny TRT subgraphs cost more in kernel-launch overhead than they save.
    • min_block_size=1: aggressive — every supported op goes to TRT, even single-node subgraphs. Useful to see what's theoretically supported, but commonly slower at inference due to the per-subgraph launch tax.
    • Recommended: leave at default 5 for production; drop to 1 only during diagnosis to see maximum TRT coverage.
  3. Look for shape-dependent control flow. torch.export traces only one branch of if x.shape[0] > 0: style code; rewrite with torch.where, torch.cond, or lift the condition out of the model. The trace error names the offending op.

If fallback persists after all three, you have a genuine unsupported op — write a converter (see Torch-TensorRT upstream docs) or switch to the ONNX path (trt-onnx-quickstart).

Engine builds but output is garbage — almost always a dtype issue. Check:

  1. Model/input dtype matches your pipeline — under strong typing precision follows the model dtype (.half() for FP16); enabled_precisions applies only under weak typing (use_explicit_typing=False).
  2. truncate_double=True if the model has any FP64 constants (common in positional encodings).
  3. The model was .eval() before export.

Compile takes >10 minutes — turn on the builder log to see what's going on:

import torch_tensorrt.logging as ttlog
ttlog.set_reportable_log_level(ttlog.Level.Info)

Usually it's the autotuner exploring kernel variants for a heavy GEMM/conv. To cap exploration, set workspace_size smaller (less scratch → fewer candidates).

ImportError: cannot import name 'XYZ' from 'torch_tensorrt' — version mismatch. Confirm the pin matrix; the torch_tensorrt Python API moves between minor versions.

Numerical debugging

If assert_close fails by more than ~5× tolerance:

  1. Re-run in FP32 — drop the model's .half() (or under weak typing, enabled_precisions={torch.float32}). If that matches PyTorch, the issue is FP16 accumulator drift in a specific op — usually a softmax or LayerNorm. The pragmatic fix is to wrap the offending submodule in a module excluded from the compile (min_block_size won't help; explicit exclusion is needed). See the Torch-TensorRT lowering guide for the version-specific exclusion API.
  2. Sanity-check at FP32 first, then re-introduce FP16 and bisect — almost always reveals one offending op.
  3. For FP8: check that the calibration distribution actually covers the runtime distribution. FP8 has no headroom for outliers.

See trt-strong-typing-migration for the broader weak-vs-strong typing discussion. By default Torch-TensorRT produces strongly-typed engines in TRT 11.0 — types inferred from the exported program — but you can override.

What this skill is not

  • Not a guide to writing custom Torch-TensorRT converters. Use the upstream Torch-TensorRT docs for that.
  • Not for TensorRT-LLM / LLM inference — use the TRT-LLM examples/ flow instead.
  • Not for QAT (quantization-aware training) — Torch-TensorRT consumes the QAT model but the training loop is upstream.

References