Skip to content

[https://nvbugs/6405760][fix] Do not load the multimodal encoder for text-only trtllm-bench runs - #16250

Merged
thorjohnsen merged 4 commits into
NVIDIA:mainfrom
thorjohnsen:fix/nvbug6405760-skip-mm-encoder
Jul 15, 2026
Merged

[https://nvbugs/6405760][fix] Do not load the multimodal encoder for text-only trtllm-bench runs#16250
thorjohnsen merged 4 commits into
NVIDIA:mainfrom
thorjohnsen:fix/nvbug6405760-skip-mm-encoder

Conversation

@thorjohnsen

@thorjohnsen thorjohnsen commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Description

Fixes the qwen3.5_9b L40S perf regression tracked in NVBug 6405760 / TRTLLM-13979 that persisted after the deep_gemm import-context fix (#15632).

Root cause: since #15249 (dense) and #14599 (MoE), Qwen3.5 checkpoints with arch Qwen3_5ForConditionalGeneration / Qwen3_5MoeForConditionalGeneration resolve to the VLM wrapper, which unconditionally instantiates and loads the vision tower (~0.86 GiB for Qwen3.5-9B) — also for text-only serving. The KV cache pool is sized from free GPU memory, so on L40S it shrank from 22.55 to 21.69 GiB, dropping the number of concurrently schedulable 2500-token requests and costing ~7.5% total token throughput on the 500/2000 ISL/OSL perf test (this workload sits right at a capacity threshold: QA history shows 22.55 GiB ↔ ~3.1k tok/s fast regime, ≤22.34 GiB ↔ ~2.87k tok/s slow regime).

Fix:

  • New prototype TorchLlmArgs.disable_mm_encoder: skip instantiating and loading the multimodal encoder of a VLM checkpoint (mirrors the existing MM E/P disagg encoder-skip path). Raw image/video requests are rejected by the pre-existing guard.
  • trtllm-bench throughput sets it automatically for the PyTorch backend when --modality is not given (overridable via extra_llm_api_options), restoring pre-[TRTLLM-13383][feat] Add support for Qwen3.5 VL Dense #15249 behavior for text-only benchmarks without QA test changes.
  • KV-capacity profiling skips the dummy encoder pass when the model has no local encoder (would crash otherwise).

Verification (H100, Qwen3.5-9B bf16, maxbs 512 / maxnt 2048 / max_seq_len 2500, identical binaries, Python-only pinned-binary bisect):

code state KV pool weights-load (torch)
pre-#15249 baseline 54.04 GiB / 1,374,848 tok 16.94 GiB
current (tower loaded) 53.13 GiB / 1,344,960 tok 17.87 GiB
current + this fix 54.03 GiB / 1,374,432 tok 16.97 GiB

End-to-end run (init, KV sizing, CUDA graph warmup, generate) passes with the flag enabled.

Likely also resolves the re-occurrence risk for the qwen3.5 MoE perf tests (nvbugs 6418453/6419078 family) since the gate lives in the shared Qwen3VLModelBase.

Test Coverage

  • tests/unittest/_torch/modeling/test_modeling_qwen3_5_vl.py::test_qwen35_dense_vl_disable_mm_encoder_skips_vision_tower
  • tests/unittest/_torch/modeling/test_modeling_qwen3_5_vl.py::test_qwen35_dense_vl_default_keeps_vision_tower
  • api_stability reference updated for the new prototype field.

PR Checklist

  • PR title and description follow the contribution guidelines
  • Test coverage added for the new flag
  • pre-commit hooks pass on all changed files

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an option to disable multimodal encoder loading for text-only serving.
    • Added validation preventing simultaneous text-only and encoder-only modes.
    • Text-only PyTorch throughput benchmarks now skip unused multimodal encoders to make more memory available for KV cache.
    • Added API support and documentation for the new configuration option.
  • Bug Fixes

    • Prevented unnecessary multimodal encoder profiling and weight loading when the encoder is disabled.

…text-only trtllm-bench runs

Since NVIDIA#15249/NVIDIA#14599, Qwen3.5 dense/MoE checkpoints (arch
Qwen3_5ForConditionalGeneration / Qwen3_5MoeForConditionalGeneration)
resolve to the VLM wrapper, which unconditionally instantiates and loads
the vision tower (~0.86 GiB for Qwen3.5-9B). For text-only serving this
memory is pure overhead: it shrinks the KV cache pool sized from free
GPU memory (22.55 -> 21.69 GiB on L40S for qwen3.5_9b) and drops
500/2000 ISL/OSL throughput by ~7.5% by reducing the number of
concurrently schedulable requests.

Add a prototype TorchLlmArgs flag, disable_mm_encoder, that skips
instantiating and loading the multimodal encoder of a VLM checkpoint
(mirroring the existing MM E/P disagg encoder-skip path), and set it in
trtllm-bench throughput runs when --modality is not given. Raw
image/video requests are rejected with a clear error when the encoder
is disabled (pre-existing guard). KV cache capacity profiling skips the
dummy encoder pass when no local encoder is present.

Verified on H100 with Qwen3.5-9B bf16 (maxbs 512, maxnt 2048, max_seq
2500): KV pool 53.13 GiB with the tower loaded vs 54.04 GiB with
disable_mm_encoder=1 or pre-NVIDIA#15249 code; weights-load memory returns
from 17.87 GiB to 16.94 GiB.

Signed-off-by: Thor Johnsen <41591019+thorjohnsen@users.noreply.github.com>
@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58698 [ run ] triggered by Bot. Commit: 2945b64 Link to invocation

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a disable_mm_encoder option for PyTorch multimodal models, propagates it through configuration loading, skips encoder construction, weight loading, and profiling when enabled, and applies it to text-only throughput benchmarks with validation and unit tests.

Changes

Multimodal encoder configuration and runtime

Layer / File(s) Summary
Configuration and propagation
tensorrt_llm/llmapi/llm_args.py, tensorrt_llm/_torch/model_config.py, tensorrt_llm/_torch/pyexecutor/model_loader.py, tests/unittest/api_stability/references/llm.yaml
Adds the disable_mm_encoder option, rejects combining it with mm_encoder_only, and passes it into ModelConfig loading.
Encoder runtime behavior
tensorrt_llm/_torch/models/modeling_qwen3vl.py, tensorrt_llm/_torch/models/modeling_qwen3_5.py, tensorrt_llm/_torch/pyexecutor/_util.py
Skips multimodal encoder construction, weight loading, and dummy encoder profiling when no local encoder exists.
Benchmark behavior and validation
tensorrt_llm/bench/benchmark/throughput.py, tests/unittest/_torch/modeling/test_modeling_qwen3_5_vl.py
Automatically disables the encoder for eligible text-only PyTorch benchmarks and tests both disabled and default encoder construction.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Benchmark
  participant ModelLoader
  participant Qwen3VLModelBase
  participant PyExecutor
  Benchmark->>ModelLoader: set disable_mm_encoder for text-only PyTorch run
  ModelLoader->>Qwen3VLModelBase: load configuration
  Qwen3VLModelBase-->>ModelLoader: omit local multimodal encoder
  PyExecutor->>Qwen3VLModelBase: request dummy encoder inputs
  Qwen3VLModelBase-->>PyExecutor: return empty batch
Loading

Possibly related PRs

Suggested labels: api-compatible

Suggested reviewers: yechank-nvidia, juney-nvidia, kaiyux, liji-nv, longlee0622, dpitman-nvda

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: skipping the multimodal encoder for text-only trtllm-bench runs.
Description check ✅ Passed The description includes the issue, root cause, fix, test coverage, and checklist, so it is mostly complete.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tensorrt_llm/llmapi/llm_args.py (1)

5286-5290: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a regression test for the mutually exclusive modes.

The existing Qwen3.5 tests exercise ModelConfig directly, so they do not verify that TorchLlmArgs(disable_mm_encoder=True, mm_encoder_only=True) raises ValueError. Add or extend tests/unittest/llmapi/test_llm_args.py to cover this invalid combination and the default False behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/llmapi/llm_args.py` around lines 5286 - 5290, Add regression
coverage in test_llm_args.py for TorchLlmArgs: assert that constructing it with
disable_mm_encoder=True and mm_encoder_only=True raises ValueError, and verify
both options default to False when omitted.

Source: Path instructions

tests/unittest/_torch/modeling/test_modeling_qwen3_5_vl.py (1)

168-190: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for downstream encoder-disabled behavior.

These tests sufficiently verify constructor state, but coverage is insufficient for the new contracts: disabled models must reject raw image/video requests, and text-only PyTorch throughput must default the flag while preserving an explicit disable_mm_encoder=False override.

Add focused cases to tests/unittest/_torch/modeling/test_modeling_qwen3_5_vl.py and the throughput command test module, for example tests/unittest/bench/benchmark/test_throughput.py; if that suite is integration-only, track the benchmark case as follow-up outside this PR.

As per path instructions, this test review identifies what is covered, what is insufficient, and the concrete follow-up files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/modeling/test_modeling_qwen3_5_vl.py` around lines 168
- 190, Add focused tests beyond constructor state: verify a Qwen3_5VLModel
created with disable_mm_encoder=True rejects raw image and video requests, and
update the PyTorch text-only throughput command tests to confirm the flag
defaults to disabled while an explicit disable_mm_encoder=False is preserved.
Use the existing Qwen3_5VLModel test helpers and throughput command test
symbols; if throughput coverage is integration-only, record that benchmark case
as a follow-up.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/bench/benchmark/throughput.py`:
- Around line 452-454: Normalize options.backend to a consistent case before the
automatic-default condition in the benchmark dispatch logic, then compare the
normalized value with "pytorch" so mixed-case PyTorch invocations also set
disable_mm_encoder=True when appropriate.

---

Nitpick comments:
In `@tensorrt_llm/llmapi/llm_args.py`:
- Around line 5286-5290: Add regression coverage in test_llm_args.py for
TorchLlmArgs: assert that constructing it with disable_mm_encoder=True and
mm_encoder_only=True raises ValueError, and verify both options default to False
when omitted.

In `@tests/unittest/_torch/modeling/test_modeling_qwen3_5_vl.py`:
- Around line 168-190: Add focused tests beyond constructor state: verify a
Qwen3_5VLModel created with disable_mm_encoder=True rejects raw image and video
requests, and update the PyTorch text-only throughput command tests to confirm
the flag defaults to disabled while an explicit disable_mm_encoder=False is
preserved. Use the existing Qwen3_5VLModel test helpers and throughput command
test symbols; if throughput coverage is integration-only, record that benchmark
case as a follow-up.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 702af6a7-b035-41b6-a377-3844cb326c88

📥 Commits

Reviewing files that changed from the base of the PR and between e523b43 and 2945b64.

📒 Files selected for processing (9)
  • tensorrt_llm/_torch/model_config.py
  • tensorrt_llm/_torch/models/modeling_qwen3_5.py
  • tensorrt_llm/_torch/models/modeling_qwen3vl.py
  • tensorrt_llm/_torch/pyexecutor/_util.py
  • tensorrt_llm/_torch/pyexecutor/model_loader.py
  • tensorrt_llm/bench/benchmark/throughput.py
  • tensorrt_llm/llmapi/llm_args.py
  • tests/unittest/_torch/modeling/test_modeling_qwen3_5_vl.py
  • tests/unittest/api_stability/references/llm.yaml

Comment thread tensorrt_llm/bench/benchmark/throughput.py
@thorjohnsen thorjohnsen added the api-compatible Accepted LLM API contract change that is backwards-compatible label Jul 10, 2026
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58698 [ run ] completed with state SUCCESS. Commit: 2945b64
/LLM/main/L0_MergeRequest_PR pipeline #47283 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

…nifest for disable_mm_encoder

The disable_mm_encoder TorchLlmArgs field added by this PR is captured by the
LLM API telemetry manifest, so the committed golden manifest must include it.
Regenerated tensorrt_llm/usage/llm_args_golden_manifest.json, which adds the
single disable_mm_encoder row. Fixes the premerge privacy-gate failure in
tests/unittest/usage/test_llmapi_config_telemetry_docs.py
(test_build_capture_manifest_matches_committed_golden).

Signed-off-by: Thor Johnsen <tjohnsen@nvidia.com>
@thorjohnsen
thorjohnsen requested review from a team as code owners July 13, 2026 15:28
@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

…-mm-encoder

Signed-off-by: Thor Johnsen <tjohnsen@nvidia.com>

# Conflicts:
#	tensorrt_llm/usage/llm_args_golden_manifest.json
@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58997 [ run ] triggered by Bot. Commit: 06f75ba Link to invocation

Comment thread tensorrt_llm/_torch/pyexecutor/_util.py
Comment thread tensorrt_llm/_torch/model_config.py Outdated
Comment thread tensorrt_llm/llmapi/llm_args.py Outdated
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #58997 [ run ] completed with state SUCCESS. Commit: 06f75ba
/LLM/main/L0_MergeRequest_PR pipeline #47527 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

…l opt-in

Address review nits: clarify in both the ModelConfig comment and the
TorchLlmArgs field description that disable_mm_encoder is not applied
auto-magically - each model implementation must honor it (currently the
Qwen3-VL / Qwen3.5-VL family), and it is a no-op for models that do not
check it. Docstring/comment only; no schema change (telemetry golden
manifest and api_stability reference unaffected).

Signed-off-by: Thor Johnsen <tjohnsen@nvidia.com>
@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59021 [ run ] triggered by Bot. Commit: 487153d Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59021 [ run ] completed with state SUCCESS. Commit: 487153d
/LLM/main/L0_MergeRequest_PR pipeline #47548 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59082 [ run ] triggered by Bot. Commit: 487153d Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59082 [ run ] completed with state SUCCESS. Commit: 487153d
/LLM/main/L0_MergeRequest_PR pipeline #47602 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59216 [ run ] triggered by Bot. Commit: 487153d Link to invocation

@DomBrown DomBrown left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM for the API review part

Comment thread tests/unittest/_torch/modeling/test_modeling_qwen3_5_vl.py
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59216 [ run ] completed with state FAILURE. Commit: 487153d
/LLM/main/L0_MergeRequest_PR pipeline #47713 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59267 [ run ] triggered by Bot. Commit: 487153d Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59267 [ run ] completed with state SUCCESS. Commit: 487153d
/LLM/main/L0_MergeRequest_PR pipeline #47757 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59316 [ run ] triggered by Bot. Commit: 487153d Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59316 [ run ] completed with state SUCCESS. Commit: 487153d
/LLM/main/L0_MergeRequest_PR pipeline #47799 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@thorjohnsen

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59484 [ run ] triggered by Bot. Commit: 487153d Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59484 [ run ] completed with state SUCCESS. Commit: 487153d
/LLM/main/L0_MergeRequest_PR pipeline #47945 completed with status: 'SUCCESS'

CI Report

Link to invocation

@thorjohnsen
thorjohnsen merged commit 573bd5f into NVIDIA:main Jul 15, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-compatible Accepted LLM API contract change that is backwards-compatible

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants