[QDP][feature] reject pipeline configs that exceed free VRAM - #1466
Open
0lai0 wants to merge 1 commit into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issues
Closes #1430
Changes
Why
An oversized QDP pipeline currently runs until
encode_batchallocates GPU buffers before it fails — slow, and the error does not say what to change. This adds a fail-fast check against free VRAM so a configuration that cannot fit is rejected in milliseconds, before any input file is read, with a message naming the values to lower.How
After
normalize(), the threePipelineIteratorconstructors (new_synthetic,new_from_file,new_from_file_streaming) compareestimate_memory().gpu_state_bytesto free device memory. The check is skipped when the CUDA runtime reports no usable device — stub / no-toolkit builds, and also an emptyCUDA_VISIBLE_DEVICES.Refactor (needed to test the rule on CI, which has no GPU):
ensure_device_memory_availablenow queriescudaMemGetInfoand delegates the comparison toensure_fits_in_free_memory. Tests inject(free, total)throughensure_config_fits_device_with. Production path, error type, and message are unchanged; both helpers arepub(crate)/ private.The budget is two concurrent batch state buffers. That is deliberate: a
for qt in loader:loop keeps the previous DLPack tensor alive while the next batch is allocated. A config that fits one buffer but not two is rejected. There is no override.This budget is a floor, not the true peak: the encoders also upload the input batch to the device (
htod_sync_copy) and hold it alongside the state buffers — for amplitude, half a buffer, so the real steady-state peak is nearer 2.5. A configuration sitting just under free memory can therefore still OOM mid-run; this narrows that window rather than closing it. Modelling it properly means changing the memory model, which [Feature] Fail fast when estimated memory exceeds VRAM #1430 puts out of scope.Precision is the wider of the loader dtype and the engine precision. File loaders budget basis as float64 (indices are read as f64); synthetic basis is budgeted at the requested dtype.
The comparison is against free memory sampled at that moment — nothing is reserved. Another process or a second loader can take it before the first batch allocates, so passing the check is a fast sanity check, not a guarantee.
On the Python surface the check runs when iteration starts (
for qt in loader:), not whenQuantumDataLoader(...)returns. The rejection is aRuntimeErrorwrapping the RustMemoryAllocationmessage (the existing conversion for every backend error). A dedicated exception type is left as follow-up.qumat_qdp.estimate_memory()exposes the same arithmetic without opening a device, so a caller can sizequbits/batch_sizebefore iterating.CI covers the accept/reject rule with injected free-memory figures (no GPU required). Integration tests still exercise the live-GPU path and self-skip on stub builds.
Out of scope, per #1430: changing the underlying memory model (see the 2-buffer note above), automatic config downsizing, and multi-GPU accounting — the guard queries the current CUDA device and does not call
cudaSetDevice. Also not covered: host-side streaming chunk limits, and the benchmark helpers (run_throughput_pipeline/run_latency_pipeline), which build their own engine and bypassPipelineIterator.Verification
Run on a host with CUDA 12.5 and a live GPU:
make pre-commit— clean.make test_rust— 379 passed, 0 failed, 0 ignored.make test_python— 888 passed, 1 skipped (pre-existing, unrelated), 0 failed.QDP_NO_CUDA=1 cargo check --workspace --tests— clean; both new CI steps pass on the stub build (15 and 47 tests).cd website && npm run typecheck && npm run build— clean; the two broken-link warnings are pre-existing (verified by buildingupstream/main).Checklist