[Examples] Glm5.2 MXFP4xMXFP8 - #3048
Conversation
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 #2975 Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
…uling, rename test Signed-off-by: Rohan Bandaru <rohanbanadaru14838@gmail.com>
Signed-off-by: Kyle Sayers <kylesayrs@gmail.com>
|
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:
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 |
Merge Protections🟢 Merge protection satisfied — ready to merge. 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 old round-robin assignment with capacity-first scheduling based on real-time VRAM estimates. It also adds corresponding unit and multi-GPU tests, alongside an experimental script for GLM-5.2 quantization. Feedback focuses on improving the scheduler's robustness by force-scheduling jobs when conservative memory estimates exceed available VRAM (rather than raising a RuntimeError), updating the associated unit tests, and correcting a non-existent model ID (Qwen/Qwen3-0.6B) used in the multi-GPU tests to an existing one like Qwen/Qwen2.5-0.5B.
| if not inflight: | ||
| if not pending: | ||
| break | ||
| raise RuntimeError( | ||
| "No device has enough estimated free memory " | ||
| "for any remaining shard. Consider reducing " | ||
| "max_workers or adjusting _MEMORY_MULTIPLIER." | ||
| ) |
There was a problem hiding this comment.
Instead of raising a RuntimeError and crashing when a job's conservative memory estimate exceeds the available VRAM on all devices, it is much more robust to force-schedule the job on the device with the most available memory. This aligns with the single-worker path behavior (which warns and proceeds) and prevents hard crashes on conservative overestimates, allowing the quantization to proceed and potentially succeed.
| if not inflight: | |
| if not pending: | |
| break | |
| raise RuntimeError( | |
| "No device has enough estimated free memory " | |
| "for any remaining shard. Consider reducing " | |
| "max_workers or adjusting _MEMORY_MULTIPLIER." | |
| ) | |
| if not inflight: | |
| if not pending: | |
| break | |
| idx = pending[0] | |
| dev = max(devices, key=lambda d: _free_bytes(d, initial_free, reserved)) | |
| logger.warning( | |
| f"Shard {idx} (~{memory_estimates[idx] / 1e9:.2f} GB) " | |
| f"exceeds estimated available capacity on all devices. " | |
| f"Force-scheduling on {dev} anyway." | |
| ) | |
| fn, iwm, sp, sch, ign, conv = jobs[idx] | |
| fut = pool.submit(fn, iwm, sp, sch, ign, dev, conv) | |
| inflight[fut] = idx | |
| fut_device[fut] = dev | |
| reserved[dev] += memory_estimates[idx] | |
| pending.remove(idx) |
| def test_multi_gpu_matches_single_gpu(tmp_path): | ||
| """Dynamic scheduling across N GPUs should give identical output to | ||
| a single-GPU run.""" | ||
| model_id = "Qwen/Qwen3-0.6B" |
There was a problem hiding this comment.
The model ID Qwen/Qwen3-0.6B does not exist on Hugging Face (Qwen3 has not been released yet). This will cause the test to fail with a RepositoryNotFoundError. Please use an existing small model such as Qwen/Qwen2.5-0.5B or Qwen/Qwen2-0.5B.
| model_id = "Qwen/Qwen3-0.6B" | |
| model_id = "Qwen/Qwen2.5-0.5B" |
| @requires_gpu(2) | ||
| def test_multi_gpu_more_workers_than_shards(tmp_path): | ||
| """Scheduler should be fine when there are more threads than shards.""" | ||
| model_id = "Qwen/Qwen3-0.6B" |
| @patch( | ||
| "llmcompressor.entrypoints.model_free.scheduler" | ||
| ".torch.accelerator.memory.get_memory_info" | ||
| ) | ||
| def test_raises_when_no_device_fits(mock_mem_info): | ||
| """Multi-worker path should raise RuntimeError when no device can fit | ||
| any remaining shard and nothing is in flight.""" | ||
| mock_mem_info.return_value = (1000, 96_000_000_000) | ||
|
|
||
| def fn(iwm, sp, sch, ign, dev, conv): | ||
| return (0, {}) | ||
|
|
||
| # job needs 10 GB but device only has 1000 bytes | ||
| jobs = [(fn, {}, "s0.st", "FP8", [], None)] | ||
| mem = [10_000_000_000] | ||
|
|
||
| with pytest.raises(RuntimeError, match="No device has enough"): | ||
| exec_jobs_dynamic( | ||
| jobs, | ||
| [torch.device("cuda:0")], | ||
| 2, | ||
| mem, | ||
| desc="Test", | ||
| ) |
There was a problem hiding this comment.
Update the unit test to verify the new robust force-scheduling behavior when no device has enough estimated free memory, rather than expecting a RuntimeError.
@patch(
"llmcompressor.entrypoints.model_free.scheduler"
".torch.accelerator.memory.get_memory_info"
)
def test_force_schedules_when_no_device_fits(mock_mem_info):
"""Multi-worker path should force-schedule when no device can fit
any remaining shard and nothing is in flight, rather than raising RuntimeError."""
mock_mem_info.return_value = (1000, 96_000_000_000)
def fn(iwm, sp, sch, ign, dev, conv):
return (100, {"t": sp})
# job needs 10 GB but device only has 1000 bytes
jobs = [(fn, {}, "s0.st", "FP8", [], None)]
mem = [10_000_000_000]
out = exec_jobs_dynamic(
jobs,
[torch.device("cuda:0")],
2,
mem,
desc="Test",
)
assert out == [(100, {"t": "s0.st"})]|
👋 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. |
|
The quality checks have failed. Please run |
| r"re:.*mlp\.gate.*", | ||
| r"lm_head", | ||
| ], | ||
| max_workers=50, |
There was a problem hiding this comment.
isn't this rather high?
There was a problem hiding this comment.
It's a large model, honestly afaict we can go larger without suffering too much overhead.
| SAVE_DIR = MODEL_ID.rstrip("/").split("/")[-1] + "-MXFP4xMXFP8" | ||
|
|
||
| # use `model_free_ptq` to apply quantization | ||
| # NOTE: MXFP4xMXFP8 is experimentally supported in vllm |
There was a problem hiding this comment.
how experimental? 😅
There was a problem hiding this comment.
Prerequisites