feat: dynamic memory-aware GPU scheduling for model-free PTQ - #2976
feat: dynamic memory-aware GPU scheduling for model-free PTQ#2976rohan9446 wants to merge 8 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
|
👋 Hi! Thank you for contributing to llm-compressor. Please add the ready label when the PR is ready for review. Note: This is required to complete the testing suite, please only add the label once the PR is code complete and local testing has been performed. |
Merge Protections🔴 1 of 2 protections blocking · waiting on 👀 reviews
🔴 Require two reviewsWaiting for
This rule is failing.PRs labelled "two-reviews" must have at least two approving reviews before merging.
Show 1 satisfied protection🟢 Require one maintainer reviewAll PRs must have at least one approving review from a maintainer before merging.
|
There was a problem hiding this comment.
Code Review
This pull request introduces a memory-aware dynamic job scheduler for multi-GPU model-free PTQ, replacing the previous static round-robin assignment with capacity-first scheduling based on real-time free VRAM. The reviewer provided valuable feedback to improve robustness and backward compatibility. Specifically, they recommended guarding device-module and cache-clearing operations against CPU devices and missing attributes to prevent runtime crashes, ensuring empty_cache() is called in the single-worker path to avoid false out-of-memory assumptions, and replacing torch.accelerator with torch.cuda in tests to maintain compatibility with PyTorch versions older than 2.4.
15e596d to
fd12f99
Compare
Replaces static round-robin device assignment with a capacity-first scheduler that checks real-time GPU memory before each job submission. Key changes: - New scheduler module with reservation tracking, caching allocator flush, and forced-fallback when nothing fits - model_free_ptq builds jobs without device assignment; scheduler picks GPUs dynamically at submit time Closes vllm-project#2975 Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
fd12f99 to
d109158
Compare
Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
kylesayrs
left a comment
There was a problem hiding this comment.
Nice job! Pinging @brian-dellabetta for review
Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
There was a problem hiding this comment.
Hi @rohan9446 , thanks for preparing this. i have a handful of comments/questions, particularly whether we want this just in the model-free-ptq pathway and not the convert_checkpoint pathway in compressed-tensors. We have some work underway to consolidate the 2 entirely:
…uling, rename test Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
SUMMARY:
Fixes #2975, replaces the static round-robin GPU assignment in model-free PTQ with a dynamic, memory-aware scheduler.
Problem
PR #2773 introduced multi-GPU support by statically assigning shards to GPUs via
devices[i % len(devices)]. When shards differ in size or GPUs differ in speed, the slower GPU accumulates pending work and eventually OOMs. This was observed on 6-GPU setups.Solution
New
scheduler.pymodule with a capacity-first scheduling loop:torch.get_device_module(dev).mem_get_info()and picks the GPU with the most free VRAMempty_cache()is called after harvesting each completed future somem_get_inforeflects actually-free memoryconcurrent.futures.wait(FIRST_COMPLETED)replaces a hand-rolledthreading.Eventcallback, eliminating a lost-wakeup race_build_jobs()no longer takes adevicesparameter — device assignment is deferred entirely to the scheduler. Validation still uses upstreamexec_jobs(it runs on meta device, so no scheduling needed).Behavioral change
max_workersnow sets an upper bound on concurrency rather than a fixed thread count. Effective concurrency may be lower when GPU memory is tight. This also prevents OOMs caused by users picking too many workers.TEST PLAN:
Unit tests (
test_scheduler.py, no GPU required — runs on all CI):estimate_job_memory: verifies sum-of-file-sizes × multiplier_pick_device: most-free selection, returnsNonewhen nothing fits, respects reservation accounting, CPU always eligibleexec_jobs_dynamicCPU path: all jobs complete, empty input, order preservationIntegration tests (
test_multi_gpu.py,@requires_gpu(2)):test_multi_gpu_matches_single_gpu: saves Qwen3-0.6B withmax_shard_size="1MB"(~198 shards), quantizes with FP8_dynamic on 1 GPU vs N GPUs, asserts bitwise tensor equalitytest_multi_gpu_more_workers_than_shards: verifies graceful handling whenmax_workers > len(jobs)Validated on:
make qualitypasses (ruff check + ruff format +tools/lint_cuda.py)