Repository navigation
[Bugfix] Size the extensible KV cache from UMA-corrected free memory - #65413
Conversation
On integrated GPUs (DGX Spark, GH200, Jetson) cudaMemGetInfo does not count reclaimable OS memory (page cache, buffers) as free, although a device allocation reclaims it. MemorySnapshot already corrects for this with psutil, but the extensible KV cache read torch.accelerator.get_memory_info directly in committable_blocks, measure_kv_cache_blocks and the weight-update release check, so after the weights had been read through the page cache (e.g. safetensors_load_strategy=prefetch) the measured headroom was a fraction of the real one. On a 119.7 GiB DGX Spark with a 27B NVFP4 model this sized the cache to 216 blocks instead of the 71 GiB the classic path finds, and to zero blocks in disaggregated serving, where the larger transient peak exhausted the headroom term. Commit via cuMemCreate/cuMemMap on the same part reclaimed 106 GiB with 59 GiB reported free, so the psutil figure is the right one there too. Move the correction into a shared device_memory_info() helper in mem_utils and use it from MemorySnapshot.measure and the three extensible KV cache call sites. Fixes vllm-project#65378 Signed-off-by: Alberto Massidda <amassidda@nvidia.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Nick Hill <nickhill123@gmail.com>
|
/ci run |
|
✅ @aijanai, CI is now available for this PR.
|
|
✅ Triggered Buildkite CI #99614 for commit ℹ️ This PR is 13 commits behind upstream |
|
This pull request has merge conflicts that must be resolved before it can be |
…kv-uma-free-memory # Conflicts: # vllm/v1/worker/extensible_kv_cache.py Signed-off-by: Nick Hill <nickhill123@gmail.com>
…kv-uma-free-memory
|
/ci retry |
|
✅ The previous CI build is still running: https://buildkite.com/vllm/ci/builds/99614 |
|
/ci retry |
|
✅ No failed, timed-out, or expired jobs need retrying in Buildkite CI #99614. |
|
/ci run |
|
✅ Triggered Buildkite CI #99713 for commit ℹ️ This PR is 5 commits behind upstream |
Picks up the main fixes for the fusion E2E mixed-precision test (vllm-project#65495), the DeepSeek-V4.1 _Mxfp8Linear test (vllm-project#63921) and UMA KV cache sizing on DGX Spark (vllm-project#65413). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: khluu <khluu000@gmail.com>
Overview
Fixes #65378. On integrated (UMA) GPUs the extensible KV cache (#56492) sized itself from raw
cudaMemGetInfo, which does not count reclaimable page cache as free, so it undersized the KV cache by up to 84% on a DGX Spark (GB10) and to zero blocks in disaggregated serving. This reads free memory through one helper with the samepsutilcorrection the classic sizing path already applies (#35920).Claims
gpu_memory_utilization=0.8, and no longer fails with "No available memory for the cache blocks" in P/D.torch.accelerator.get_memory_infoas is whenis_integrated_gpu()is false.--no-enable-extensible-kv-cacheis no longer needed as a workaround on UMA parts.Validation
All runs on one GB10 (DGX Spark, 119.7 GiB unified memory, CC 12.1) or one RTX PRO 6000 (discrete, 96 GB), image
vllm/vllm-openai:nightlyat 40941f2 (the extensible code it ships is identical to this branch's base), with this PR'smem_utils.pyandextensible_kv_cache.pycopied over the installed package for the "with fix" rows.End to end,
vllm serve nvidia/Qwen3.8-27B-NVFP4 --quantization modelopt_fp4 --kv-cache-dtype fp8 --max-model-len 4096 --gpu-memory-utilization 0.8 --max-num-seqs 32 --speculative-config '{"method":"mtp","num_speculative_tokens":3}', V2 model runner, extensible KV cache at its default:Available KV cache memory)Before the server started on GB10,
cudaMemGetInforeported 59-81 GiB free whilepsutil.virtual_memory().availablereported 114-116 GiB: the difference is page cache, from this load and earlier runs on the node. In disaggregated P/D (NIXL, same model, MTP k=3) the larger transient peak (7.28 GiB vs 1.71 GiB) took the same deficit below zero: both workers failed with0 KV cache blocks (0.0 GiB) fit within the requested 95.71 GiB/ValueError: No available memory for the cache blocks.Device allocations reclaim the page cache. After filling the page cache by reading the checkpoint, committing through the extensible cache's own
_VirtualBuffer(cuMemCreate+cuMemMap) in 1 GiB steps reached 106 GiB, 1.8x the 59.2 GiBcudaMemGetInforeported free, with no error;psutil"cached" fell from 56.4 to 4.1 GiB in step with the commits. So thepsutilfigure is the one an allocation can actually obtain.New test fails before the fix, passes after (GB10):
tests/utils_/test_mem_utils.pyandtests/v1/worker/test_extensible_kv_cache.pywith the fix on GB10: 26 passed, 1 failed. The failure,test_memory_profiling(asserts 256 MiB of non-torch memory, measures ~269-273 MB), also fails on the unmodified nightly with the upstream test file, 2 out of 2 runs, so it is a pre-existing UMA flake unrelated to this change. Both files are already run by CI (pytest -v -s v1/worker,pytest -v -s utils_in.buildkite/test_areas/misc.yaml).Details
Root cause.
MemorySnapshot.measure()(vllm/utils/mem_utils.py) replacescudaMemGetInfo's free memory withpsutil.virtual_memory().availableon integrated GPUs, and the extensible cache receives that asinit_free_memory. Butcommittable_blocks()andmeasure_kv_cache_blocks()invllm/v1/worker/extensible_kv_cache.pyre-read rawtorch.accelerator.get_memory_info. The sizing inmeasure_kv_cache_bytes()ismin(requested - non_kv_used, free + committed - margin) - transient_peakwithnon_kv_used = init_free - free - committed: with the raw figure, page cache counts as engine memory in the first term and is missing from the headroom in the second. In the P/D failure the headroom term alone was already negative: 10.46 + 0.73 - 5.46 - 7.28 = -1.55 GiB. Warmup was capped by the same raw read (only 0.73 GiB committed).Fix. Move the existing correction into
device_memory_info(device)inmem_utilsand use it fromMemorySnapshot.measure()and the three extensible call sites (committable_blocks,measure_kv_cache_blocks, and the free-memory read when releasing for a weight update). The one remainingget_memory_infocall in the extensible cache reads only the total. Main-code change is ~30 lines; the rest is tests.Alternative considered. Turning the extensible cache off on integrated GPUs in
_resolve_extensible_kv_cachewould also avoid the failure, but would hide the inconsistency and drop the feature on UMA parts. The reclaim measurement above shows the corrected figure is safe to size from, so this PR fixes the measurement instead.Tests. The new tests patch
current_platform.is_integrated_gpu,psutilandtorch.accelerator.get_memory_info: CI has no integrated GPU, and these stand in for the hardware/OS readings rather than for vLLM objects. One covers the helper (integrated vs discrete), one covers the extensible sizing end to end throughmeasure_kv_cache_blockswith the readings from this report.Related. #55828 (open) sizes the KV cache from the process's own memory via NVML with a fallback to the device-wide delta; on GB10 that fallback would hit the same raw reading, so the two changes should agree on
device_memory_info. #65204 / #65225 (granule rounding with offload connectors) is a different cause with the same error message.Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.
🤖 Generated with Claude Code