Repository navigation
Torch-TensorRT 2.15.0 Linux x86-64 and Windows targets
PyTorch 2.15, CUDA 13.2/13.4, TensorRT 11.3, Python 3.11~3.14
Torch-TensorRT Wheels are available:
x86-64 Linux and Windows:
CUDA 13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT 11.4
- Available via PyPI: https://pypi.org/project/torch-tensorrt
CUDA 13.2/13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT 11.4
- Available via PyTorch index: https://download.pytorch.org/whl/torch-tensorrt
aarch64 SBSA Linux and Jetson Thor and Orin:
CUDA 13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT 11.4
- Available via PyPI: https://pypi.org/project/torch-tensorrt
CUDA 13.2/13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT 11.4
- Available via PyTorch index: https://download.pytorch.org/whl/torch-tensorrt
Torch-TensorRT-RTX 2.15.0 Linux x86-64 and Windows targets
PyTorch 2.15, CUDA 13.2/13.4, TensorRT-RTX 1.6, Python 3.11~3.14
Torch-TensorRT-RTX Wheels are available:
x86-64 Linux and Windows:
CUDA 13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT-RTX 1.6
- Available via PyPI: https://pypi.org/project/torch-tensorrt-rtx
CUDA 13.2/13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT-RTX 1.6
- Available via PyTorch index: https://download.pytorch.org/whl/torch-tensorrt-rtx
Torch-TensorRT-Executorch-Runtime 0.2.1 Linux x86-64 and Windows targets
PyTorch 2.15, CUDA 13.2/13.4, TensorRT 11.3, Python 3.11~3.14
Torch-TensorRT-Executorch-Runtime Wheels are available:
x86-64 Linux and Windows:
CUDA 13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT 11.4
- Available via PyPI: https://pypi.org/project/torch-tensorrt-executorch-runtime
CUDA 13.2/13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT 11.4
- Available via PyTorch index: https://download.pytorch.org/whl/torch-tensorrt-executorch-runtime
aarch64 SBSA Linux and Jetson Thor and Orin:
CUDA 13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT 11.4
- Available via PyPI: https://pypi.org/project/torch-tensorrt-executorch-runtime
CUDA 13.2/13.4 + Python 3.11-3.14 + Torch 2.15 + TensorRT 11.4
- Available via PyTorch index: https://download.pytorch.org/whl/torch-tensorrt-executorch-runtime
New Features:
Highlights
- Efficient Deployment of LeRobot Policies: Compile and run policies from LeRobot without PyTorch at TensorRT speeds using
lerobot-exportand Executorch-TensorRT - ExecuTorch on Windows: compile with Torch-TensorRT, save a
.pte, and run it with ExecuTorch's runtime on Windows x86-64. - Compile and save large models with far less memory: A number of improvements have been made to the host memory / unified memory efficiency of Torch-TensorRT compilation. Some of these features are exposed through a new API
torch_tensorrt.dynamo.low_memory, an experimental API that manages weight and engine streaming to/from disk during compilation to reduce max memory utilization. Now a LeRobot pi0.5 ExecuTorch export peaks at 5GB max utilization. - TorchAO Quantization Support: compile FP8, static FP8, INT4, NVFP4 and MXFP4 models quantized with TorchAO
- Bring your own kernels (experimental):
triton_opandcutile_opturn a Triton or cuTile kernel into a TensorRT plugin in one call, and custom operators that update their inputs in place can be compiled into the engine, bypassing the overhead of running the kernels outside of the engine . - More control over compilation: module-level PyTorch fallback, Global Performance Tuning (sweeps TensorRT builder options for a faster engine), and improvements to torch_tensorrt.autocast.
LeRobot on ExecuTorch + TensorRT: from checkpoint to robot, without PyTorch
Take a trained LeRobot policy from a checkpoint to a compiled, self-checking program on your robot's NVIDIA GPU. With Torch-TensorRT 2.15, ExecuTorch 1.6 and LeRobot 0.7.1, lerobot-export --backend.type=executorch_tensorrt compiles a policy once, ahead of time, into a bundle: a plain folder of ExecuTorch programs with TensorRT engines inside. lerobot-rollout --bundle.path=<bundle> then drives the robot from it, in a process that never imports PyTorch.
.
- Light weight deployment The run-time process never loads PyTorch, so that memory stays free for the model.
- Fit large policies on small boards. pi0, pi0.5, GR00T N1.7, SmolVLA, X-VLA, ACT, Diffusion Policy and more export and run on an 8 GB Jetson Orin Nano
- Currently over 20 Policies Supported SmolVLA, pi0, pi0.5, pi0-FAST, GR00T N1.7, X-VLA, ACT, Diffusion Policy, Multi-Task DiT, VQ-BeT, TD-MPC, FastWAM, EO-1, VLA-JEPA, Wall-OSS and more have export recipes.
- Verification tooling The export replays a test frame through the bundle and through PyTorch and refuses a bundle that disagrees.
lerobot-rolloutreplays it again before the robot connects,lerobot-check-bundleverifies a bundle on the robot's own computer, and a NaN or infinite action stops the run before it reaches the motors. - Serving models
lerobot-policy-servercan serve a executorch-tensorrt bundle from a GPU workstation to a small robot computer to be integrated into greater pipelines.
# Install on the machine that will run the policy (Linux x86-64 or aarch64, CUDA 13)
pip install 'lerobot[export]' 'torch-tensorrt[executorch]>=2.15,<2.16' torch-tensorrt-executorch-runtime
pip install 'cuda-toolkit[nvcc]==13.2.*' # the export compiles CUDA code; JetPack already ships nvcc
export CUDA_HOME=$(python -c "import os, nvidia; print(os.path.join(list(nvidia.__path__)[0], 'cu13'))")
export PATH=$CUDA_HOME/bin:$PATH
# Compile a trained policy into a bundle. The export checks it against PyTorch
# ("Bundle test case: largest difference ... (tolerance ...)") and stops if it disagrees.
lerobot-export \
--policy.path=outputs/train/act_so101/checkpoints/last/pretrained_model \
--backend.type=executorch_tensorrt
# Drive the robot from the bundle: PyTorch is never imported
lerobot-rollout \
--bundle.path=outputs/export/act_executorch_tensorrt \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM0 \
--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \
--duration=30Docs: LeRobot export guide (policies and backends, a Jetson Orin Nano walkthrough, remote serving) · ExecuTorch Deployment guide
ExecuTorch on Windows
Torch-TensorRT 2.15 adds ExecuTorch support on Windows x86-64 (#4835): compile with Torch-TensorRT, save a .pte with output_format="executorch", and run it with ExecuTorch's runtime. All that is required to run compiled .pte files is ExecuTorch (the CUDA build), the TensorRT delegate (the separate torch-tensorrt-executorch-runtime wheel) and TensorRT.
# Linux
pip install "torch_tensorrt[executorch]"
# Windows: PyPI only has CPU builds of PyTorch and ExecuTorch there, so add the PyTorch CUDA index
pip install "torch_tensorrt[executorch]" --extra-index-url https://download.pytorch.org/whl/cu132Docs: ExecuTorch Deployment guide · torch_tensorrt.executorch API reference
TorchAO support for FP8 weight-only, static FP8, symmetric INT4 weight-only, NVFP4 weight-only, and MXFP4 dynamic-activation/weight quantization configurations
Torch-TensorRT 2.15 compiles models quantized with TorchAO through the Dynamo backend. TorchAO's quantize/dequantize ops are preserved through torch.export and converted to TensorRT IQuantizeLayer / IDequantizeLayer, so the engine stores weights as low-precision constants instead of upcasting them at build time. Install with pip install "torch_tensorrt[quantization]" (or pip install torchao).
• FP8 weight-only (Float8WeightOnlyConfig): FP8 weight constants with BF16/FP16 activations; no calibration needed.
• Static FP8: weights and activations are quantized using observer-based calibration, and TensorRT fuses the Q/DQ pairs into FP8 GEMMs. Requires Hopper or newer.
• Symmetric INT4 weight-only: group-wise INT4 with a zero zero-point (TensorRT rejects asymmetric INT4 DQ). Engines keep Int4 weight constants; compile with immutable_weights=True.
• NVFP4 weight-only: packed FP4 (E2M1) weights with FP8 block scales, lowered to a two-level IDequantizeLayer. Weight dims must be divisible by 16; compile with immutable_weights=True.
• MXFP4 (MXDynamicActivationMXWeightConfig): packed FP4 weights with E8M0 block scales (block size 32), with activations kept in BF16. TorchAO has no MXFP4 weight-only config.
INT4, NVFP4, and MXFP4 are currently storage + dequantize paths: they reduce weight memory and engine size, but GEMMs run at the activation precision rather than on native low-bit MMA kernels. These paths require TensorRT ≥ 10.8.
End-to-end examples in examples/dynamo/torchao/ cover a Linear model for every config, FLUX.1 for FP8 weight-only, INT4, NVFP4, and MXFP4, and Qwen3 for INT4. See the Quantization user guide for the workflow and hardware requirements.
In Place Custom Operators
KV-cache writes, running statistics and recurrent state are often updated in place. In the case that these operators are backed by a user supplied kernel, Torch-TensorRT can now compile such a custom operator into a TensorRT engine as a Quick Deployable Plugin and writes the updates in place.
Declare the op with torch.library.custom_op(..., mutates_args=...) and register_fake, then register it with torch_tensorrt.dynamo.conversion.plugins.custom_op.
import torch
import torch_tensorrt
from torch_tensorrt.dynamo.runtime import TorchTensorRTModule
@torch.library.custom_op("demo::scale_add_", mutates_args=("state",))
def scale_add_(state: torch.Tensor, update: torch.Tensor) -> None:
state.mul_(0.9).add_(update) # in place: state = 0.9 * state + update
scale_add_.register_fake(lambda state, update: None) # nothing is returned
torch_tensorrt.dynamo.conversion.plugins.custom_op(
"demo::scale_add_", supports_dynamic_shapes=True
)
class Model(torch.nn.Module):
def forward(self, state, update):
scale_add_(state, update)
return state * 2
state, update = torch.zeros(4, 8, device="cuda"), torch.randn(4, 8, device="cuda")
# min_block_size=1: this graph is smaller than the default block size of 5
trt_model = torch_tensorrt.compile(
Model(), ir="dynamo", inputs=[state, update], min_block_size=1
)
engine = next(m for m in trt_model.modules() if isinstance(m, TorchTensorRTModule))
print("mutated engine inputs:", [i for i, _ in engine.aliased_io.values()])
ref_state = state.clone()
for step in range(3): # the caller's state accumulates across calls
update = torch.randn(4, 8, device="cuda")
out = trt_model(state, update) # TensorRT updates `state` in place
ref_out = Model()(ref_state, update) # eager reference
print(step, torch.allclose(out, ref_out), torch.allclose(state, ref_state))Docs: Plugins tutorial: Mutating Custom Operators
Limits and requirements
- Mutating a module buffer (
register_buffer) is not supported
Custom cuTile and Triton Kernel Support (Experimental)
If your model calls a Triton kernel through a PyTorch custom op, Torch-TensorRT has no converter for it: the op runs in PyTorch and splits the engine in two. torch_tensorrt.kernels.triton_op (#4435) and torch_tensorrt.kernels.cutile_op (#4463) turn a kernel you write into a TensorRT plugin in one call, so the kernel runs inside the engine. One call registers the PyTorch custom op, the Torch-TensorRT converter and a TensorRT plugin (a Quick Deployable Plugin, QDP) that carries the compiled kernel (PTX) inside the engine.
import tensorrt.plugin as trtp
import torch
import triton
import triton.language as tl
import torch_tensorrt
import torch_tensorrt.kernels as ttk
BLOCK_SIZE = 256
@triton.jit
def relu_sq_kernel(x_ptr, n, y_ptr, BLOCK_SIZE: tl.constexpr):
offs = tl.program_id(0) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
x = tl.maximum(tl.load(x_ptr + offs, mask=offs < n), 0.0)
tl.store(y_ptr + offs, x * x, mask=offs < n)
def relu_sq_meta(x: torch.Tensor) -> torch.Tensor:
return torch.empty_like(x)
# One call compiles the kernel to PTX and registers torch.ops.demo.relu_sq, the
# TensorRT plugin and the Torch-TensorRT converter. The grid and n are symbolic
# expressions of the input shape, so one engine serves every batch size from 2 to 32.
ttk.triton_op(
"demo::relu_sq",
kernel=relu_sq_kernel,
signature={"x_ptr": "*fp32", "n": "i32", "y_ptr": "*fp32"},
constexprs={"BLOCK_SIZE": BLOCK_SIZE},
grid=lambda inputs, outputs: (trtp.cdiv(inputs[0].shape_expr.numel(), BLOCK_SIZE),),
meta_fn=relu_sq_meta,
extra_args_fn=lambda inputs, outputs: [trtp.SymInt32(inputs[0].shape_expr.numel())],
)
class Net(torch.nn.Module):
def forward(self, x):
return torch.ops.demo.relu_sq(x - 0.5)
spec = torch_tensorrt.Input(
min_shape=(2, 64), opt_shape=(8, 64), max_shape=(32, 64), dtype=torch.float32
)
# The default min_block_size (5) would leave this two-op graph in PyTorch.
trt_model = torch_tensorrt.compile(Net().cuda().eval(), inputs=[spec], min_block_size=1)
for batch in (2, 5, 32):
x = torch.randn(batch, 64, device="cuda")
torch.testing.assert_close(trt_model(x), torch.relu(x - 0.5) ** 2)
print("TensorRT engine with the Triton kernel matches PyTorch for batch 2, 5, 32")
torch_tensorrt.save(trt_model, "relu_sq.ep", arg_inputs=[spec])Deploying the saved engine needs neither Triton nor the kernel definition:
import sys
import torch
import torch_tensorrt
# No kernel definition and no triton_op(...) call in this process.
loaded = torch_tensorrt.load("relu_sq.ep").module()
for n in (2, 5, 32):
x = torch.randn(n, 64, device="cuda")
torch.testing.assert_close(loaded(x), torch.relu(x - 0.5) ** 2)
print("ran without Triton; op registered here:", hasattr(torch.ops.demo, "relu_sq"))cuTile works the same way: register the kernel, then use the op in a model and compile it as above. It needs cuda-tile>=1.3,<2, cuda-python, a tileiras compiler from CUDA Toolkit 13.2 or newer and a driver that supports CUDA 13.2 or newer.
import cuda.tile as ct
import tensorrt.plugin as trtp
import torch
import torch_tensorrt.kernels as ttk
TILE = 128
@ct.kernel
def reglu_kernel(gate, up, out, tile_size: ct.Constant[int]):
pid = ct.bid(0)
g = ct.load(gate, index=(pid,), shape=(tile_size,))
u = ct.load(up, index=(pid,), shape=(tile_size,))
ct.store(out, index=(pid,), tile=ct.maximum(g, 0.0) * u)
def reglu_meta(gate: torch.Tensor, up: torch.Tensor) -> torch.Tensor:
return torch.empty_like(gate)
# One call compiles the kernel with cuda.tile and registers torch.ops.demo.reglu, the
# TensorRT plugin and the converter. signature lists the arrays (inputs, then outputs);
# constants holds the ct.Constant values; grid is a tile count built from symbolic shapes.
ttk.cutile_op(
"demo::reglu",
kernel=reglu_kernel,
signature={"gate": "fp32", "up": "fp32", "out": "fp32"},
constants={"tile_size": TILE},
grid=lambda inputs, outputs: (trtp.cdiv(inputs[0].shape_expr.numel(), TILE),),
meta_fn=reglu_meta,
)
# From here, use torch.ops.demo.reglu(gate, up) in a model and compile it exactly as
# in the Triton example above.Docs: API reference for triton_op and cutile_op · Plugins tutorial · example: triton_op.py
Limits and requirements
- You supply the grid: give it as a symbolic expression of the input shapes. Both frontends derive the block size (
triton_opfromnum_warps,cutile_opfrom the compiled kernel's.reqntid); onlycutile_opalso derives the extents and strides launch arguments, while fortriton_opyou pass anyi32scalar arguments throughextra_args_fn.meta_fnmust be fully type-annotated. - Validation: the kernel signature, dtypes and compiled launch features are validated at registration. Grid and scalar values are range-checked only when they are constants, so keep symbolic ones within Grid limits for every shape in the profile.
- Known limitations: a Triton kernel that needs more than 48 KiB of dynamic shared memory per block builds but fails when the engine runs (#4877, not yet merged, will make registration reject such kernels with an explicit error); keep block sizes and
num_stagessmall. AOT-compiled Triton kernels get no pointer-alignment specialization, so loads and stores are not vectorized (fix pending in #4875). - Portability: the embedded PTX targets one GPU architecture and one PTX ISA version. Therefore similar to TensorRT, it is important to build on the target platform.
Global Performance Tuning
TensorRT's builder has over 200 internal knobs that can change how fast an engine runs. Global Performance Tuning lets you sweep them, and Torch-TensorRT 2.15 brings it to the TensorRT partitions of Thorch-TensorRT compilations, so you can search for a faster engine without changing the model. get_all_build_routes() (in torch_tensorrt.dynamo) lists the knobs of the installed TensorRT, the build_route setting applies one route, and the tune_build_routes setting sweeps routes (tuning_search: fast, the default, builds the defaults and then varies one knob at a time; full builds every combination; mixed runs fast and then combines only the knobs that helped), timing each on the GPU and, if you set accuracy_threshold, rejecting routes whose output drifts from the eager model. The compile call returns the module built with the fastest route that passes the accuracy check; the winning route is logged at INFO and every trial is in the cache file, so you can pin it later with build_route. Each trial runs in its own spawned Python process, so a TensorRT or driver crash costs one trial. Gains depend on the model, GPU and TensorRT version; re-tune after changing any of them.
import glob, json, os, tempfile
import torch
import torch_tensorrt
from torch import nn
from torch_tensorrt.dynamo import get_all_build_routes, is_global_perf_tuning_available
def main():
assert is_global_perf_tuning_available(), "needs TensorRT >= 11.1 (not TensorRT-RTX, not Windows)"
# allowed_values is already in sweep syntax, e.g. "-kgen:codegen:cuda_tile=[0|1|2|3]"
expr = get_all_build_routes("-kgen:codegen:cuda_tile")["tuner_options"][0]["allowed_values"]
torch.manual_seed(0)
model = nn.Sequential(
nn.Conv2d(3, 32, 3, padding=1), nn.ReLU(), nn.Conv2d(32, 64, 3, padding=1), nn.ReLU(),
nn.AdaptiveAvgPool2d(1), nn.Flatten(), nn.Linear(64, 10),
).eval().cuda().half()
x = torch.randn(8, 3, 64, 64, device="cuda", dtype=torch.half)
cache = os.path.join(tempfile.mkdtemp(), "tune.jsonl")
torch_tensorrt.compile(
model, ir="dynamo", arg_inputs=[x], min_block_size=1,
tune_build_routes=expr, tuning_search="fast", # one build per allowed value
accuracy_threshold=0.01, accuracy_algorithm="cos", # reject routes with 1 - cos_sim > 0.01
tuning_timeout_s=120, tuning_cache_file=cache, # 120 s budget per TRT partition
)
path = glob.glob(cache.replace(".jsonl", ".*.jsonl"))[0] # one cache file per TRT partition
rows = [json.loads(line) for line in open(path)][1:] # line 0 is the sweep header
for r in rows:
print(r["build_route"], "->", f"{r['gpu_time']:.4f} ms" if r["gpu_time"] else r["error_message"])
best = min((r for r in rows if r["gpu_time"]), key=lambda r: r["gpu_time"])["build_route"]
tuned = torch_tensorrt.compile(model, ir="dynamo", arg_inputs=[x], min_block_size=1, build_route=best)
print("best:", best, "| max abs err vs eager:", (tuned(x) - model(x)).abs().max().item())
if __name__ == "__main__": # required: each trial runs in a spawned child that re-imports this file
main()Docs: Performance Tuning guide: Global Performance Tuning · NVIDIA's Global Performance Tuning
Module-level PyTorch fallback for Dynamo Frontend
Keep a particular submodule in PyTorch while the rest of the model compiles to TensorRT. Dynamo compilation now honors torch_executed_modules, with the same convention as the TorchScript frontend, the fully qualified class name (torchvision.models.resnet.BasicBlock; a class defined in your own script is __main__.Gate to Dynamo, where TorchScript uses Gate) (#4639; #4958). Every operation traced from an instance of a listed class, including its nested submodules, stays out of the TensorRT partitions.
import torch
import torch.nn as nn
import torch_tensorrt
class Gate(nn.Module): # a custom layer that should stay in PyTorch
def forward(self, x):
return x * torch.sigmoid(x)
torch.manual_seed(0)
model = nn.Sequential(nn.Conv2d(3, 8, 3, padding=1), Gate(), nn.Conv2d(8, 8, 3, padding=1))
model = model.eval().cuda()
x = torch.randn(1, 3, 16, 16, device="cuda")
trt_model = torch_tensorrt.compile(
model, ir="dynamo", inputs=[x],
min_block_size=1, disable_tf32=True, # toy model: allow one-op engines, compare at full FP32
torch_executed_modules=[f"{Gate.__module__}.{Gate.__qualname__}"],
)
for name, child in trt_model.named_children():
print(name, type(child).__name__)
if name.startswith("_run_on_gpu"):
print(" runs in PyTorch:", [str(n.target) for n in child.graph.nodes if n.op == "call_function"])
print("max abs diff vs eager:", (trt_model(x) - model(x)).abs().max().item())Output:
_run_on_acc_0 TorchTensorRTModule
_run_on_gpu_1 GraphModule
runs in PyTorch: ['aten.sigmoid.default', 'aten.mul.Tensor']
_run_on_acc_2 TorchTensorRTModule
max abs diff vs eager: 1.7881393432617188e-07
Limits and requirements
- Names are strings, in a list, set or tuple:
f"{cls.__module__}.{cls.__qualname__}"for a class you define. A class object or a short name matches nothing; the partitioner logs a "not found in the graph" warning, and withrequire_full_compilation=True, where a matching module raises anAssertionError, a misspelled name gives no message at all, so check the spelling.
Low host memory compilation (Experimental)
Compiling a large model used to hold several copies of its weights in memory at once. That limited compiling models on host memory constrained devices, in particular Jetson and DGX Spark where memory is shared between the CPU and GPU Torch-TensorRT 2.15 lowers the peak memory consumption, some features are automatically applied, Automatic savings and new opt-in options go further when memory is particularly constrained.
New options
- Engines kept on disk until the save:
torch_tensorrt.compile_and_save()(#4863). Compiles anExportedProgramand saves the result as an ExportedProgram (.pt2), an AOTInductor package or an ExecuTorch.ptein one call. Each engine is written to disk as soon as it is built, so finished engines stay out of memory while the rest build, and a.ptesave streams them from disk without loading them. - Weights released once they are built:
release_weights_after_build(#4853). A new compile setting that frees each weight as soon as the last engine that reads it is built, and frees parameters that no partition reads before the first build starts. - Compiling from memory-mapped weights (#4854; experimental, the API will change in 2.16).
save_weights_for_mapping(model, path, source=checkpoint_source(checkpoint))writes the file thatmap_weightsreads from a safetensors checkpoint, one chunk at a time, andtorch_tensorrt.dynamo.tools.map_weights()points a model's weights at that file instead of loading them, so each weight is read from disk only while the engine that uses it builds. - The above features can be used together using the experimental
torch_tensorrt.dynamo.low_memory()(#4854, #4942; experimental, the API may change in 2.16). This API builds the model on as if it was on the virtualmetadevice, maps its weights, and supplies the compile settings that lower memory, passed as**low_mem.compile_settings. - A fixed engine size for the resource partitioner:
max_partition_size(#4855). Sets the weight bytes per TensorRT engine, so a model shards are deterministic. Withcpu_memory_budget, which moves with the process's memory, the same pi0.5 policy split into 27 engines in one run and 13 in another; a fixed 0.5 GiB size gave 16 engines in both. - Shared activation memory in the ExecuTorch runtime:
use_shared_activation_scratch(#4739). A process option of the TensorRT delegate that makes all TensorRT execution contexts on a device share one scratch buffer, so a model split into many engines no longer pays for each engine's scratch separately.
import os
import tempfile
import torch
import torch_tensorrt
from torch_tensorrt.dynamo.tools import save_weights_for_mapping
def mlp(): # 8 x (Linear + ReLU), 32 MiB of float32 weights
layers = []
for _ in range(8):
layers += [torch.nn.Linear(1024, 1024), torch.nn.ReLU()]
return torch.nn.Sequential(*layers).eval()
folder = tempfile.mkdtemp()
weights, program = os.path.join(folder, "mlp.safetensors"), os.path.join(folder, "mlp.pt2")
reference = mlp() # stands in for a trained model
save_weights_for_mapping(reference, weights) # once: write its weights as a safetensors file
x = torch.randn(4, 1024)
# Builds mlp() on the meta device, so it holds no weights, and maps the file into memory.
with torch_tensorrt.dynamo.low_memory(mlp, weights) as low_mem:
# CPU inputs: torch.export reads only shapes, so the weights stay on disk.
exported = torch.export.export(low_mem.model, (x,))
torch_tensorrt.compile_and_save( # each engine goes to disk as soon as it is built
exported,
program,
arg_inputs=[x.cuda()],
max_partition_size=10 << 20, # engines of at most 10 MiB of weights
**low_mem.compile_settings,
)
print(f"{low_mem.report.mapped_bytes >> 20} MiB of weights mapped from disk")
loaded = torch_tensorrt.load(program).module()
print("max |reloaded - eager| =", (loaded(x.cuda()).cpu() - reference(x)).abs().max().item())Limits and requirements
compile_and_save: not supported on Windows. Onlyoutput_format="executorch"avoids loading the engines; for the other formats every engine is loaded once the last one is built, so only the compile phase gets the lower peak. The temporary files (<TMPDIR>/torch_tensorrt_<user>/offloaded_engines) need disk space for potentially all engines concurrently.- Memory-mapped weights: export the model with CPU inputs.
checkpoint_sourceneeds thesafetensorspackage, which Torch-TensorRT does not install (save_weights_for_mappingwithoutsource=andmap_weightsdo not). On Linux the pages are released after each engine builds; on Windows the mapping works but the pages stay resident until the file is unmapped. max_partition_size: a smaller size lowers the peak of each build at the cost of more engines (a weight read by several engines is stored in each). It replacescpu_memory_budget(setting both raisesValueError;cpu_memory_budgetitself is unchanged) and turns resource partitioning on. The size counts weight bytes, not build memory, which has been measured at 3 to 10 times the weights; give it in bytes.use_shared_activation_scratch: off by default, and it affects run-time memory only: set it before loading the programs that should share the buffer, withRuntime.get().backend_registry.set_option("TensorRTBackend", {"use_shared_activation_scratch": True}). Pooled calls never run concurrently on one GPU.
Automatic savings
- Compiling (#4792–#4796). With the C++ runtime, engines are built, set up and refit from TensorRT's serialized plan without copying it, and modules no longer keep a host copy of the plan once the engine is set up (#4794, #4795, #4796). On Linux, the host memory the TensorRT builder freed after each engine build goes back to the OS (#4792), and
offload_module_to_cpu=Trueno longer holds the weights on the GPU and the CPU at the same time (#4793). Together, on a 3 GiB FP16 model (24 ×nn.Linear(8192, 8192), RTX 5090, TensorRT 11.3), the peak host + GPU growth of compiling fell by about 40%, from 4.7× to 2.8× the weight size (2.2× withoffload_module_to_cpu=True), measured over what the process held before compiling, which includes the PyTorch model; once the PyTorch model is deleted, the process holds about 0.2× the weight size more than before compiling, down from 1.7×. - Saving a
.pte(#4821, #4774, #4775, #4864, #4865). A.ptestores each engine once, as named data that identical engines in different methods share, instead of holding two host copies until the file is written (pi0.5 with 13 engines: peak host memory of the export 17.8 → 11.6 GiB, for #4821).torch_tensorrt.saveshares the compiled module's tensors instead of deep-copying them (#4864) and frees each program and its engines when it returns (#4865), so saving several programs in one process no longer stacks their memory (pi0.5's three-program.ptesave: 23.99 → 12.07 GiB host + GPU, then 7.62 → 6.59 GiB). - Resource partitioner (#4963, #4961). With resource partitioning on, long graphs no longer hit Python's recursion limit, a multi-output node stays in the same engine as its outputs, and engines stay within the size budget (#4963). Values larger than 64 KiB that depend only on the model's inputs, such as attention masks and rotary cos/sin, are recomputed inside each engine instead of passed between engines, which avoids Gemma attention engines failing to build on Jetson AGX Thor (#4961).
Limits and requirements
- Saving:
torch_tensorrt.saveshares tensors when it inlines the module without retracing (retrace=False, the default foroutput_format="executorch"), not when you pass your own ExecuTorch partitioners.
Docs: the Resource Management guide covers offload_module_to_cpu, builder trimming and compiling from memory-mapped weights, where it also describes the resource partitioner and release_weights_after_build as the settings low_memory supplies. API references: compile_and_save, low_memory and, on the same page, map_weights and save_weights_for_mapping. max_partition_size is documented in the CompilationSettings reference and in Partitioning: Fixing the Engine Size, and use_shared_activation_scratch in the ExecuTorch guide. Worked examples: low-CPU-memory compilation and compiling from mapped weights.
Fixes and Refinements
Rule-based mixed-precision autocast: reliability fixes and new options
Rule-based autocast (available since 2.10) runs a model in FP16 or BF16 and keeps in FP32 the operations you exclude and the ones an optional calibration run flags next to an out-of-range value, so you do not cast layers by hand. In 2.15 it compiles models that use list-returning operations such as chunk, split and unbind, or integer indices such as nn.Embedding, which failed under autocast before; saved programs no longer carry the calibration data; and it has two new opt-in settings.
Set enable_autocast=True and autocast_low_precision_type (torch.float16 or torch.bfloat16), exclude operations with autocast_excluded_ops, autocast_excluded_nodes or the new autocast_excluded_node_names, and let a calibration run over autocast_calibration_dataloader flag overflow-prone nodes (autocast_max_output_threshold, plus the opt-in reduction-depth check autocast_max_depth_of_reduction).
In FP16 the -1e9 mask fill in the model below becomes -inf, and multiplying it by a zero mask value gives NaN (observed on TensorRT 11.3). With calibration data, every node of this small model touches the -1e9 value, so all of them stay in FP32 and the result matches the FP32 model (the point is that the NaN is avoided); without it, compilation logs a warning and the FP16 engine returns NaN.
import torch
import torch.nn as nn
import torch_tensorrt
from torch.utils.data import DataLoader, TensorDataset
class Scores(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(16, 16)
def forward(self, x, mask):
# the -1e9 fill does not fit in FP16
return torch.softmax(self.linear(x) + (1.0 - mask) * -1e9, dim=-1)
torch.manual_seed(0)
model = Scores().cuda().eval()
x = torch.randn(2, 8, 16, device="cuda")
mask = (torch.rand(2, 8, 16, device="cuda") > 0.3).float()
data = TensorDataset(torch.randn(8, 8, 16, device="cuda"), mask.repeat(4, 1, 1))
for loader in (DataLoader(data, batch_size=2), None): # batches match the export
compiled = torch_tensorrt.dynamo.compile(
torch.export.export(model, (x, mask)),
arg_inputs=[x, mask],
enable_autocast=True,
autocast_low_precision_type=torch.float16,
autocast_calibration_dataloader=loader,
)
error = (compiled(x, mask).float() - model(x, mask)).abs().max().item()
print(f"calibrated: {loader is not None}, max error vs FP32 eager: {error:.1e}")Output:
calibrated: True, max error vs FP32 eager: 4.5e-08
calibrated: False, max error vs FP32 eager: nan
What changed in 2.15:
- Calibration data stays out of saved programs (#4980, #4978). Saved programs and engine-cache entries no longer embed the calibration dataloader or its inputs (in one measured case a saved program was 2.96 MB with the calibration set embedded and 160 KB without), an unpicklable dataloader no longer fails compilation, and refit after loading reuses the recorded precision decisions.
- Operator exclusions accept ATen op objects (#4980).
{torch.ops.aten.softmax.int}used to fail with a pickling error; strings already worked. - List-returning operations such as
chunk,splitandunbindwork (#4980, #4978). They used to fail with'list' object has no attribute 'to'. - Integer indices, bool masks and factory operations are no longer cast (#4978). Models with
nn.Embeddingused to fail under autocast, and an integer comparison could give a silently wrong mask. - Missing calibration is reported (#4980). The output-range and reduction-depth checks need
autocast_calibration_dataloader; without it they are skipped and compilation logs a warning. - New opt-in settings (#4995).
autocast_excluded_node_nameskeeps nodes in FP32 by graph node name, andautocast_model_regions="reclassify"lets the rules choose precisions inside the model's owntorch.autocastblocks; this overrides choices of the model's author, so check accuracy. - Calibration uses far less GPU memory (#4995). It keeps one maximum per node instead of every output, with no setting needed (peak extra GPU memory during calibration of four Linear + ReLU layers over 128 batches of 64 × 1024: 448.2 → 1.8 MiB).
Docs: Compile Mixed Precision models with Torch-TensorRT · CompilationSettings reference
Limits and requirements
- Matching:
autocast_excluded_nodesis a regex against module paths such asblock.norm, andautocast_excluded_node_namesa regex against graph node names; both usere.match, so they must match from the start of the name (normdoes not matchblock.norm,.*norm$does) - The checks: the output-range check flags a node when one of its inputs, or the output of a node that consumes it, exceeded
autocast_max_output_threshold(default 512), so a node that itself produces a value beyond the FP16 range is only protected when one of its inputs or consumers is also out of range: check the output for inf and NaN. The reduction-depth check only handles matmul, mm, bmm, dot, mv, attention and convolution nodes and cannot see model inputs or weights, so it never flags annn.Linear(aten.linear) or annn.Convlayer. - Calibration batches must be tuples or lists with one tensor per positional input, on the model's device, with exactly the exported shapes (use
drop_last=True). - Autocast applies on the exported-program path (
torch_tensorrt.dynamo.compile,torch_tensorrt.compile(ir="dynamo"), refit);torch.compile(backend="torch_tensorrt")ignores it, and models withtorch.condare rejected.MutableTorchTensorRTModule.savestill stores the dataloader.
Tensor-parallel Llama and Qwen export with static KV cache
This release adds an end-to-end multi-GPU workflow for exporting, saving, and loading tensor-parallel Llama and Qwen models as one serialized TensorRT engine per rank. The example supports FP16 and FP32 execution, KV caches, dynamic sequence profiles, and preserves symbolic bounds for scalar cache indices across save/load. Reproducible prefill/decode benchmark support is provided.
What's Changed
- fix(tests): repair two ExecuTorch save() tests broken by production changes by @Conarnar in #4556
- upgrade torch-tensorrt from 2.14.dev to 2.15.dev by @lanluo-nvidia in #4493
- ci: stop gdb --batch from masking native runtime crashes by @shoumikhin in #4552
- ci: stop the uv.lock job from building the project by @shoumikhin in #4551
- fix python only artifacts naming conflict issue by @lanluo-nvidia in #4569
- upgrade tensorrt 11.1 to 11.2 by @lanluo-nvidia in #4491
- feat(executorch): copy-back for non-KV mutable buffers in the TensorRT delegate by @Conarnar in #4459
- fix: fix the numpy related CI failure issue by @bowang007 in #4535
- Disable specific test due to bug in trt-rtx by @SandSnip3r in #4571
- ci: check the ExecuTorch build job installed a wheel with the C++ runtime by @shoumikhin in #4554
- feat: add aten.glu converter by @zupengwang in #4475
- fix: make the packaged ExecuTorch reference runner run TensorRT delegated models by @shoumikhin in #4573
- ci: fix the ExecuTorch runtime artifact name collapsing to one name by @shoumikhin in #4553
- fix(dynamo): gate refittable cumsum on RTX version by @SandSnip3r in #4575
- fix(executorch): register device copy kernels in the packaged runner by @shoumikhin in #4581
- style: format test_cumsum_aten.py with black by @shoumikhin in #4582
- fix(ci): build the Windows download artifact name in the right order by @shoumikhin in #4578
- fix(executorch): share the C++ runtime instead of shipping a second copy by @shoumikhin in #4592
- fix(dynamo): keep
x & Falseandx | Trueout of TensorRT by @shoumikhin in #4579 - fix(dynamo): keep the complex decomposition inside the TRT op set by @shoumikhin in #4580
- fix(dynamo): stop unpacking complex inputs before the refit output check by @shoumikhin in #4583
- fix(runtime): give the TensorRT engine class an eq by @shoumikhin in #4584
- fix(tests): compare baddbmm lowering against TensorRT in fp32, not TF32 by @shoumikhin in #4586
- fix(tests): download the shared torchvision checkpoints before pytest forks by @shoumikhin in #4587
- fix: engines default to no runtime cache; the module owns the implicit one by @tp5uiuc in #4482
- [fix] MD-TRT: rebind NCCL communicator after execution context invalidation by @apbose in #4464
- fix: resovle the CI failures when driver versions mismatch for kernel… by @bowang007 in #4638
- fix(executorch): drop the unused cuRAND link from the CUDA shims by @shoumikhin in #4653
- Fix brittle layer count by @jloftin-nv in #4576
- ci(executorch): run a coalesced TensorRT + CUDA program in the reference runner gate by @shoumikhin in #4572
- Lift each constant once when exporting a partitioned graph by @shoumikhin in #4642
- bring back cu126 support by @lanluo-nvidia in #4635
- fix: add CUDA 13 support for TRT-LLM plugins by @apbose in #4549
- skip hf_gqa in windows and fix platform serialization in windows by @lanluo-nvidia in #4663
- update nv pytorch windows on arm url by @lanluo-nvidia in #4669
- add executorch aarch64 release build by @lanluo-nvidia in #4674
- fix(dynamo): remap engine output symbols during fake execution by @narendasan in #4627
- fix(dynamo): normalize scalar values at engine boundaries by @narendasan in #4628
- fix(dynamo): preserve explicit device placement across partitions by @narendasan in #4629
- fix(dynamo): make truncate_double metadata match engine precision by @narendasan in #4630
- fix(dynamo): handle dynamic scatter and index updates by @narendasan in #4631
- cherry pick from release/2.14 branch back to main by @lanluo-nvidia in #4687
- Short-circuit index_put with a statically-empty index by @apbose in #4641
- fix select empty tensor squeeze dgx spark by @apbose in #4695
- fix(dynamo): fall back to PyTorch for nonzero on TensorRT-RTX by @tp5uiuc in #4692
- upgrade to tensorrt 11.3 by @lanluo-nvidia in #4713
- Cast/normalize attn_bias in scaled_dot_product_efficient_attention by @apbose in #4636
- remove cu12* support by @lanluo-nvidia in #4716
- Fix TopK rank-1 by @jloftin-nv in #4508
- fix(runtime): support NCCL2 communicator binding by @apbose in #4661
- Fix empty aten::select constant: addConstant needs null weights by @apbose in #4720
- Track the ExecuTorch pin and move it to new nightlies automatically by @shoumikhin in #4566
- fix: TensorRT-RTX Turing (SM 7.5) capability guards and compute-capability targeting by @tp5uiuc in #4643
- fix: guard the 3D convolutions the pad-folding pass hides from the conv guard by @tp5uiuc in #4644
- fix: guard the GEMM the cdist converter emits on Turing by @tp5uiuc in #4645
- fix: guard the FP32 GEMMs that reach TensorRT-RTX through linear and attention by @tp5uiuc in #4646
- add license header by @lanluo-nvidia in #4722
- Skip adjacency partitioning when the graph is fully supported by @micwill755 in #4530
- fix(fx): drop the dynamic_range argument from the 8 remaining mark_as_int8_layer calls by @Anai-Guo in #4651
- dryrun stats observable by @jloftin-nv in #4519
- Ship only the TensorRT delegate in the ExecuTorch runtime wheel by @shoumikhin in #4567
- Added TorchAO FP8 weight-only and static quantization + user guides by @micwill755 in #4589
- Added TorchAO INT4 weight-only quantization + Linear, FLUX, and Qwen examples by @micwill755 in #4590
- Added TorchAO NVFP4 weight-only quantization + Linear and FLUX examples by @micwill755 in #4593
- Added TorchAO MXFP4 (MXDynamicActivationMXWeightConfig) + Linear and FLUX examples by @micwill755 in #4595
- Added dynamo-torchao test suite under the full CI lane by @micwill755 in #4601
- create the trt_api test suite for tensorrt api level test case by @lanluo-nvidia in #4715
- add license header to the files by @lanluo-nvidia in #4726
- Make three more checks fail when the thing they check is gone by @shoumikhin in #4725
- Fix/converter dtype promotion by @dongwoonhyun in #4520
- Fix complex rewrite device mismatch by @jloftin-nv in #4513
- Move the ExecuTorch pin forward, and let it keep moving by @shoumikhin in #4728
- Borrow the device through the shared CUDA guard by @shoumikhin in #4729
- fix(ci): make the lockfile update reproducible and actually checked by @shoumikhin in #4743
- fix: handle empty one-dimensional tensors in concatenation by @shoumikhin in #4701
- fix(ci): keep the ExecuTorch pin out of the workflow files by @shoumikhin in #4748
- perf(executorch): opt-in shared per-device activation-scratch pool by @shoumikhin in #4739
- feat(executorch): let a TensorRT engine write the caller's KV cache in place by @shoumikhin in #4740
- add python-only executorch export support by @lanluo-nvidia in #4736
- fix: reject unsupported operators when full compilation is required by @shoumikhin in #4707
- fix: accept host-resident shape-tensor inputs without a device round trip by @SrivastavaKshitij in #4718
- feat: report operators that fall back to PyTorch by @shoumikhin in #4710
- fix: skip const-fold for
arangefor attention mask by @fs-eire in #4452 - Fix slice_scatter open end by @jloftin-nv in #4512
- fix(dynamo): decide MutableTorchTensorRTModule LIVE from weight snaps… by @cehongwang in #4664
- fix: optimize attention decomposition by @fs-eire in #4448
- fix: add the missing f-string prefix to two diagnostic messages by @Anai-Guo in #4686
- fix: support changing input shapes in log1p by @shoumikhin in #4702
- fix(ci): repair TorchAO nightly tests by @micwill755 in #4750
- fix: preserve integer precision in cumulative sums by @shoumikhin in #4706
- fix: bind a scalar engine output at rank 0 by @SrivastavaKshitij in #4681
- fix: name the engine and the binding in execute_engine failures by @SrivastavaKshitij in #4682
- Build the docker image on an OSDC runner by @huydhn in #4746
- Move the containerised Linux jobs to OSDC runners by @huydhn in #4744
- fix: count unsupported operators with side effects by @shoumikhin in #4703
- fix: support dynamic inputs in clone and _to_copy by @shoumikhin in #4705
- fix: keep random operations in PyTorch by @shoumikhin in #4711
- fix: Dynamo compile support for None outputs by @dongwoonhyun in #4640
- fix(ci): skip TorchAO INT4 test when Triton is unavailable by @micwill755 in #4758
- fix partition smoke test failures by @lanluo-nvidia in #4754
- fix(tests): expect the TensorRT-RTX refusal reason for nonzero by @shoumikhin in #4769
- fix(executorch): create the TensorRT execution context once by @shoumikhin in #4767
- fix: support dynamic shapes in gather by @shoumikhin in #4704
- fix: run select with a computed index by @shoumikhin in #4709
- fix: preserve results when evaluating constant tensor operations by @shoumikhin in #4708
- fix(runtime): keep other outputs correct when an engine output is empty by @shoumikhin in #4771
- fix(executorch): share tensors when copying a program for preprocess by @shoumikhin in #4775
- Fix strided slice_scatter KV cache updates by @apbose in #4550
- fix(executorch): copy the engine once when building the delegate blob by @shoumikhin in #4774
- fix: correct misleading fallback reporting and full-compilation error by @shoumikhin in #4751
- fix(tests): keep the empty engine output test inside one engine by @shoumikhin in #4776
- fix(dynamo): preserve int64 gather and scatter indices by @apbose in #4023
- feat: support Global Performance Tuning by @zewenli98 in #4476
- Drop cu130 from the CUDA build matrix by @shoumikhin in #4777
- test(dynamo): cover bool tensor multiplication by @apbose in #4764
- feat: add triton_op for AOT QDP plugins from Triton kernels by @bowang007 in #4435
- fix: keep PyTorch-executed weights on the device after an offload by @tp5uiuc in #4647
- fix: order the placeholders first before computing serde range constraints by @tp5uiuc in #4648
- feat: apply_runtime_settings — permanent runtime config for AOT-loaded engines by @tp5uiuc in #4570
- feat: add cutile_op for AOT QDP plugins from cuda tile kernels by @bowang007 in #4463
- Fix mutable state leakage from folded constructors by @micwill755 in #4501
- Fix noncontiguous AOT inputs by @jloftin-nv in #4509
- Fix CPU-only import by @jloftin-nv in #4504
- associative_scan lowering by @jloftin-nv in #4518
- MLA attention fallback by @jloftin-nv in #4517
- MoE histc / _grouped_mm by @jloftin-nv in #4514
- feat: support mutating QDP plugins and preserve input writeback by @narendasan in #4761
- Raise when TensorRT enqueueV3 fails by @micwill755 in #4662
- feature: Dynamo implementation of torch_executed_modules by @dongwoonhyun in #4639
- fix(ci): repair main lint failures by @apbose in #4787
- Update ExecuTorch pin to 1.6.0.dev20261005 by @pytorchbot in #4753
- fix(executorch): initialize a CUDA context for default-stream execution by @shoumikhin in #4779
- feat(executorch): share TensorRT engines across loads by @shoumikhin in #4778
- fix: run and convert engines that have uint8 or FP8 bindings by @shoumikhin in #4782
- Build the Linux wheels on OSDC runners by @huydhn in #4745
- fix(converters): support symbolic repeat counts on static inputs in tile by @kodlan in #4747
- fix(converters): embedding_bag crashes when per_sample_weights is a runtime tensor by @kodlan in #4770
- Fix cumsum dtype by @jloftin-nv in #4511
- complex _reshape_copy / _to_copy by @jloftin-nv in #4516
- fix(dynamo): harden generated optimization profiles by @apbose in #4765
- fix windows mslk cu134 quantization by @lanluo-nvidia in #4789
- fix(converters): embedding_bag returns zeros for the last bag with dynamic indices and constant offsets by @kodlan in #4785
- chore: add moe tests by @zewenli98 in #4262
- Run torch-compile-backend on the g5.8xlarge pod by @huydhn in #4808
- remove python 3.10 support by @lanluo-nvidia in #4791
- cumsum dtype issue fix by @lanluo-nvidia in #4799
- Grant the Linux wheel builds access to the release runner groups by @huydhn in #4798
- Update ExecuTorch pin to 1.6.0.dev20261008 by @shoumikhin in #4822
- perf(dynamo): return builder heap to the OS after each engine build by @narendasan in #4792
- perf(dynamo): offload modules to CPU without holding both copies by @narendasan in #4793
- fix(executorch): load TensorRT engines through ExecuTorch's CUDA allocator by @shoumikhin in #4780
- embedding_bag: remove the precision since it is no longer supported by @lanluo-nvidia in #4823
- fix import bertmodel issue due to mslk in windows by @lanluo-nvidia in #4825
- feat(executorch): optional CUDA graph replay in the TensorRT delegate by @shoumikhin in #4781
- fix constant folding test issues by @lanluo-nvidia in #4800
- 2.15 release branch cut by @lanluo-nvidia in #4832
- fix the mla attention fallback issue by @lanluo-nvidia in #4836
- cherry pick 4837: upgrade executorch pin by @lanluo-nvidia in #4838
- remove cu130 in the release build. by @lanluo-nvidia in #4840
- [2.15] Backport #4784: Llama and Qwen export TP examples with KV cache by @apbose in #4874
- fix nspec scan vulnerbility by @lanluo-nvidia in #4937
- fix transformers scan issue by @lanluo-nvidia in #4938
- fix TestEngineSetupMemory.test_setup_does_not_copy_the_plan in dyanmo… by @lanluo-nvidia in #4947
- [2.15] Bump the ExecuTorch delegate to 0.2.1 by @shoumikhin in #4909
- skip the mslk test for now due to the incompaitible issue with torch by @lanluo-nvidia in #4951
- [2.15] docs(plugins): compile the mutating custom operator example with min_block_size=1 by @cehongwang in #4955
- [2.15] Cherry-pick #4966, #4964, #4963 and #4965 by @micwill755 in #4969
- [2.15] Pin ExecuTorch 1.6.0 by @shoumikhin in #4950
- [2.15] example: deploy LeRobot's pi0.5 policy with ExecuTorch and TensorRT by @cehongwang in #4949
- fix enabled_features not imported by @lanluo-nvidia in #4992
- skip gqa test due to gqa accuracy issue remain unresolved in rtx by @lanluo-nvidia in #4994
- change woa torch from oot nightly to oot by @lanluo-nvidia in #4993
- [2.15] Fix TorchAO example failures from the rc3 sanity check by @micwill755 in #4973
- fix kv cache export test by @lanluo-nvidia in #5004
- fix windows issue by @lanluo-nvidia in #5006
New Contributors
- @Anai-Guo made their first contribution in #4651
- @dongwoonhyun made their first contribution in #4520
- @SrivastavaKshitij made their first contribution in #4718
- @kodlan made their first contribution in #4747
Full Changelog: v2.14.0...v2.15.0