NVIDIA / NVIDIA/TensorRT

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)

Open
#4,853 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Module:Accuracy
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=2 AND 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=0 still 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] (from nvcr.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: correct
  • LayerNorm → 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=0 still fails (max_abs_diff ~22 instead of ~38; batch=2 no longer fully degenerate).
  • Current workaround: build engines for batch=1 only.
Related reports

Happy to provide engine files, layer info dumps or run additional experiments on request.

trt_batch_bug_repro.zip

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.