[MM][V2] Enable encoder-only ViT CUDA graph capture by jiangkuaixue123 · Pull Request #56922 · vllm-project/vllm · GitHub
Skip to content

[MM][V2] Enable encoder-only ViT CUDA graph capture - #56922

Merged
Isotr0py merged 20 commits into
vllm-project:mainfrom
jiangkuaixue123:codex/mrv2-eonly-vit-cudagraph
Sep 17, 2026
Merged

Isotr0py merged 20 commits into
vllm-project:mainfrom
jiangkuaixue123:codex/mrv2-eonly-vit-cudagraph

Conversation

@jiangkuaixue123

@jiangkuaixue123 jiangkuaixue123 commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

Enable vision encoder CUDA graph capture for ModelRunnerV2 encoder-only instances. #49852 added encoder graph support to the ordinary V2 GPUModelRunner, but MMEncoderModelRunner overrides capture_model with an immediate return, so E-only instances do not capture those graphs.

Call the existing EncoderRunner capture path from the E-only runner, with GC handling, capture-memory accounting, and workspace locking. Reuse existing budget selection, replay, eager fallback, output copies, and EC transfer. The feature remains opt-in, independent of decoder graph mode, and disabled by enforce_eager.

Add E-only configuration/lifecycle/model tests, Mooncake graph-hit assertions, and CI configuration. The attention-padding zero-initialization workaround, its capture context, and its dedicated padding regression test have been removed. The production change is confined to MMEncoderModelRunner; this PR does not change attention kernels or default backend selection.

Known FA backend issue and backend validation

The separate NaN issue is tracked in #57136. With the workaround disabled, H20 / Qwen2.5-VL-3B / BF16 / head_dim=80 / TP=1 testing of nine encoder graph budgets sharing one pool produced:

Actual encoder backend 1196-token image replaying budget 2048 V-padding NaN injection into a single attention call
FLASH_ATTN, FA3 All-NaN graph output; eager finite Valid output becomes NaN in eager and graph
FLASH_ATTN, FA4 All-NaN graph output; eager finite Valid output becomes NaN in eager and graph
TRITON_ATTN Finite; maximum difference from its eager reference = 0 Valid output remains finite and matches reference
FLASHINFER (cuDNN) Finite; maximum difference from its eager reference = 0 Valid output remains finite and matches reference

Backend/version selection was inspected on the actual attention layers; there was no silent fallback. Each backend ran three small -> large -> small sequences, with nine graph hits and zero misses. Tail-injection tests covered capacity/active lengths 256/128 and 2048/1196, using fixed atol=rtol=0.016 against unpadded FP32 SDPA. FLASHINFER used its required offset metadata and interleaved QKV layout. All backends leave some output padding unwritten, but only FA3/FA4 propagated V-tail NaNs in these tests. The complete full-model propagation chain and interface-contract responsibility remain under investigation in #57136.

FLASHINFER and Triton did not reproduce this issue in the tested configuration; this is not a universal guarantee across models and shapes. FA3/FA4 remain affected in that configuration, and this PR does not fix or close #57136. All E-only model test cases (Qwen2.5-VL, Qwen3-VL, and Qwen3.5) select FLASHINFER. Mooncake CI selects TRITON_ATTN. Users can set --mm-encoder-attn-backend TRITON_ATTN or FLASHINFER when using encoder graphs on supported configurations; the integration script exposes E_MM_ENCODER_ATTN_BACKEND.

Environment: NVIDIA H20-3e, driver 580.105.08, Python 3.12, Torch 2.13.0+cu132; FLASHINFER 0.6.17, cuDNN package 9.20.0.48. Model revision: 66285546d2b821cf421d4f5eb2576359d3770cd3. Backend comparisons used 53a766f with the workaround disabled before worker initialization; the final change removes that workaround entirely. Existing precompiled extensions were reused. Multi-GPU, V1, RDMA, LoRA, and broad model-quality benchmarks were not evaluated in this follow-up. No performance improvement is claimed.

Test Plan

export PATH="$PWD/.venv/bin:$PATH"
export VLLM_USE_V2_MODEL_RUNNER=1
export VLLM_WORKER_MULTIPROC_METHOD=spawn
export OMP_NUM_THREADS=1

.venv/bin/python -m pytest \
  tests/kernels/attention/test_mha_attn.py \
  tests/v1/worker/test_encoder_runner.py \
  tests/v1/cudagraph/test_encoder_cudagraph.py \
  tests/v1/ec_connector/unit/test_worker_ec_connector.py -q

.venv/bin/python -m pytest \
  tests/models/multimodal/generation/test_vit_cudagraph.py \
  -k 'eonly and (qwen2_5_vl or qwen3_vl)' -v -s

Test Result

Code validation revision 05c72d0: pre-commit (including shellcheck and documentation checks) and manual mypy-3.12 passed. H20 rerun on this revision: combined attention/encoder/EC suites 132 passed, 14 skipped (27.85s; ROCm/AITER-only skips). Qwen2.5-VL and Qwen3-VL E-only tests using TRITON_ATTN: 2 passed (123.19s), covering images, video, budget fallback, and retained-output integrity. The backend comparisons above were completed without the workaround. The updated Mooncake CI backend configuration has not yet been run end-to-end on this revision; earlier EPD results with the workaround are not presented as validation of this final version.

The subsequent documentation-only commit 31993e3 removes all changes to docs/features/disagg_encoder.md; its pre-commit checks passed. Production code and tests are unchanged from the validated revision above.

Duplicate-work checks and human review

Open PR searches for E-only/encoder-only graphs, encoder cudagraph, and padding NaNs found no competing E-only capture implementation. #49852 is the merged infrastructure reused here. #55929 addresses input normalization; #52230 addresses V1 non-first PP ranks. The independent FA NaN issue is tracked in #57136 rather than fixed in this PR.

AI assistance (OpenAI Codex) was used for implementation, testing, and review. This PR remains a draft pending the human submitter's required review and test confirmation:

  • Human submitter reviewed every changed line and understands the change end-to-end.
  • Human submitter ran the relevant tests.

E-only model test backend follow-up

All three parameterized E-only model test cases now use FLASHINFER. The H20 FLASHINFER rerun described below covers Qwen2.5-VL; Qwen3-VL and Qwen3.5 await validation with this backend. Buildkite 89352 failed that case with TRITON_ATTN on H200 MIG (finite outputs, but maximum absolute difference 3.40625). On H20 at the same cc04b48 source revision, the isolated test passed with TRITON_ATTN and with a test-only FLASHINFER override: 1 passed in 88.82s and 57.55s respectively, both with maximum absolute error 0, seven graph hits and one fallback. These durations include startup and are not a backend performance comparison. Pre-commit passed for the committed backend change; the new CI result remains pending.

Co-authored-by: GPT-6 <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: GPT-6 <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: GPT-6 <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: GPT-6 <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: GPT-6 <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: GPT-6 <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: GPT-6 <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: GPT-6 <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: Codex <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: Codex <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: Codex <noreply@openai.com>

Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
@mergify

mergify Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Documentation preview: https://vllm--56922.org.readthedocs.build/en/56922/

@mergify mergify Bot added documentation Improvements or additions to documentation ci/build multi-modality Related to multi-modality (#4194) nvidia labels Sep 15, 2026
@njhill
njhill requested a review from Isotr0py September 15, 2026 04:12
@Isotr0py Isotr0py self-assigned this Sep 15, 2026
Comment thread vllm/v1/attention/ops/vit_attn_wrappers.py Outdated
jiangkuaixue123 and others added 2 commits September 16, 2026 16:40
Track the unresolved NaN propagation in vllm-project#57136 and avoid zeroing ordinary eager attention outputs.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Remove the temporary attention workaround and its regression test. Exercise E-only capture with Triton and document the backend comparison in vllm-project#57136.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
@jiangkuaixue123 jiangkuaixue123 changed the title [MM][V2] Enable encoder-only ViT CUDA graphs and initialize attention padding [MM][V2] Enable encoder-only ViT CUDA graph capture Sep 16, 2026
@jiangkuaixue123

jiangkuaixue123 commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor Author

During validation with Qwen2.5-VL-3B on H20 (BF16), we observed NaN encoder embeddings when replaying a 1196-token image through the 2048-token graph in a multi-budget shared pool. This reproduced with both FA3 and FA4, while ordinary eager execution remained finite. FLASHINFER and Triton did not reproduce the issue in the same tested configuration. The investigation is tracked in #57136.

The earlier attention-output zero-initialization avoided the observed failure, but it was a temporary workaround rather than a fully established root-cause fix. In particular, isolated tests showed that NaNs in the unused V tail can contaminate valid FA3/FA4 outputs even in eager execution; the full propagation path and the appropriate fix still need investigation.

I have removed the padding workaround and its dedicated test from this PR. This PR now focuses on enabling E-only encoder graph capture; the FA NaN fix will be handled separately under #57136. The E-only functional tests and Mooncake CI explicitly use Triton, without changing the product's default backend selection. @gty111

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
@jiangkuaixue123
jiangkuaixue123 marked this pull request as ready for review September 16, 2026 09:27
@gty111

gty111 commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

@Isotr0py can also take a look

@Isotr0py Isotr0py left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@github-project-automation github-project-automation Bot moved this to Ready in NVIDIA Sep 16, 2026
@Isotr0py
Isotr0py enabled auto-merge (squash) September 16, 2026 12:48
@github-actions github-actions Bot added the ready ONLY add when PR is ready to merge/full CI is needed label Sep 16, 2026
@jiangkuaixue123

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

❌ This PR is 149 commits behind upstream main. Your branch must contain every commit currently on upstream main. No new CI build was started. Merge or rebase onto the latest main, then rerun /ci run. To test this branch at your own risk, use /ci run --allow-stale.

@jiangkuaixue123

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #89352 for commit cc04b4877065.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
auto-merge was automatically disabled September 17, 2026 01:29

Head branch was pushed to by a user without write access

jiangkuaixue123 and others added 2 commits September 17, 2026 09:30
Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
@jiangkuaixue123

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #89497 for commit 6f4b06a1cc36.

Co-authored-by: Codex <noreply@openai.com>
Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
@jiangkuaixue123

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

❌ This PR is 12 commits behind upstream main. Your branch must contain every commit currently on upstream main. No new CI build was started. Merge or rebase onto the latest main, then rerun /ci run. To test this branch at your own risk, use /ci run --allow-stale.

@jiangkuaixue123

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #89555 for commit 56aee443760f.

@jiangkuaixue123

Copy link
Copy Markdown
Contributor Author

@Isotr0py
Isotr0py merged commit de24e51 into vllm-project:main Sep 17, 2026
139 checks passed
@github-project-automation github-project-automation Bot moved this from Ready to Done in NVIDIA Sep 17, 2026
michael-gpu pushed a commit to practical-inference/vllm-ampere-extended that referenced this pull request Oct 20, 2026
Signed-off-by: jiangkuaixue123 <jiangxiaozhou111@163.com>
Co-authored-by: Codex <noreply@openai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/build documentation Improvements or additions to documentation multi-modality Related to multi-modality (#4194) nvidia ready ONLY add when PR is ready to merge/full CI is needed

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug]: NaN vision embeddings with multi-budget encoder CUDA graphs sharing a pool

3 participants