torch-remat 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- torch_remat/__init__.py +37 -0
- torch_remat/_annotate.py +111 -0
- torch_remat/_api.py +1686 -0
- torch_remat/_compat.py +73 -0
- torch_remat/_oom.py +137 -0
- torch_remat/_placeholder.py +180 -0
- torch_remat/_pytree.py +178 -0
- torch_remat/_recompute_boundary.py +83 -0
- torch_remat/_region.py +355 -0
- torch_remat/_reporting.py +564 -0
- torch_remat/_saved_report.py +505 -0
- torch_remat/_trace.py +192 -0
- torch_remat/_types.py +263 -0
- torch_remat/_view.py +194 -0
- torch_remat-0.2.0.dist-info/METADATA +196 -0
- torch_remat-0.2.0.dist-info/RECORD +19 -0
- torch_remat-0.2.0.dist-info/WHEEL +5 -0
- torch_remat-0.2.0.dist-info/licenses/LICENSE +28 -0
- torch_remat-0.2.0.dist-info/top_level.txt +1 -0
torch_remat/__init__.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This source code is licensed under the BSD-style license found in the
|
|
5
|
+
# LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from torch_remat._annotate import ( # noqa: F401
|
|
10
|
+
clear_memory_snapshot_cache,
|
|
11
|
+
memory_snapshot_annotate,
|
|
12
|
+
)
|
|
13
|
+
from torch_remat._api import ( # noqa: F401
|
|
14
|
+
checkpoint,
|
|
15
|
+
current_saved_tensor_info,
|
|
16
|
+
is_placeholder,
|
|
17
|
+
recompute_needs_tensor,
|
|
18
|
+
region,
|
|
19
|
+
save_for_backward,
|
|
20
|
+
saved_tensors_hooks,
|
|
21
|
+
)
|
|
22
|
+
from torch_remat._oom import ( # noqa: F401
|
|
23
|
+
discover_autograd_roots,
|
|
24
|
+
format_oom_saved_tensors_report,
|
|
25
|
+
oom_observer,
|
|
26
|
+
)
|
|
27
|
+
from torch_remat._region import is_recomputing, RecomputeStateHook # noqa: F401
|
|
28
|
+
from torch_remat._reporting import ( # noqa: F401
|
|
29
|
+
format_current_memory_report,
|
|
30
|
+
print_current_memory_report,
|
|
31
|
+
)
|
|
32
|
+
from torch_remat._saved_report import ( # noqa: F401
|
|
33
|
+
format_saved_tensors_report,
|
|
34
|
+
print_saved_tensors_report,
|
|
35
|
+
)
|
|
36
|
+
from torch_remat._trace import collect_trace, trace_scope # noqa: F401
|
|
37
|
+
from torch_remat._types import SavedTensorInfo, SavedTensorKind # noqa: F401
|
torch_remat/_annotate.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This source code is licensed under the BSD-style license found in the
|
|
5
|
+
# LICENSE file in the root directory of this source tree.
|
|
6
|
+
|
|
7
|
+
# pyre-strict
|
|
8
|
+
|
|
9
|
+
"""A default :data:`~torch_remat._reporting.Annotate` backed by the CUDA memory snapshot.
|
|
10
|
+
|
|
11
|
+
The saved-tensors report can tag each storage row with a caller-supplied label. By far the
|
|
12
|
+
most common label is *where the tensor was allocated*, which the CUDA caching allocator
|
|
13
|
+
already records (when memory-history recording is on). This module turns that snapshot into a
|
|
14
|
+
ready-made annotator -- the "which tensor is this" that a positional ``saved.<i>`` row cannot
|
|
15
|
+
give -- so callers don't reinvent the storage-pointer -> allocation-site mapping.
|
|
16
|
+
|
|
17
|
+
The snapshot walk is expensive, so the fetch is memoized (:func:`clear_memory_snapshot_cache`
|
|
18
|
+
resets it) and the built annotator indexes the snapshot once, reusing it across every
|
|
19
|
+
per-tensor lookup -- a whole report costs a single snapshot fetch.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import bisect
|
|
25
|
+
from typing import Any, Mapping
|
|
26
|
+
|
|
27
|
+
import torch
|
|
28
|
+
from torch_remat._reporting import Annotate
|
|
29
|
+
|
|
30
|
+
_snapshot_cache: Mapping[str, Any] | None = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _memory_snapshot(*, refresh: bool = False) -> Mapping[str, Any]:
|
|
34
|
+
"""Return the CUDA allocator memory snapshot, memoized after the first fetch.
|
|
35
|
+
|
|
36
|
+
``torch.cuda.memory._snapshot()`` walks the whole allocator and is expensive, so the
|
|
37
|
+
result is cached. Pass ``refresh=True`` (or call :func:`clear_memory_snapshot_cache`) to
|
|
38
|
+
re-fetch after more allocation. The snapshot carries allocation backtraces only while
|
|
39
|
+
memory-history recording is enabled.
|
|
40
|
+
"""
|
|
41
|
+
global _snapshot_cache
|
|
42
|
+
if refresh or _snapshot_cache is None:
|
|
43
|
+
_snapshot_cache = torch.cuda.memory._snapshot()
|
|
44
|
+
return _snapshot_cache
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def clear_memory_snapshot_cache() -> None:
|
|
48
|
+
"""Drop the memoized snapshot so the next annotator re-fetches (see :func:`_memory_snapshot`)."""
|
|
49
|
+
global _snapshot_cache
|
|
50
|
+
_snapshot_cache = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _alloc_site_from_frames(frames: list[dict[str, Any]] | None) -> str | None:
|
|
54
|
+
"""Pick a short ``file:line name`` label from an allocation backtrace (innermost first).
|
|
55
|
+
|
|
56
|
+
Prefer the innermost frame outside library internals (torch and torch_remat), which is
|
|
57
|
+
usually the model/kernel code that produced the tensor; fall back to the innermost frame
|
|
58
|
+
of any kind.
|
|
59
|
+
"""
|
|
60
|
+
for frame in frames or []:
|
|
61
|
+
filename = frame.get("filename", "")
|
|
62
|
+
if "/torch/" not in filename and "/torch_remat/" not in filename:
|
|
63
|
+
base = filename.rsplit("/", 1)[-1]
|
|
64
|
+
return f"{base}:{frame.get('line')} {frame.get('name', '')}".rstrip()
|
|
65
|
+
if frames:
|
|
66
|
+
frame = frames[0]
|
|
67
|
+
return f"{frame.get('filename', '?').rsplit('/', 1)[-1]}:{frame.get('line')}"
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def memory_snapshot_annotate(snapshot: Mapping[str, Any] | None = None) -> Annotate:
|
|
72
|
+
"""Return an :data:`Annotate` mapping a saved tensor's storage to its allocation site.
|
|
73
|
+
|
|
74
|
+
Each live allocator block carries its address and allocation backtrace, so a saved
|
|
75
|
+
tensor's storage pointer (which falls inside exactly one live block) resolves to the code
|
|
76
|
+
that allocated it. Pass the result as ``annotate=`` to
|
|
77
|
+
:func:`torch_remat.format_saved_tensors_report`.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
snapshot: A CUDA memory snapshot (``torch.cuda.memory._snapshot()``); defaults to the
|
|
81
|
+
cached :func:`_memory_snapshot`. The block index is built once here and reused by
|
|
82
|
+
every per-tensor lookup, so a whole report costs a single snapshot fetch.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
if snapshot is None:
|
|
86
|
+
snapshot = _memory_snapshot()
|
|
87
|
+
|
|
88
|
+
blocks: list[tuple[int, int, str]] = [] # (address, size, site), sorted by address
|
|
89
|
+
for segment in snapshot.get("segments", []):
|
|
90
|
+
for block in segment.get("blocks", []):
|
|
91
|
+
if block.get("state") != "active_allocated":
|
|
92
|
+
continue
|
|
93
|
+
site = _alloc_site_from_frames(block.get("frames"))
|
|
94
|
+
if site is not None:
|
|
95
|
+
blocks.append((block["address"], block["size"], site))
|
|
96
|
+
blocks.sort()
|
|
97
|
+
addrs = [address for address, _, _ in blocks]
|
|
98
|
+
|
|
99
|
+
def annotate(tensor: torch.Tensor) -> str | None:
|
|
100
|
+
try:
|
|
101
|
+
ptr = tensor.untyped_storage().data_ptr()
|
|
102
|
+
except Exception:
|
|
103
|
+
return None
|
|
104
|
+
index = bisect.bisect_right(addrs, ptr) - 1
|
|
105
|
+
if 0 <= index < len(blocks):
|
|
106
|
+
address, size, site = blocks[index]
|
|
107
|
+
if address <= ptr < address + size:
|
|
108
|
+
return site
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
return annotate
|