Repository navigation
[Bugfix] Collect garbage before CUDA graph capture - #141
Merged
hsliuustc0106 merged 1 commit intoOct 24, 2026
Merged
Conversation
torch.cuda.graph no longer runs gc.collect() before capture (torch.compiler.config.force_cudagraph_gc defaults to False). Reference cycles left by an earlier engine, holding CUDA graphs, streams and pinned buffers, then survive into a later capture, and an allocation inside it can run the collector and free that CUDA state mid-capture. That invalidates the capture or aborts the process. Collect explicitly after warmup instead. Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
|
No actionable findings. Reviewed head The GC placement at The regression test preserves an earlier engine’s cycles, forces collection during the next capture, requires actual captures, and compares exact tokens and exit depths.
|
This was referenced Oct 24, 2026
0z5a
added a commit
to 0z5a/vllm-rlt
that referenced
this pull request
Oct 25, 2026
Picks up InferMatrixLab#128 (now merged) and the pre-capture gc.collect() from InferMatrixLab#141. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: 0z5a <dezhen.lu@student.uni-tuebingen.de>
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.
Required human contributor answers
1. What outcome are you trying to achieve?
Fix the abort we hit while iterating: the process aborted during CUDA graph capture.
2. What is the highest-leverage variable for that outcome?
Whether the Python garbage collector can run, and free CUDA state, in the middle of a CUDA graph capture. (Answers 2 and 3 were drafted by the agent at the contributor's explicit request.)
3. How will this work move that variable rather than create busywork?
One
gc.collect()before each capture closes that window, so failure no longer depends on when the collector happens to fire. Captures are rare and bounded by the graph cache, so replay pays nothing. The new test forces the collection inside a capture, so the bug is reproduced deterministically instead of relying on GC timing.4. Who is the customer or user, and what do they actually need?
Users running on SM90 GPUs. They need CUDA graph capture not to abort or be invalidated.
PR necessity and bugfix evidence
The human contributor decided on 2026-10-22 to fix this in its own PR against
mainrather than inside #130. Observed failure: a full GPU regression run of #130 at9874287aborted (Fatal Python error: Aborted, Python stack inGarbage-collectingundervllm_rlt/worker/cuda_graph.py::_capture) duringtest_mixed_execution; see the #130 reply. It was reproduced on a GH200, intermittently in the full suite and deterministically with the new test below.Purpose
torch.cuda.graphno longer runsgc.collect()before capture; since PyTorch made it opt-in, it collects only whentorch.compiler.config.force_cudagraph_gc(TORCH_CUDAGRAPH_GC) is set, which defaults toFalse. Reference cycles left by an earlier engine, holding CUDA graphs, streams, CUDA tensors and pinnedUvaBuffers, then survive into a later capture. When an allocation inside that capture triggers the collector, the CUDA state is freed mid-capture: the capture is invalidated (cudaErrorStreamCaptureInvalidated) or the process aborts from a destructor._capturenow callsgc.collect()after the warm-up runs and before the capture. Captures are rare (bounded by the graph cache), so the collection is not on the replay path.How it was found: forcing one collection inside every capture, across
test_attention*,test_speculative,test_async_speculative,test_cuda_graph,test_kv_cache,test_split_prefillandtest_mixed_execution, gave 13 failures (12cudaErrorStreamCaptureInvalidated, 1 invalid argument). At that point up to 995 unreachable objects were pending, including 2–4CUDAGraphs and up to 85 CUDA tensors. With the fix, every one of 80 probed captures found 0 unreachable objects, and all 255 tests passed. Which freed object issues the illegal CUDA call (graph destruction, private-pool release, pinned-host free or stream destruction) was not isolated; the fix does not depend on it.Test Plan
vllm-rlt Version / Commit:
45565cconmain4a35034.tests/test_cuda_graph.py::test_capture_survives_collecting_an_earlier_engines_garbage. With the collector disabled, it runs one engine with CUDA graphs and drops it, leaving its cycles. A second engine then collects garbage inside its first capture; its outputs must match the first engine's.python -m pytest --run-gpu -q -rA -k 'not flash_attn' tests/test_{attention,attention_64bit,speculative,async_speculative,cuda_graph,kv_cache,split_prefill,mixed_execution,huginn,input_preparation,backend_selection}.pypython -m pytest -q -m "not gpu"; Ruff check and format.Test Result
One GH200 (SM90), torch 2.14.0+cu130, Triton 3.8.0, transformers 4.55.0, Python 3.12. The GPU runs used a tree whose two changed files match
45565ccby SHA256.main4a35034: aborts in 2 of 2 runs (Fatal Python error: Aborted,Garbage-collectinginside_capture). With the fix: passes in 3 of 3 runs (one on the exact tree; two on an earlier copy that lacked only the three comment lines).test_input_preparation.py::test_packed_metadata_supports_query_lengths_and_per_request_depths[*], which select FA4 (ModuleNotFoundError: No module named 'flash_attn'); FA4 is not installed on this host. Unrelated to this change.