Skip to content

fix(vllm): cap gpu_memory_utilization for encoder-only vision model load - #8466

Merged
dmitry-tokarev-nv merged 3 commits into
mainfrom
qiwa/ops-4445-fix-vllm-multi-modal-epd-disagg-tests
Apr 22, 2026
Merged

dmitry-tokarev-nv merged 3 commits into
mainfrom
qiwa/ops-4445-fix-vllm-multi-modal-epd-disagg-tests

Conversation

@furionw

@furionw furionw commented Apr 21, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR

Add gpu_memory_utilization=0.2 to the nested vllm.LLM(...) in load_vision_model(). The encode worker was hitting a 0.9-default memory precheck that mm_encoder_only=True does not skip, breaking three multimodal disagg tests on every amd64 nightly run.

Fixes OPS-4445. Unblocks the qwen3-vl-2b skip hunks in #8443.

The bug

components/src/dynamo/vllm/multimodal_utils/model.py:173 constructs a nested vLLM engine just to load the Qwen VL vision tower:

vllm_model = LLM(model=model_id, kv_cache_memory_bytes=64*MiB, max_model_len=1,
                 mm_encoder_only=True, enable_prefix_caching=False, ...)

It never sets gpu_memory_utilization, so vLLM falls back to its default 0.9. On CI's amd64 A10G (22.03 GiB) with the PD worker already holding some memory, the up-front free-memory check at vllm/v1/worker/utils.py:413 fails:

[rank0] File ".../dynamo/vllm/multimodal_utils/model.py", line 173, in load_vision_model
[rank0] File ".../vllm/v1/worker/gpu_worker.py", line 283, in init_device
[rank0] ValueError: Free memory on device cuda:0 (19.5/22.03 GiB) on startup is less
        than desired GPU memory utilization (0.9, 19.83 GiB).

That ValueError is what the test harness surfaces as the generic RuntimeError: Engine core initialization failed. See root cause above. — the "root cause above" only lives in the full per-test bash.log.txt (pulled from the allure-results artifact for job 72349014718), which is why the Linear ticket and #8443 never saw it.

Why only e_pd / epd / epd-video fail: only those topologies run a separate encode worker that calls load_vision_model(). p_d handles images inline via PIL; mm_agg_* is a single combined worker — neither exercises the nested LLM(), so neither hits the precheck.

Why it didn't reproduce on our dev workstation: on a 48 GiB card, 0.9 × 48 ≈ 43 GiB fits comfortably in the ~47 GiB free at startup. The bug is GPU-size-gated around ~24 GiB — exactly the CI class.

Why 0.2 and not something cleaner

mm_encoder_only=True already skips model-weight load (interfaces.py:234) and the encoder profile_run() pass (gpu_model_runner.py:5237). But the up-front request_memory() precheck at gpu_worker.py:283 runs before either of those — and it is architecture-blind. Alternatives considered:

Candidate Skips request_memory()? Notes
kv_cache_memory_bytes alone No Already set. Only skips later determine_available_memory().
skip_mm_profiling=True (v0.19.1 CLI flag) No Only skips encoder contribution to profile_run().
AutoModel.from_pretrained (the non-Qwen branch of load_vision_model) N/A Worse. Qwen3VLModel.__init__ bundles self.visual + self.language_model; HF loads both. Wastes ~2 GiB on Qwen3-VL-2B, ~27 GiB on Qwen3-VL-30B.
gpu_memory_utilization=0.2 Yes 0.2 × 24 GiB = 4.8 GiB ceiling — covers ViT weights (~1 GiB for Qwen3-VL-2B) + 64 MiB KV stub + activation peak on every GPU class we run on.

Upstream gap worth tracking

vllm/v1/worker/gpu_worker.py:request_memory() has no awareness of mm_encoder_only. When mm_encoder_only=True AND kv_cache_memory_bytes are both set, the precheck could be skipped (or effective utilization could be capped at kv_cache_memory_bytes / total_memory). Neither path exists in vLLM 0.19.1 — hence the explicit cap here.

Test plan

  • amd64 post-merge vllm / Test cuda12.9 and cuda13.0 jobs pass mm_e_pd_qwen3-vl-2b, mm_epd_qwen3-vl-2b, and mm_epd_qwen3-vl-2b-video
  • mm_agg_* and mm_p_d_* topologies (which don't go through load_vision_model) remain unaffected
  • On a 48 GiB dev GPU, encode + PD single-GPU startup still succeeds end-to-end — the cap to 0.2 strictly lowers the ask; it never increases it

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Optimized GPU memory allocation settings for vision model initialization to improve resource efficiency and system stability during model loading operations.

The encode worker's handler creates a nested vllm.LLM() via
load_vision_model() to load only the vision submodule. Without an
explicit gpu_memory_utilization kwarg it falls back to vLLM's default
0.9, which fails the free-memory precheck on <=24 GiB GPUs when the
outer worker (e.g. PD in single-GPU mode) is already holding memory:

    ValueError: Free memory on device cuda:0 (19.5/22.03 GiB) on
    startup is less than desired GPU memory utilization (0.9, 19.83
    GiB).

vLLM's precheck in gpu_worker.request_memory() runs before
kv_cache_memory_bytes takes effect, so capping kv-cache alone isn't
enough. 0.2 is generous for an encoder-only load (64 MiB KV stub,
~1 GiB ViT weights for Qwen3-VL-2B) and safe on every GPU class we
run on.

Fixes OPS-4445: unskips
  test_serve_deployment[mm_e_pd_qwen3-vl-2b]
  test_serve_deployment[mm_epd_qwen3-vl-2b]
  test_serve_deployment[mm_epd_qwen3-vl-2b-video]

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added fix backend::vllm Relates to the vllm backend multimodal labels Apr 21, 2026
@furionw
furionw marked this pull request as ready for review April 21, 2026 21:16
@furionw
furionw requested review from a team as code owners April 21, 2026 21:16
@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b63bf499-36a9-4788-8d22-24a8c83cc191

📥 Commits

Reviewing files that changed from the base of the PR and between cc583b2 and b7ea2dc.

📒 Files selected for processing (1)
  • components/src/dynamo/vllm/multimodal_utils/model.py

Walkthrough

The vLLM LLM initialization for Qwen VL vision-model-only loading in load_vision_model now explicitly sets gpu_memory_utilization=0.2, adjusting the memory allocation behavior for this specific model configuration.

Changes

Cohort / File(s) Summary
Memory Configuration
components/src/dynamo/vllm/multimodal_utils/model.py
Added gpu_memory_utilization=0.2 parameter to vLLM LLM initialization for Qwen VL vision-model-only loading in load_vision_model.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding a gpu_memory_utilization cap for encoder-only vision model loading in vLLM.
Description check ✅ Passed The description fully covers all required template sections: Overview (TL;DR), Details (comprehensive explanation of the bug and fix), Where to review (specific file and line), and Related Issues (OPS-4445 and PR #8443).
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

furionw and others added 2 commits April 21, 2026 14:35
…gg tests

The three tests were skipped in #8443 while OPS-4445 was investigated.
The preceding commit fixes the encode worker's nested vllm.LLM() memory
precheck; re-enable the tests under pre_merge so they gate regressions.

Leaves the qwen2-audio-7b post_merge skip in place — that is a distinct
failure on the agg topology (no encode worker, not addressed here).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@GuanLuo GuanLuo 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.

Is this regression introduced in newer vLLM version? Previously kv_cache_memory_bytes should affects the required memory determined by vLLM. I remember I was toggling good gpu_memory_utilization for encoder but then realized setting kv_cache_memory_bytes is sufficient

@dmitry-tokarev-nv
dmitry-tokarev-nv merged commit af32579 into main Apr 22, 2026
87 checks passed
@dmitry-tokarev-nv
dmitry-tokarev-nv deleted the qiwa/ops-4445-fix-vllm-multi-modal-epd-disagg-tests branch April 22, 2026 15:59
@rmccorm4 rmccorm4 mentioned this pull request Sep 27, 2026

This branch was previously deployed

1 inactive deployment
GITLAB — 7d1b42e4 Deployed Apr 21, 2026 by copy-pr-bot[bot] via Trigger CI Pipeline #24614
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants