Repository navigation
fix(vllm): cap gpu_memory_utilization for encoder-only vision model load - #8466
Conversation
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>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughThe vLLM LLM initialization for Qwen VL vision-model-only loading in Changes
Estimated code review effort🎯 1 (Trivial) | ⏱️ ~3 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ 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. Comment |
…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
left a comment
There was a problem hiding this comment.
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
TL;DR
Add
gpu_memory_utilization=0.2to the nestedvllm.LLM(...)inload_vision_model(). The encode worker was hitting a 0.9-default memory precheck thatmm_encoder_only=Truedoes 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:173constructs a nested vLLM engine just to load the Qwen VL vision tower:It never sets
gpu_memory_utilization, so vLLM falls back to its default0.9. On CI's amd64 A10G (22.03 GiB) with the PD worker already holding some memory, the up-front free-memory check atvllm/v1/worker/utils.py:413fails: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-testbash.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_dhandles images inline via PIL;mm_agg_*is a single combined worker — neither exercises the nestedLLM(), 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=Truealready skips model-weight load (interfaces.py:234) and the encoderprofile_run()pass (gpu_model_runner.py:5237). But the up-frontrequest_memory()precheck atgpu_worker.py:283runs before either of those — and it is architecture-blind. Alternatives considered:request_memory()?kv_cache_memory_bytesalonedetermine_available_memory().skip_mm_profiling=True(v0.19.1 CLI flag)profile_run().AutoModel.from_pretrained(the non-Qwen branch ofload_vision_model)Qwen3VLModel.__init__bundlesself.visual+self.language_model; HF loads both. Wastes ~2 GiB on Qwen3-VL-2B, ~27 GiB on Qwen3-VL-30B.gpu_memory_utilization=0.2Upstream gap worth tracking
vllm/v1/worker/gpu_worker.py:request_memory()has no awareness ofmm_encoder_only. Whenmm_encoder_only=TrueANDkv_cache_memory_bytesare both set, the precheck could be skipped (or effective utilization could be capped atkv_cache_memory_bytes / total_memory). Neither path exists in vLLM 0.19.1 — hence the explicit cap here.Test plan
vllm / Test cuda12.9andcuda13.0jobs passmm_e_pd_qwen3-vl-2b,mm_epd_qwen3-vl-2b, andmm_epd_qwen3-vl-2b-videomm_agg_*andmm_p_d_*topologies (which don't go throughload_vision_model) remain unaffected🤖 Generated with Claude Code
Summary by CodeRabbit