FIX: Prevent GCG model gradient accumulation#2244
Conversation
Compute coordinate gradients with input-only autograd, keep model ownership inside persistent workers, and stream prompt aggregation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5c847c5a-6917-4e2b-81a6-e511d9cbbe6e
There was a problem hiding this comment.
Pull request overview
This PR reduces GCG CUDA memory usage and improves throughput by preventing unnecessary model parameter-gradient materialization and by avoiding repeated model serialization in the multiprocessing worker pipeline, while keeping GCG behavior/API consistent.
Changes:
- Compute coordinate gradients via
torch.autograd.grad(loss, one_hot)to avoid accumulating per-parameter gradients. - Introduce typed worker operations/tasks (
ModelWorkerOperation,ModelWorkerTask) so queued multiprocessing work no longer carries a model payload per operation. - Stream prompt-gradient aggregation with FP32 accumulation for FP16/BF16 inputs, and remove
gc.collect()/ per-steptorch.cuda.empty_cache()from hot paths.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
pyrit/executor/promptgen/gcg/attack/gcg/gcg_attack.py |
Switches token-gradient computation to autograd.grad and updates worker dispatch to use typed operations without model payloads. |
pyrit/executor/promptgen/gcg/attack/base/attack_manager.py |
Adds ModelWorkerOperation/ModelWorkerTask, updates worker execution to use the persistent worker-owned model, and streams gradient reduction with FP32 accumulation where needed. |
tests/unit/executor/promptgen/gcg/test_gcg_core.py |
Expands unit coverage to validate spawn-safe task payloads, worker-owned model usage, streamed gradient reduction, and parity vs backward-based gradients. |
|
|
||
| @dataclass(frozen=True) | ||
| class ModelWorkerTask: | ||
| """A spawn-safe model worker task that excludes the worker-owned model.""" |
There was a problem hiding this comment.
Can we better document how this is relevant to the PR's goal of reducing overall memory usage? You added this in the PR body ("using typed, spawn-safe worker task payloads that exclude the model for grad, logits, test, and test-loss operations") but the description as-is is vague. Something like:
| """A spawn-safe model worker task that excludes the worker-owned model.""" | |
| """ | |
| A spawn-safe model worker task that excludes the worker-owned model. | |
| By assigning each worker a task with its own ModelWorkerOperation and | |
| tensor object / prompt (obj), the lifecycle and memory footprint of each operation | |
| is bounded, preventing memory overuse. | |
| """ |
That exact description is probably missing important context especially for obj which is just typed as Any but I think we should make sure it's explicit why we introduce this abstraction to handle memory usage
Description
GCG retained a full gradient tensor for every trainable model parameter while computing coordinate gradients, roughly doubling model-related CUDA memory. The multiprocessing path also serialized a model with every worker operation even though each worker already owned a persistent model.
This change addresses those root causes by:
torch.autograd.grad(loss, one_hot)gc.collect()and per-steptorch.cuda.empty_cache()calls from GCG hot pathsA fresh-process TinyLlama 1.1B FP16 benchmark on an RTX 2000 Ada 8 GB ran 8 steps across 3 repeats in both local and multiprocessing modes (96 measured steps). All paired loss traces were bitwise identical and live allocations remained flat across every local run.
origin/mainThe live and peak allocation reductions independently confirm that this is not merely allocator-cache trimming. Benchmark scripts and raw results were kept outside the repository.
Closes #961
Tests and Documentation
uv run pytest tests\unit\executor\promptgen\gcg\test_gcg_core.py -q(56 passed)uv run ruff format --checkon the changed filesuv run ruff checkon the changed filesuv run ty checkon the changed filesNo documentation changes are needed because this preserves the existing GCG API and attack behavior.