Skip to content

TensorRT: LeRobot policies that do not run yet, and what blocks them #24353

Open

Description

@shoumikhin

This issue tracks LeRobot policies exported to ExecuTorch with Torch-TensorRT: which ones run on which GPU, how fast, and what each project still needs to look at. CPU backends are tracked in #24325.

Coverage

Policy H100 A100 Thor Orin Nano Programs
act $\color{#2da44e}{\checkmark}$ 2.88 (2.6x) $\color{#2da44e}{\checkmark}$ 4.48 (3.3x) $\color{#2da44e}{\checkmark}$ 8.51 (4.2x) $\color{#2da44e}{\checkmark}$ 26.1 (1.6x) TRT 1
diffusion $\color{#2da44e}{\checkmark}$ 531 (1.6x) $\color{#2da44e}{\checkmark}$ 747 (3.2x) $\color{#2da44e}{\checkmark}$ 1268 (3.5x) $\color{#2da44e}{\checkmark}$ 3107 (1.7x) TRT 3
dm05 $\color{#d4a017}{\checkmark}$ 79.9 (6.8x) $\color{#d4a017}{\checkmark}$ 120 (10.0x) $\color{#d4a017}{\checkmark}$ 184 (3.0x) ➖ TRT 2 + CUDA 1
eo1 $\color{#2da44e}{\checkmark}$ 58.0 (5.3x) $\color{#2da44e}{\checkmark}$ 83.0 (9.3x) $\color{#2da44e}{\checkmark}$ 286 (1.4x) ➖ TRT 2 + CUDA 1
evo1 $\color{#2da44e}{\checkmark}$ 28.8 (6.7x) $\color{#2da44e}{\checkmark}$ 57.1 (7.9x) ➖ TRT 4 + CUDA 3
fastwam $\color{#2da44e}{\checkmark}$ 53.7 (8.2x) $\color{#2da44e}{\checkmark}$ 83.9 (12.2x) $\color{#2da44e}{\checkmark}$ 196 (2.2x) ➖ TRT 2 + CUDA 1
fineart_vla $\color{#2da44e}{\checkmark}$ 105 (2.3x) $\color{#2da44e}{\checkmark}$ 243 (2.6x) $\color{#2da44e}{\checkmark}$ 693 (1.4x) ➖ TRT 1
flux3 ➖
gaussian_actor $\color{#2da44e}{\checkmark}$ 0.57 (4.1x) $\color{#2da44e}{\checkmark}$ 1.14 (4.4x) $\color{#2da44e}{\checkmark}$ 0.62 (3.1x) $\color{#2da44e}{\checkmark}$ 1.38 (4.6x) TRT 1
groot $\color{#2da44e}{\checkmark}$ 19.3 (6.9x) $\color{#2da44e}{\checkmark}$ 28.9 (9.3x) $\color{#2da44e}{\checkmark}$ 58.4 (3.6x) $\color{#2da44e}{\checkmark}$ 200 TRT 10
lawam $\color{#d4a017}{\checkmark}$ 35.8 (5.1x) $\color{#d4a017}{\checkmark}$ 64.5 (9.0x) $\color{#d4a017}{\checkmark}$ 92.2 (2.9x) ➖ TRT 2 + CUDA 1
lingbot_va 🔨 🔨 🔨 ➖
lingbot_vla_v2 $\color{#2da44e}{\checkmark}$ 76.2 (7.3x) $\color{#2da44e}{\checkmark}$ 99.8 (11.3x) ➖ TRT 3 + CUDA 2
molmoact2 $\color{#d4a017}{\checkmark}$ 48.6 (4.3x) $\color{#d4a017}{\checkmark}$ 88.0 (7.1x) $\color{#d4a017}{\checkmark}$ 153 (2.3x) ➖ TRT 1
multi_task_dit $\color{#2da44e}{\checkmark}$ 79.5 (3.7x) $\color{#2da44e}{\checkmark}$ 98.4 (6.3x) $\color{#2da44e}{\checkmark}$ 195 (1.4x) $\color{#2da44e}{\checkmark}$ 484 (2.5x) TRT 3
pi0 $\color{#2da44e}{\checkmark}$ 127 (2.0x) $\color{#2da44e}{\checkmark}$ 272 (2.2x) $\color{#2da44e}{\checkmark}$ 820 (1.4x) $\color{#2da44e}{\checkmark}$ 543 TRT 9
pi05 $\color{#2da44e}{\checkmark}$ 34.1 (5.7x) $\color{#2da44e}{\checkmark}$ 56.3 (7.7x) $\color{#2da44e}{\checkmark}$ 90.0 (2.7x) $\color{#2da44e}{\checkmark}$ 447 TRT 9
pi0_fast $\color{#2da44e}{\checkmark}$ 1589 (1.8x) $\color{#2da44e}{\checkmark}$ 2337 (2.6x) $\color{#2da44e}{\checkmark}$ 11538 (1.1x) ➖ TRT 3
smolvla $\color{#2da44e}{\checkmark}$ 17.6 (10.9x) $\color{#2da44e}{\checkmark}$ 28.5 (14.7x) $\color{#2da44e}{\checkmark}$ 37.8 (4.6x) $\color{#2da44e}{\checkmark}$ 176 (4.3x) TRT 4
tdmpc TRT 1
vla_jepa $\color{#2da44e}{\checkmark}$ 16.9 (7.5x) $\color{#2da44e}{\checkmark}$ 27.9 (12.5x) $\color{#2da44e}{\checkmark}$ 40.0 (2.5x) ➖ TRT 3 + CUDA 2
vqbet $\color{#2da44e}{\checkmark}$ 2.26 (3.3x) $\color{#2da44e}{\checkmark}$ 3.75 (3.7x) $\color{#2da44e}{\checkmark}$ 3.50 (2.0x) $\color{#2da44e}{\checkmark}$ 7.56 (2.8x) TRT 1
wall_oss_05 $\color{#2da44e}{\checkmark}$ 67.3 (5.2x) $\color{#2da44e}{\checkmark}$ 105 (6.8x) $\color{#2da44e}{\checkmark}$ 152 (2.9x) ➖ TRT 2 + CUDA 1
wall_x $\color{#d4a017}{\checkmark}$ 93.5 (5.6x) $\color{#d4a017}{\checkmark}$ 471 (1.4x) ➖ TRT 41 + CUDA 40
xvla $\color{#2da44e}{\checkmark}$ 66.6 (1.6x) $\color{#2da44e}{\checkmark}$ 134 (1.5x) $\color{#2da44e}{\checkmark}$ 402 (1.2x) $\color{#2da44e}{\checkmark}$ 245 (1.3x) TRT 1

Legend:

  • The number: milliseconds to produce one action chunk (batch size 1, median) in lerobot-rollout --bundle.path, with PyTorch never loaded, inputs and outputs on the GPU, and no operator on the CPU.
  • In brackets: how many times faster than eager PyTorch (lerobot-rollout --policy.path) on the same device, checkpoint and dtype, with --tf32=false. No brackets: eager does not fit the device.
  • $\color{#2da44e}{\checkmark}$ and a number: works with torch 2.15 and the ExecuTorch 1.6 and Torch-TensorRT 2.15 fixes listed under "Built on" (some are still in review).
  • $\color{#d4a017}{\checkmark}$ and a number: works only with PyTorch fixes that are not in torch 2.15 (see "PyTorch" below), so it needs the next torch release. Measured with a torch 2.15.0 build that adds them.
  • ➖: the model is too big for this device's memory.
  • 🔨: a fix for this cell is being worked on.
  • ⏳: this cell is being measured (running or queued).
  • Empty: not measured, and no fix in progress.

"Programs": how many TensorRT engines (TRT) and CUDA backend programs (CUDA) the model is split into.

Notes:

  • One stack on all 4 devices (see "Built on"). Every bundle first passed the export's accuracy check against eager PyTorch.
  • Orin Nano: xvla runs in bf16, because the float32 export does not fit.
  • On the Thor, eager with TF32 on is faster than the strict float32 bundles (pi0: 249 against 820 ms).
  • diffusion, eo1, gaussian_actor, multi_task_dit and vqbet use random weights, and so do fastwam, fineart_vla, vla_jepa and wall_x on the Thor.

Built on

One build per architecture from the same sources, used on all 4 devices:

Known issues

"Fix" is an open pull request unless it says merged. "Needs a decision" is a design question for the maintainers. "Needs an owner" means nobody is fixing it yet. "Verified" means the policy was measured before and after the fix.

ExecuTorch

  • Review the open pull requests below. "Built on" shows their release/1.6 picks.
  • Merge the release/1.6 picks.
  • Decide the GIL question and pick up the scatter kernel (last 2 rows).
Problem Policies Fix Status
CUDA backend: no kernel for fused attention that Torch-TensorRT leaves to it, or for complex rotary embeddings. lingbot_va, fastwam #24403, #24426 lingbot_va now compiles
CUDA backend: the Triton attention kernel computes only the first 256 values for wider heads that are not a power of 2, with no error. lingbot_va (CUDA only) #24427 Verified
CUDA backend: no kernel for varlen_attn. wall_x #24326 Verified
CUDA backend: same random seed in parallel processes, no bf16 scatter, an extra libgomp dependency, attention reading past the last key. all with a CUDA program #24326, pytorch/pytorch#202902 Verified
CUDA backend: no kernel for x[mask], stale shapes from CUDA graphs, a too-small output buffer is accepted. smolvla (CUDA only) #24320 In our build
CUDA backend: programs in one folder overwrite each other's weights file; antialiased resize does not lower. all with 2+ CUDA programs #24324 In our build
Python runtime: CUDA stream rules are checked on the wrapper, not on the stream. none (API) #24346 In our build
Python runtime: a backend's log line can deadlock 2 threads, for example 2 TensorRT bundles in one process. 2 bundles in one process #24393 Verified: 0 of 120 runs hang, 26 of 120 before
CUDA backend: no kernel for scatter with a scalar source in deterministic mode. none yet needs an owner Repro
Python runtime: Method.execute holds the GIL, so other Python threads stall while it runs. multi-threaded callers needs a decision: release the GIL during execute? Measured

Torch-TensorRT

  • Review the open pull requests below, then pick them into release/2.15 ([2.15] Pin ExecuTorch 1.6.0 TensorRT#4950 pins ExecuTorch 1.6.0 there).
  • Land the low-memory Jetson pull requests in main and release/2.15 (row near the end).
  • Decide the row marked "needs a decision" and pick up the rows marked "needs an owner".
  • 10 policies export only because some of their ops go to the ExecuTorch CUDA backend next to the TensorRT engines (see "Programs"; wall_x has 41 engines and 40 CUDA programs). It would help if Torch-TensorRT could take these ops, so that each policy builds one engine or a few. The 3 rows before the runtime wheel row list what stays out. fastwam is the exception: its own code copies a tensor to the CPU, which LeRobot has to fix.
Problem Policies Fix Status
index_put for the Qwen3-VL mask writes. groot pytorch/TensorRT#4936 Merged, in release/2.15
A program with no inputs (the noise draw) cannot be saved. all with noise pytorch/TensorRT#4953 Verified
A block-causal attention mask next to complex numbers becomes plain causal: wrong results, no error. fastwam pytorch/TensorRT#4953 Verified on a repro
Views above 8 dimensions reach the CUDA backend as _reshape_copy, which it cannot run. lingbot_vla_v2 pytorch/TensorRT#4953 Verified
A shape op on a uint8 input before a cast fails (Could not get tensor shape). xvla, fastwam pytorch/TensorRT#4953 Verified
An engine with the ScatterAdd plugin cannot load without libtorch. lingbot_vla_v2 pytorch/TensorRT#4953 Verified
On the Thor, 360 scatter plugin layers end up in one compiler region and the build fills host memory. lingbot_vla_v2 pytorch/TensorRT#4953 Verified: 64 to 13 GiB, 32 to 4 min
With CPU offload, complex lowering mixes CPU and CUDA tensors. fastwam pytorch/TensorRT#4953 Verified
A float64 output gets a cast with no metadata, so saving the program fails. lingbot_va pytorch/TensorRT#4953 Verified
The runtime reads an input that is not row-major in the wrong order, with no error. none so far pytorch/TensorRT#4953 Verified on a repro
float32 attention on inputs cast from bf16 does not build. fastwam pytorch/TensorRT#4964 Verified
bf16 attention without an explicit scale loses accuracy (error 0.068, 0.0084 after). eo1, wall_oss_05, wall_x, molmoact2 pytorch/TensorRT#4964 Verified on a repro
Softmax over a size-1 dimension followed by a matmul returns infinity. none so far pytorch/TensorRT#4964 Verified on a repro
Attention with a constant query, key or value does not convert. flux3 pytorch/TensorRT#4964 Verified on a repro
Causal attention with different query and key lengths uses a bottom-right mask, not top-left like PyTorch. none so far pytorch/TensorRT#4964 Verified on a repro
Integer division truncates inside engines (x.shape[0] / 2 at 15 gives 7, not 7.5). not known yet pytorch/TensorRT#4964 Not verified on LeRobot
Each weight read through a transpose is copied once per reader (a TensorRT issue, see below). xvla, eo1 pytorch/TensorRT#4965 (opt-in) A100 compile peak: xvla 20.5 to 11.0 GiB
Constant folding keeps one copy of a weight per reader. evo1 pytorch/TensorRT#4965 A100 export peak 9.8 to 3.3 GiB
Resource partitioning: recursion error on long chains, split tuple outputs, engines over budget. xvla pytorch/TensorRT#4963 Verified
After global partitioning, a PyTorch node cannot find a folded constant (_frozen_param0). none so far pytorch/TensorRT#4966 In our build
Tensors above 8 dimensions (the Qwen-VL image patch layout in the preprocessing) go to the CUDA backend. eo1, lawam, lingbot_vla_v2, vla_jepa, wall_oss_05, wall_x needs an owner (for example, drop size-1 dimensions first) Open
Antialiased resize (_upsample_bilinear2d_aa, _upsample_bicubic2d_aa) has no converter. dm05, evo1, lingbot_vla_v2, vla_jepa, wall_x needs an owner Open
PyTorch's packed variable-length attention (varlen_attn) has no converter: 32 of wall_x's 40 CUDA programs. uint8 tensors made inside the graph also stay out. wall_x, evo1, lingbot_va needs an owner Open
The torch-tensorrt-executorch-runtime wheel, which every TensorRT bundle needs, is on no index, and pip install torch-tensorrt[executorch] does not install it. all needs a decision: how does it ship? Open
Low-memory compile for Jetsons by @cehongwang is not in main or release/2.15 yet: pytorch/TensorRT#4853 (release weights after their engine is built), with #4854 (compile from memory-mapped weights) and #4942 (compile from a weights file) stacked on it, and the pi0.5 example #4949 for release/2.15. It would let pi0, lawam, vla_jepa and evo1 export on the Orin Nano. 4 policies on Orin Nano needs to land in main and release/2.15 Open, no update since Oct 30
flux3_droid runs out of GPU memory in the compile, even alone on a 95 GiB H100. flux3 needs an owner Open

NVIDIA TensorRT

These are bugs in TensorRT itself (11.4 unless noted). Each one reproduces with TensorRT alone; we can share the repros.

  • Triage the rows below. "Workaround" says where Torch-TensorRT avoids one for now.
Problem Policies Workaround Status
The builder keeps a host copy of a weight for every layer that reads it through a transpose: 1 GiB of weights with 10 readers costs 10.9 GiB. xvla, eo1, flow-matching on 8 GB Jetsons pytorch/TensorRT#4965 (opt-in) Repro
With weight sharing on, dm05's RoPE table is one constant read by 290 matmuls, and the engine returns wrong results (0.246, 0.0039 without sharing). dm05 turn sharing off (--backend.deduplicate_weights=false, used for the dm05 cells) Repro in progress
On the Thor (sm_110) only, the fused attention kernel rounds the scores to bf16 whenever the attention has a mask; without a mask, and on H100, A100 and Orin, it keeps float32. evo1 and lingbot_vla_v2 miss their check there (1.65 and 1.10, limit 0.075). evo1, lingbot_vla_v2 none (float32 inputs avoid it but cost 2.2x) Repro
TensorRT 10.16 (JetPack's system TensorRT) returns NaN for bf16 causal attention. JetPack users none Repro ready
float32 attention on inputs cast from bf16 fails to build or crashes. fastwam pytorch/TensorRT#4964 Repro
On the Thor, newer-interface plugin layers end up in one compiler region, and the build grows host memory by 64 GiB (14 to 16 GiB on H100 and A100). lingbot_vla_v2 pytorch/TensorRT#4953 Repro
A constant key read through a transpose is folded wrongly in fp16 and bf16 (error 1.8). constant-key attention pytorch/TensorRT#4964 Repro
The fused attention kernel is 12% slower than unfused for evo1's shape (8 heads, 50 queries, 805 keys, head size 112). evo1 none Measured

PyTorch

These show up only when a program is saved and loaded, or compiled ahead of time.

  • Review the pull requests below. None is in torch 2.15, so the yellow cells need a patched torch until a release has them. Could they go into a 2.15.x patch?
  • Pick up the row marked "needs an owner".
Problem Policies Fix Status
torch.export.save cannot save flat_apply (transformers' mask functions). wall_x, dm05 pytorch/pytorch#202785 Verified
torch.export.load loses metadata on autocast regions that return nothing. lawam, molmoact2 pytorch/pytorch#202925 Verified
A program with varlen_attn cannot be loaded after a plain import torch. wall_x pytorch/pytorch#202925 Verified
A failing check inside a no_grad region leaves grad mode off. none so far pytorch/pytorch#202898 Not verified
AOTInductor autotuning runs a gather with a bounds check on made-up inputs, and the compile fails. vla_jepa (CUDA only) pytorch/pytorch#202931 Verified
If PyTorch first lists its device constructors while transformers has torch.linspace swapped, later linspace calls ignore their device. models built on the meta device needs an owner Found on LeRobot

LeRobot

Problem Policies Fix Status
lerobot-export and the torch-free lerobot-rollout --bundle.path exist only on the lerobot-export-stack branch. all needs a decision: how to bring them upstream in reviewable pieces Open
The export check for lingbot_va cannot work: rounding alone moves it to 0.52 against a limit of 0.2. Checking each program on its own inputs does work (correct programs within 1.85x their noise, broken ones 9x or more). lingbot_va needs a decision: check per program Measured
evo1's check uses 1 test frame where 2 gripper choices are nearly tied, so a correct float32 run fails it (1.48). Checking 16 frames and allowing up to 4 gripper flips passed all 30 correct runs and still caught the Thor bundle. evo1 needs a decision: check 16 frames Measured
lerobot-rollout cannot feed tdmpc its environment state on the test robot, so it has no timing. tdmpc needs a decision Open
The export refuses the flux3 checkpoint, which needs history conditioning. flux3 needs a decision Open
On the Orin Nano, the export loads weights the program never reads (vla_jepa 1.8 GiB, lawam 1.6 GiB) and runs out of memory. lawam, vla_jepa needs an owner Open
evo1 fits the Orin Nano only at TensorRT optimization level 0, which lerobot-export cannot pass yet. evo1 needs an owner Open

Thanks to Adrian Wang (@cehongwang) for the low-memory Jetson path: pytorch/TensorRT#4854 compiles from memory-mapped weights, and pytorch/TensorRT#4949 deploys pi0.5 with ExecuTorch and TensorRT.

cc @Gasoonjia @digantdesai

Metadata

Metadata

Assignees

No one assigned

    Labels

    module: cudaIssues related to the AOTI CUDA backend

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions