理解 CUDA 内存使用¶
为了调试 CUDA 内存使用情况,PyTorch 提供了一种生成内存快照的方法,可以记录任意时间点已分配 CUDA 内存的状态,并可选择记录导致该快照的分配事件历史。
生成的数据可以拖放到 pytorch.org/memory_viz 上托管的交互式查看工具中,用于探索快照。
生成快照¶
记录快照的常见模式是:启用内存历史记录,运行待观察的代码,然后将序列化(pickled)的快照保存到文件中。
# enable memory history, which will
# add tracebacks and event history to snapshots
torch.cuda.memory._record_memory_history()
run_your_code()
torch.cuda.memory._dump_snapshot("my_snapshot.pickle")
使用可视化工具¶
打开 pytorch.org/memory_viz 并将序列化(pickled)的快照文件拖放到可视化工具中。该可视化工具是一个在您计算机本地运行的 JavaScript 应用程序。它不会上传任何快照数据。
活动内存时间轴¶
活动内存时间轴显示了特定 GPU 上快照中所有存活的张量随时间的变化。平移/缩放图表以查看较小的分配。将鼠标悬停在已分配的内存块上,可以查看该内存块分配时的堆栈跟踪以及诸如其地址之类的详细信息。可以调整详细信息滑块来渲染较少的分配,从而在数据量较大时提高性能。

分配器状态历史记录¶
分配器状态历史记录在左侧时间轴中显示单个分配器事件。在时间轴中选择一个事件,以查看该事件发生时分配器状态的可视化摘要。该摘要显示了 cudaMalloc 返回的每个独立段,以及它如何被分割成独立的分配块或空闲空间。将鼠标悬停在段和块上,可以查看分配内存时的堆栈跟踪。将鼠标悬停在事件上,可以查看事件发生时的堆栈跟踪,例如张量被释放时。内存不足错误会报告为 OOM 事件。查看 OOM 期间的内存状态可以帮助理解为什么即使保留内存仍然存在,分配也会失败。

堆栈跟踪信息还会报告发生分配的地址。地址 b7f064c000000_0 指的是地址 7f064c000000 处的内存块 (b),它是该地址第“_0”次被分配。可以在活动内存时间轴中查找并活动状态历史记录中搜索这个唯一字符串,以检查张量分配或释放时的内存状态。
快照 API 参考¶
- torch.cuda.memory._record_memory_history(enabled='all', context='all', stacks='all', max_entries=9223372036854775807, device=None)[source][source]¶
启用记录与内存分配相关的堆栈跟踪,以便您知道
torch.cuda.memory._snapshot()
中任何一块内存是由什么分配的。除了保留每次当前分配和释放的堆栈跟踪外,此功能还将启用记录所有分配/释放事件的历史记录。
使用
torch.cuda.memory._snapshot()
检索此信息,并使用 _memory_viz.py 中的工具来可视化快照。Python 跟踪收集速度很快(每次跟踪 2 微秒),因此如果您预计需要调试内存问题,可以考虑在生产作业中启用此功能。
C++ 跟踪收集速度也很快(约 50 纳秒/帧),对于许多典型程序来说,这相当于每次跟踪约 2 微秒,但可能因堆栈深度而异。
- 参数
enabled (Literal[None, "state", "all"], 可选) – None,禁用内存历史记录;“state”,保留当前已分配内存的信息;“all”,额外保留所有分配/释放调用的历史记录。默认为 "all"。
context (Literal[None, "state", "alloc", "all"], 可选) – None,不记录任何回溯;“state”,记录当前已分配内存的回溯;“alloc”,额外保留分配调用的回溯;“all”,额外保留释放调用的回溯。默认为 "all"。
stacks (Literal["python", "all"], 可选) – “python”,在回溯中包含 Python、TorchScript 和 Inductor 帧;“all”,额外包含 C++ 帧。默认为 "all"。
max_entries (int, 可选) – 在记录的历史记录中最多保留 max_entries 个分配/释放事件。
- torch.cuda.memory._snapshot(device=None)[source][source]¶
保存调用时 CUDA 内存状态的快照。
状态表示为一个具有以下结构的字典。
class Snapshot(TypedDict): segments : List[Segment] device_traces: List[List[TraceEntry]] class Segment(TypedDict): # Segments are memory returned from a cudaMalloc call. # The size of reserved memory is the sum of all Segments. # Segments are cached and reused for future allocations. # If the reuse is smaller than the segment, the segment # is split into more then one Block. # empty_cache() frees Segments that are entirely inactive. address: int total_size: int # cudaMalloc'd size of segment stream: int segment_type: Literal['small', 'large'] # 'large' (>1MB) allocated_size: int # size of memory in use active_size: int # size of memory in use or in active_awaiting_free state blocks : List[Block] class Block(TypedDict): # A piece of memory returned from the allocator, or # current cached but inactive. size: int requested_size: int # size requested during malloc, may be smaller than # size due to rounding address: int state: Literal['active_allocated', # used by a tensor 'active_awaiting_free', # waiting for another stream to finish using # this, then it will become free 'inactive',] # free for reuse frames: List[Frame] # stack trace from where the allocation occurred class Frame(TypedDict): filename: str line: int name: str class TraceEntry(TypedDict): # When `torch.cuda.memory._record_memory_history()` is enabled, # the snapshot will contain TraceEntry objects that record each # action the allocator took. action: Literal[ 'alloc' # memory allocated 'free_requested', # the allocated received a call to free memory 'free_completed', # the memory that was requested to be freed is now # able to be used in future allocation calls 'segment_alloc', # the caching allocator ask cudaMalloc for more memory # and added it as a segment in its cache 'segment_free', # the caching allocator called cudaFree to return memory # to cuda possibly trying free up memory to # allocate more segments or because empty_caches was called 'oom', # the allocator threw an OOM exception. 'size' is # the requested number of bytes that did not succeed 'snapshot' # the allocator generated a memory snapshot # useful to coorelate a previously taken # snapshot with this trace ] addr: int # not present for OOM frames: List[Frame] size: int stream: int device_free: int # only present for OOM, the amount of # memory cuda still reports to be free
- 返回值
快照字典对象