TensorRT silently produces wrong results for batch > 1 on SegFormer (b0/b1/b2 verified, fp32, static and dynamic engines, TRT 10.11 & 11.1, sm89 + sm120)
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 13.4k
- Forks
- 2.4k
- Avg merge
- 5d 3h
- Merged PRs (30d)
- 2
Description
Summary
TensorRT silently produces wrong results for batch > 1 when compiling SegFormer models. This was verified with all three public checkpoints nvidia/segformer-b0/b1/b2-finetuned-ade-512-512 and a reduced single encoder block of the b2 variant. Batch=1 is bit-accurate against ONNX Runtime; batch=2 is wrong by orders of magnitude while trtexec reports PASSED. The bug is:
- precision-independent (fp32 AND fp16 engines affected),
- shape-mode-independent (fully static batch=2 engines
min=opt=max=2AND dynamic 1–2 engines affected), - architecture-independent (reproduced on sm_120 Blackwell laptop and sm_89 L4),
- present in TensorRT 10.11 (10.11.0.x) and 11.1 with identical error values,
- not affected by fusion level (
--builderOptimizationLevel=0still fails, though less catastrophically).
ONNX Runtime (CPU and CUDA EP) executes the same ONNX with batch=2 correctly on the same GPUs.
Environment
- TensorRT 10.11 —
trtexec [TensorRT v101100](CUDA 12) and TensorRT 11.1 —trtexec [TensorRT v110100](fromnvcr.io/nvidia/tritonserver:26.07-py3) - GPUs: NVIDIA RTX PRO 2000 Blackwell Generation Laptop (sm_120), driver 595.84; NVIDIA L4 (sm_89)
- ONNX: opset 19, exported with
torch.onnx.export(legacy TorchScript path) +onnxsim, dynamic batch axis - Model:
nvidia/segformer-b2-finetuned-ade-512-512(public), 512×512 NHWC input, fp32
Symptom (full public models)
Build a plain fp32 engine with dynamic batch 1–2 and run the same random input as batch=1 and as b2[0] of a batch=2 request. The batch=2 input's first sample is identical to the batch=1 input, so a correct engine must produce identical outputs:
| full model (512×512, fp32) | b2[0] vs b1, TRT 10.11 | b2[0] vs b1, TRT 11.1 | argmax match b2[0] vs b1 |
|---|---|---|---|
segformer-b0-finetuned-ade-512-512 |
9.94 (34% of out magnitude) | not tested | — |
segformer-b1-finetuned-ade-512-512 |
11.35 (37%) | not tested | — |
segformer-b2-finetuned-ade-512-512 |
19.2 | 19.2 | 49.2% |
ORT CUDA EP: b2[0] vs b1 max_abs_diff = 0.018, argmax match = 100 %
ORT CPU : batch=2 bit-identical to batch=1
The segmentation output is essentially garbage for batch≥2 (with a fine-tuned SegFormer variant at 1024×1024, batch=2 collapsed to all-background). The b0/b1/b2 checkpoints above were tested via generate/export_public.py from the attached zip, which regenerates each ONNX and the input files from the HuggingFace hub.
Bisection
Prefix-bisection (cut the graph after every encoder block, compare TRT vs ORT at batch=2, fp32):
| prefix cut | b2 max_abs_diff vs ORT |
|---|---|
| patch embeds + stage-0 blocks (0.0–0.2) | ~0.002–0.007 (noise) |
| block.1.0 end | 0.62 (first corruption) |
| block.1.1 end | 1.57 |
| … monotonically growing downstream … | … |
| full model | ~19 |
Node-level cuts inside block.1.0 show: everything up to the mlp/dense1 output is clean at batch=2; the first wrong values appear at the mlp/dwconv (3×3 depthwise conv, groups=512) output (diff 3.1 vs fp32 noise 0.004).
However, the dwconv alone does not reproduce the bug:
- single
Conv(same weights, shapes, real activations): correct at batch=2 dense1 → transpose → reshape → dwconv: correctLayerNorm → dense1 → (dynamic Shape/Gather/Concat reshape chain) → dwconv: correct
Only the complete block (LayerNorm → efficient attention → residual/LN → MLP with dwconv, 64 nodes, 1.9 MB) reproduces. Ending the same graph at dense1 (removing the dwconv) heals it. So the broken kernel/tactic is only selected in the full-block compilation context.
Minimal repro (attached, trt_batch_bug_repro.zip)
repro.onnx — one SegFormer-b2 encoder block, fully synthetic weights (seeded RNG), no third-party data. Input x: [B, 4096, 128], output y: [B, 512, 64, 64]. The batch=2 input file's first sample is identical to the batch=1 input, so a correct engine must produce identical outputs — no reference model needed:
trtexec --onnx=repro.onnx --saveEngine=repro.engine \
--minShapes=x:1x4096x128 --optShapes=x:2x4096x128 --maxShapes=x:2x4096x128
trtexec --loadEngine=repro.engine --shapes=x:1x4096x128 \
--loadInputs=x:repro_input_b1.bin --exportOutput=out_b1.json \
--iterations=1 --warmUp=0 --duration=0
trtexec --loadEngine=repro.engine --shapes=x:2x4096x128 \
--loadInputs=x:repro_input_b2.bin --exportOutput=out_b2.json \
--iterations=1 --warmUp=0 --duration=0
python3 verify.py out_b1.json out_b2.json
Measured max_abs_diff(out_b2[0], out_b1[0]) — identical input, same engine:
| TRT 10.11 (v101100) | TRT 11.1 (v110100) | |
|---|---|---|
repro.onnx (synthetic weights) |
0.0133 | 0.0133 |
repro_public_weights.onnx (public SegFormer weights) |
3.105 | 3.105 |
For comparison: ONNX Runtime CPU executes batch=2 bit-identical to batch=1,
and a correct fp32 engine gives ~1e-6 for the same comparison. Every trtexec
run prints &&&& PASSED.
Additional observations
- Same failure through Triton Inference Server (26.07 / TRT backend), so it is not trtexec-specific.
- Two identical images in the batch produce two identical wrong outputs (deterministic miscompile, not input mixing).
- With the 1024×1024 fine-tuned SegFormer variant we additionally verified: fp16 AND fp32 fail; static batch=2 (
min=opt=max=2) fails;--builderOptimizationLevel=0still fails (max_abs_diff ~22 instead of ~38; batch=2 no longer fully degenerate). - Current workaround: build engines for batch=1 only.
Related reports
- #4333 (ViT attention miscompile on L4, internally tracked)
- #4837 (silently wrong fused MHA outputs on sm_120)
- https://forums.developer.nvidia.com/t/319274 (MaxViT: batch>1 wrong, fp32, TRT 10.7, unresolved)
- https://forums.developer.nvidia.com/t/361820 (SegFormer-b2: ONNX vs TRT huge differences — same symptom, closed by reporter without root cause)
Happy to provide engine files, layer info dumps or run additional experiments on request.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Extract the attached trt_batch_bug_repro.zip and start with repro.onnx, the trtexec commands, and verify.py to reproduce the batch-2 mismatch. Compare the complete encoder block with the isolated dwconv and the bisection results around mlp/dwconv. Done means batch-2 output for the identical first sample matches batch-1 across the reported engine configurations without silently passing incorrect results.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- machine-learning, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 28/100