Skip to content

bench_parallel: add opt-in --memory-snapshot (CUDA memory snapshot) for full-graph models#76

Open
karthickai wants to merge 1 commit into
eellison:mainfrom
karthickai:full-graph-memory-snapshot
Open

bench_parallel: add opt-in --memory-snapshot (CUDA memory snapshot) for full-graph models#76
karthickai wants to merge 1 commit into
eellison:mainfrom
karthickai:full-graph-memory-snapshot

Conversation

@karthickai

Copy link
Copy Markdown
Contributor

Opt-in --memory-snapshot flag on the --full-graphs path dumps a CUDA memory snapshot per model (torch.cuda.memory._record_memory_history + _dump_snapshot) to <dir>/<tag>/<suite>/<mode>/<model>/<graph>.pickle, loadable in pytorch.org/memory_viz. This is the "Level 3" memory profiler (full allocation timeline + stacks + fragmentation), complementing --peak-memory (the number).

  • Separate run from the do_bench timing loop.
  • Per---tag subdir (default latest) so tagged A/B runs don't clobber; path base honors --models-root.
  • Parallel across GPUs, 1 worker/GPU (reuses the --peak-memory guard; per-process/per-GPU, verified with a 2-GPU sweep → distinct collision-free pickles).
  • Exception-guarded — an OOM/IO failure can't discard the already-measured timing; recording is always disabled again in a finally so it can't leak into the next model.
  • Opt-in / additive — off by default the result schema is unchanged; records the snapshot path in the result JSON. Adds a worker-script test.

@eellison eellison left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good - after #75 is landed, rebase /restructure this pr on that one?

Comment thread scripts/bench_parallel.py
os.makedirs(os.path.dirname(memory_snapshot) or ".", exist_ok=True)
with gpu_setup_lock():
with torch.no_grad():
for _ in range(3):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_record_memory_history starts AFTER 3 warmup iterations, so the snapshot captures steady-state only — compile-time + first-run allocations (often the actual peak!) are invisible. If the tool's purpose is debugging OOMs, peak usually happens during compile/first iteration. Suggest either a flag to record from process start, or explicitly documenting 'steady-state snapshot' in the help so nobody reads it as peak-memory forensics. Also: max_entries=100000 silently drops older events once exceeded — big models can blow through that; worth surfacing (or making it a flag).

Comment thread scripts/bench_parallel.py
memory_snapshot = None
if MEMORY_SNAPSHOT:
try:
_rel = os.path.relpath(repro_path, {args_dict.get("models_root", "repros/models")!r})

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asymmetry with #75: this derives _rel from args_dict['models_root'] (new plumbing) while #75 derives it from root + 'repros/models' inline. Whichever lands second should adopt the other's mechanism so the two artifact trees mirror identically (profiles//... and memory_snapshots//... diverge today if --models-root is non-default). models_root plumbing (this PR's approach) looks like the better one.

Comment thread scripts/bench_parallel.py

if args.peak_memory:
# --peak-memory: 1 worker per GPU.
if args.peak_memory or args.memory_snapshot:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consolidation with #75: this PR and #75 instantiate the same scaffold twice — this 1-worker/GPU gating (same line; textual conflict for whichever merges second), TAG plumbing, the relpath+fallback artifact-path derivation, the warmup→lock→instrumented-run→export→try/except block shape, and the near-identical injection test. Only the ~15-line core (profiler trace vs memory snapshot) differs. Suggest either stacking this on #75 (or merging both into one 'full-graph debug artifacts' PR), or having whichever lands second extract a shared capture-run helper (warmup/lock/path/makedirs/except, with the models_root-based path derivation from this PR as the single mechanism) so --profile, --memory-snapshot, and the existing --peak-memory become thin instantiations. Also note: if both flags are set, each runs its own extra warmup+instrumented pass — they should compose.

…or full-graph models

Add --memory-snapshot (+ --memory-snapshot-dir) to the --full-graphs path.
For each model, dump a CUDA memory snapshot (torch.cuda.memory._record_memory_
history + _dump_snapshot) to <dir>/<tag>/<suite>/<mode>/<model>/<graph>.pickle
for pytorch.org/memory_viz, recorded in a separate run from the do_bench timing.

Output is written under a per-<tag> subdir (tag defaults to "latest") so tagged
A/B runs don't clobber each other. Path base honors --models-root.

Parallel across GPUs but pinned to 1 worker/GPU (reuses the --peak-memory
guard). The snapshot run is exception-guarded so an OOM/IO failure can't discard
the already-measured timing, and recording is always disabled again in a finally
so it can't leak into the next model. Records the snapshot path in the result JSON.

Opt-in and additive: off by default the result schema is unchanged. Full-graph
path only. Adds a worker-script test.
@karthickai
karthickai force-pushed the full-graph-memory-snapshot branch from c87296c to 5afa67a Compare July 21, 2026 23:32
@karthickai

Copy link
Copy Markdown
Contributor Author

Thanks for the review! pushed fixes to updated the --memory-snapshot help text to note it's a steady-state snapshot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants