Repository navigation
Description
This issue tracks LeRobot policies exported to ExecuTorch with its CUDA backend alone (CudaPartitioner, no TensorRT): which ones run on which GPU, how fast, and what each project still needs to look at. Other backends have their own issues: TensorRT in #24353, OpenVINO in #24325, XNNPACK in #24667, Qualcomm in #24668, and Core ML and MLX in #24669.
Coverage
| Policy | H100 | A100 | TensorRT, H100 (from #24353) | TensorRT, A100 (from #24353) | Programs |
|---|---|---|---|---|---|
| act |
|
|
|
|
CUDA 1 |
| cosmos3 |
|
|
|
|
CUDA 3 |
| diffusion |
|
|
|
|
CUDA 3 |
| dm05 |
|
|
|
|
CUDA 1 |
| eo1 |
|
|
|
|
CUDA 1 |
| evo1 |
|
|
|
|
CUDA 1 |
| fastwam |
|
|
|
|
CUDA 1 |
| fineart_vla |
|
|
|
|
CUDA 1 |
| flux3 |
|
❌ | ❌ | ❌ | CUDA 1 |
| g05 |
|
|
|
|
CUDA 3 |
| gaussian_actor |
|
|
|
|
CUDA 1 |
| groot |
|
|
|
|
CUDA 10 |
| lawam |
|
|
|
|
CUDA 1 |
| lingbot_va |
|
|
|
|
CUDA 18 |
| lingbot_vla_v2 |
|
❌ |
|
|
CUDA 1 |
| molmoact2 |
|
|
|
|
CUDA 1 |
| multi_task_dit |
|
|
|
|
CUDA 3 |
| pi0 |
|
|
|
|
CUDA 9 |
| pi05 |
|
|
|
|
CUDA 9 |
| pi0_fast |
|
|
|
|
CUDA 3 |
| smolvla |
|
|
|
|
CUDA 4 |
| tdmpc |
|
|
|
|
CUDA 1 |
| vla_jepa |
|
|
|
|
CUDA 1 |
| vqbet |
|
|
|
|
CUDA 1 |
| wall_oss_05 |
|
|
|
|
CUDA 1 |
| wall_x |
|
❌ |
|
|
CUDA 1 |
| xvla |
|
|
|
|
CUDA 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. -
$\color{#2da44e}{\checkmark}$ and a number: works with torch 2.15 and the ExecuTorch 1.6 build under "Built on". -
$\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. - ❌: measured, and it fails; the cause is in the tables below.
- ⏳: this cell is being measured (running or queued).
"Programs": how many CUDA backend programs the model is split into, not counting the one that draws the noise.
Notes:
- Every row is on the build under "Built on" in both columns, except lingbot_vla_v2: as in TensorRT: LeRobot policies that do not run yet, and what blocks them #24353, it uses the Nov 3 LeRobot branch and transformers 5.5.4, because the new branch cannot export it yet (LeRobot table). Every bundle first passed the export's accuracy check against eager PyTorch.
- cosmos3 (lerobot/Cosmos3-Edge-Policy-LIBERO, bf16) and g05 (lerobot/g05_libero, bf16) also need LeRobot export recipes on top of the build under "Built on"; the
lerobot-export-stackbranch does not have them yet. - flux3 is the frame-conditioned
black-forest-labs/flux-3-action-droid, in bf16. - pi0 and xvla run in bf16, as in TensorRT: LeRobot policies that do not run yet, and what blocks them #24353.
- The 2 TensorRT columns are copied from TensorRT: LeRobot policies that do not run yet, and what blocks them #24353. They use the same checkpoints and dtypes as the CUDA columns, so no cell is left blank. Each bracket compares a backend with eager PyTorch on the same GPU, so a CUDA cell and a TensorRT cell in the same row compare directly only by their milliseconds. "Programs" describes the CUDA columns only.
- The TensorRT cells use the same build as the CUDA cells, plus Torch-TensorRT release/2.15 with [2.15] Pin ExecuTorch 1.6.0 TensorRT#4950 and fix(dynamo): save programs with no inputs, keep attention masks that are not plain causal, fix uint8 shape ops and views left to PyTorch, show the runtime TensorRT build number, keep plugins that run PyTorch out of ExecuTorch programs TensorRT#4953, and TensorRT 11.4. For dm05, TensorRT runs with weight sharing off (
--backend.deduplicate_weights=false). - diffusion, dm05, eo1, fineart_vla, gaussian_actor, lingbot_vla_v2, molmoact2, multi_task_dit, tdmpc, vla_jepa, vqbet and wall_x use random weights, made by the same script and seed as in TensorRT: LeRobot policies that do not run yet, and what blocks them #24353. fastwam uses its real weights.
Built on
One build for both GPUs. Only the lingbot_vla_v2 row uses the older Nov 3 build, as in #24353.
- ExecuTorch: the official 1.6 rc3 wheel for CUDA 13.2 from the PyTorch test index (rc4 is the final 1.6.0, now on PyPI). The Nov 3 build was release/1.6 with the same pull requests applied at the heads we measured. The CUDA fixes in it, merged into release/1.6:
- Fix CUDA mask indexing, CUDA graph outputs and the runtime output-buffer check #24320: mask indexing, CUDA graph outputs, output buffer check (pick: [release/1.6] Fix CUDA indexing, graph execution and output buffers #24488)
- CUDA backend: per-program weights file names and antialiased resize #24324: weights file names, antialiased resize (pick: [release/1.6] Fix CUDA weights filenames and antialiased resize #24489)
- CUDA backend: delete each output handle once when a program returns one tensor twice #24420: output handles deleted once (pick: [release/1.6] Delete duplicate CUDA output handles once #24490)
- CUDA backend: lower fused attention variants #24426: fused attention variants (pick: [release/1.6] Lower fused CUDA attention variants #24491)
- CUDA backend: keep wide attention heads that are not a power of 2 off the Triton SDPA kernel #24468: wide attention heads that are not a power of 2
- CUDA backend: CUDA graph fixes for mask indexing, .item() and changed input shapes, masked copies as where, tensor checks dropped before compiling, float64, _reshape_copy, scatter_reduce and varlen_attn, follow-ups #24326: CUDA graph and lowering fixes,
varlen_attn(pick: CUDA backend: CUDA graph fixes for mask indexing, .item() and changed input shapes, masked copies as where, tensor checks dropped before compiling, float64, _reshape_copy, scatter_reduce and varlen_attn, follow-ups #24512) - CUDA backend: lower rotary embeddings written with complex numbers #24403: complex rotary embeddings (pick: CUDA backend: lower rotary embeddings written with complex numbers #24550)
- Python runtime: enforce CUDA stream selection rules on the stream itself #24346: CUDA stream rules in the Python runtime
- torch 2.15.0, transformers 5.22.1 (5.5.4 in the Nov 3 build). The yellow cells use torch 2.15.0 with the export fixes from [export] Save and load flat_apply calls, such as indexing inside vmap pytorch#202785 applied, as it was on Oct 31. That pull request has since been split into #202785, #202925, #202929 and #202991.
- LeRobot: the
lerobot-export-stackbranch of shoumikhin/lerobot, as it was on Nov 3 at 10:53 AM Pacific (see TensorRT: LeRobot policies that do not run yet, and what blocks them #24353). pi0 and pi05 also map transformers 5.22's SigLIP weight names, as feat(policies): load pi0, pi0.5 and pi0-FAST checkpoints without first building random weights huggingface/lerobot#4775 does. - GPUs: H100, A100 80 GB.
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
- CUDA backend: return an error when an output has no buffer #24640 (C++ abort row) is merged into main, after 1.6.0. Every other pull request in the table below is in ExecuTorch 1.6.0.
- Pick up the rows marked "needs an owner", starting with float32 speed (first row) and export memory (second row).
| Problem | Policies | Fix | Status |
|---|---|---|---|
In float32, bundles are often slower than eager PyTorch. The backend tunes matmuls and convolutions with Triton only (max_autotune_gemm_backends and max_autotune_conv_backends are "TRITON", so the program does not need libtorch), while eager PyTorch calls cuBLAS and cuDNN. In xvla's float32 bundle on the H100, one Triton convolution takes 3.0 ms per call and 30% of the GPU time. |
act, diffusion, fineart_vla, pi0_fast; xvla in float32 (0.3x on both GPUs) | needs an owner (for example, cuBLAS and cuDNN fallback shims, like the existing one for _weight_int4pack_mm) |
Repro ready: 4 float32 convolutions run at 0.56x of eager on the H100 (0.57x on the A100) and 2 linear layers at 0.45x (0.55x), inputs and outputs on the GPU |
| The flux3 export peaks at 83 GiB of GPU memory during AOTInductor's autotuning, so it runs out of memory on the 80 GB A100. The finished program runs in 60 GiB on the H100. | flux3 | needs an owner | Measured |
bf16 F.grouped_mm (aten._grouped_mm) has no kernel choice on the A100 (sm_80), so the export fails with NoValidChoicesError. |
cosmos3 | needs an owner | Repro ready (one F.grouped_mm layer); Inductor's message asks to add ATEN to max_autotune_gemm_backends, the same Triton-only setting as the float32 row. The cosmos3 recipe avoids it by using the fixed domain's weights |
| No kernel for fused attention variants or for complex rotary embeddings. | lingbot_va, fastwam | #24403, #24426 | In 1.6.0 |
| 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 | #24427 | Verified, in 1.6.0 |
No kernel for varlen_attn. |
wall_x | #24326 | Verified, in 1.6.0 |
Same random seed in parallel processes, no bf16 scatter, an extra libgomp dependency, attention reading past the last key. |
all | #24326, pytorch/pytorch#202902 | Verified, in 1.6.0 (the PyTorch part is merged into main) |
No kernel for x[mask], stale shapes from CUDA graphs, a too-small output buffer is accepted. |
smolvla | #24488 | In 1.6.0 |
| Programs in one folder overwrite each other's weights file; antialiased resize does not lower. | all with 2+ programs | #24489 | In 1.6.0 |
From C++, a program whose outputs stay on the GPU aborts the process (from_blob: data pointer cannot be nullptr) when the caller gives no output buffer with Module::set_output. |
C++ callers | #24640 | Verified on the A100: an error instead of an abort; merged into main |
No kernel for scatter with a scalar source in deterministic mode (missing fallback kernels: aoti_torch_cuda_scatter_value_out); the backend has a shim only for a tensor source. |
none yet | needs an owner | Repro ready |
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.
| 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 |
| AOTInductor autotuning runs a gather with a bounds check on made-up inputs, and the compile fails. | vla_jepa (earlier build) | pytorch/pytorch#202931 | Verified |
LeRobot
- Review the open pull requests these cells depend on:
- feat(policies): load pi0, pi0.5 and pi0-FAST checkpoints without first building random weights huggingface/lerobot#4775: pi0 and pi0.5 with transformers 5.22's SigLIP weight names
- fix(policies): avoid tensor value reads that block torch.export in eight policies huggingface/lerobot#5150: 8 policies that pass
torch.export - fix(molmoact2): let single-example inference export with torch.export huggingface/lerobot#5147: molmoact2
- fix(policies): let EO-1, WALL-X and Wall-OSS 0.5 pass torch.export huggingface/lerobot#5162: eo1, wall_x, wall_oss_05
- fix(policies): let LingBot-VLA v2, the LaWAM VLM stage and the GR00T N1.7 backbone pass torch.export huggingface/lerobot#5163: lingbot_vla_v2, LaWAM, GR00T N1.7
- Decide the rows marked "needs a decision".
| 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 |
| 2 bf16 policies miss their 0.075 limit on the A100 by a small margin. wall_x gave 0.095 and 0.047 in 2 exports of the same weights; lingbot_vla_v2 gave 0.078 (0.0625 on the H100). The limit sits at bf16 rounding noise. | wall_x, lingbot_vla_v2 | needs a decision: a limit that allows for bf16 rounding (LeRobot uses 0.2 for 10-step bf16 policies such as pi0) | Measured; not narrowed, so the backend's bf16 kernels are also a candidate |
On the current branch, the lingbot_vla_v2 export stops before tracing: Exporting LingBot-VLA v2 needs a vision_grid. |
lingbot_vla_v2 | in progress: an export recipe | Measured |
Activity
shoumikhin commentedon Nov 5, 2026
Repro pack for the CUDA backend failures in #24684
Every open "needs an owner" or "needs a decision" row in #24684, and every ❌ cell, is below with the real error and a way to reproduce it.
The CUDA backend rows have a small standalone script (under 110 lines each). It needs only torch and the CUDA build of ExecuTorch, and runs in under a minute. Where a script cannot show the problem, there is the LeRobot export command instead.
Summary
F.grouped_mmhas no kernel choice on the A100 (sm_80)flat_apply)wrap_with_autocast)Policy to failure: act, diffusion, fineart_vla, pi0_fast: 1 (slow, not failing). flux3 (A100): 3. wall_x (A100): 5, and 7 on stock torch. lingbot_vla_v2: 6 on the current branch, 5 on the A100. dm05: 7. lawam, molmoact2: 8.
1 and 2 have the same root: the backend asks Inductor for Triton kernels only (
max_autotune_gemm_backends="TRITON"andmax_autotune_conv_backends="TRITON"inCudaBackend.get_aoti_compile_options), so a program never needs libtorch. Its only non-Triton kernels are the fallback shims inget_supported_fallback_kernels(int4 matmul, sort, index, scatter and a few more), and none of them is a matmul or a convolution.Setup
Versions: ExecuTorch 1.6.0 rc3 (the CUDA 13.2 wheel from the PyTorch test index), torch 2.15.0, Python 3.12, Linux x86_64, on an H100 (sm_90) and an A100 80 GB (sm_80).
The test index now serves 1.6.0 rc4 under the same version. The scripts were run on rc3.
LeRobot export commands
The export commands use the
lerobot-export-stackbranch of shoumikhin/lerobot (see #24353) as it was on Nov 3, pinned by theexport-stack-2026-11-03tag, and transformers 5.22.1:Some rows use a randomly initialized checkpoint, since they test the compiler, not the policy.
make_random.pybuilds one:make_random.py
1. float32 matmuls and convolutions are slower than eager PyTorch (act, diffusion, fineart_vla, pi0_fast, xvla in float32; needs an owner)
In float32 (TF32 off on both sides), the Triton kernels that the backend picks are about 2x slower than the cuBLAS and cuDNN kernels that eager PyTorch calls. In the policies this makes float32 bundles slower than eager: xvla in float32 runs at 359 ms per chunk against 105 ms eager on the H100 (742 against 200 on the A100), act at 0.8x, diffusion 0.8x and fineart_vla 0.7x on the H100. In xvla's kernel trace on the H100, one Triton convolution (
triton_tem_fused_convolution) takes 3.0 ms per call and 30% of the GPU time, and the next 4 kernels are Triton matmul templates.Repro (
fp32_speed.py): 4 convolutions, and 2 linear layers, exported and run with inputs and outputs on the GPU, timed against eager PyTorch.Output:
2. bf16
F.grouped_mmhas no kernel choice on the A100 (cosmos3; needs an owner)On the A100, Inductor has no Triton template for
aten._grouped_mmin bf16, and the backend allows no other kind, so the lowering stops. On the H100 the same script lowers. The cosmos3 export recipe works around it by running each step with the fixed domain's projection (x @ W[domain] + b).Error in the policy (cosmos3 on the A100, before the workaround):
Repro (
grouped_mm_bf16.py):Output:
3. The flux3 export runs out of GPU memory during autotuning on the 80 GB A100 (flux3; needs an owner)
The export of
black-forest-labs/flux-3-action-droid(bf16) peaks at 85,320 MiB of GPU memory, during AOTInductor's autotuning. That is more than the A100's 80 GB. On the H100 (95 GB) it exports, and the finished program then runs in 61,694 MiB. The A100 export failed after 86 minutes:Export command:
4. Scatter with a scalar source in deterministic mode (no policy yet; needs an owner)
With
torch.use_deterministic_algorithms(True), Inductor does not generate a Triton kernel forscatterwith a scalar source; it calls the ATen kernel instead, and the backend has no shim for it. The backend has one for a tensor source (aoti_torch_cuda_scatter_src_out), not for a scalar one. With deterministic mode off, the same model lowers. No policy in the table hits it.Repro (
scatter_value_deterministic.py):Output:
5. wall_x and lingbot_vla_v2 checks on the A100 sit at their limit (needs a decision, LeRobot)
2 exports of the same random weights on the A100 gave 0.095 and 0.047 against wall_x's limit of 0.075. The policy runs 10 bf16 flow steps, which LeRobot's rule gives a limit of 0.2 for other policies (pi0, pi0.5, eo1, wall_oss_05, dm05, fineart_vla, molmoact2). On the H100 it passes (0.022).
lingbot_vla_v2 (random weights, the earlier branch head from 6) gives 0.078 on the A100 against the same 0.075 limit, and 0.0625 on the H100. This was not narrowed further, so the backend's bf16 kernels are also a candidate; eager PyTorch on the 2 GPUs was not compared.
Export commands for wall_x (needs torch with pytorch/pytorch#202785, see 7); lingbot_vla_v2 uses the commands in 6:
6. lingbot_vla_v2 does not export on the current LeRobot branch (lingbot_vla_v2; in progress, LeRobot)
On the branch as of Nov 3 at 10:53 AM Pacific, the export stops before tracing. The cells use an earlier head of the branch, with transformers 5.5.4, as #24353 does.
Export commands:
7. torch.export cannot save the program (dm05, wall_x)
Fixed by pytorch/pytorch#202785 (not in torch 2.15). The yellow cells use torch 2.15.0 with it applied.
8. torch.export cannot load the program (lawam, molmoact2)
Fixed by pytorch/pytorch#202925 (not in torch 2.15).