已开启
【社区跟随】_dump_snapshot特性补齐 #5310
xuyun15创建于 21 天前
21 天前 添加了label:triage-reviewmodule: tools
TorchNPU-Bot
21 天前 评论:
21 天前 评论:
无报错,属功能建议/feature request;主题为内存调试工具 _dump_snapshot 特性补齐
添加分类标签:module: tools。
当前分类由社区bot提供,如分类有误请在评论中回复。


15 天前 关联了pull request:feat: add NPU memory history recording controls
15 天前 添加了label:bot-triaged;删除了label:triage-review
TorchNPU-Bot
15 天前 评论:
15 天前 评论:
检测到当前 issue 已关联 PR,自动添加标签:bot-triaged


12 天前 关联了pull request:feat: add skip_actions filtering to NPU memory history
11 天前 关联了pull request:refactor: reuse upstream RingBuffer for NPU memory history
10 天前 关联了pull request:feat: support compile_context in NPU memory history
10 天前 关联了pull request:feat: support global_record_annotations in NPU memory history
10 天前 关联了pull request:feat: add allocator_settings to memory snapshot
9 天前 关联了pull request:feat: add allocator_settings to memory snapshot
长期任务
PyTorch Feature 设计与实现分析
模块设计目标与背景
为什么需要这个 feature
_dump_snapshot是 PyTorch 内存调试体系中的核心功能,用于将当前设备内存分配器的完整状态(包括 segment 布局、block 状态、分配/释放历史 trace)序列化保存到文件。该功能主要服务于:不使用它会怎样
没有
_dump_snapshot,OOM 发生时只能看到简单的错误信息,无法回溯哪些 tensor 占用了内存、哪些调用栈分配了内存,排查效率极低。在整体架构中的位置
_dump_snapshot位于torch.cuda.memory/torch.npu.memory模块的底层,是内存调试工具链(_record_memory_history→_snapshot→_dump_snapshot→ 可视化)的输出端。长期任务
整体设计架构
核心组件说明
_dump_snapshot(Python)_snapshot()获取快照并 pickle 写入文件_snapshot(Python)_record_memory_history(Python)THNPModule_memorySnapshot(C++)_memory_snapshot_pickled(C++)_record_memory_history(C++)NPUCachingAllocator::snapshot()(C++)NPUWorkspaceAllocator::snapshot()(C++)OptionsManager::IsOomSnapshotEnable()(C++)整体执行流程
flowchart TD A[用户调用 _dump_snapshot] --> B[_snapshot 获取快照] B --> C[torch_npu._C._npu_memorySnapshot] C --> D[THNPModule_memorySnapshot C++] D --> E[NPUCachingAllocator::snapshot] D --> F[NPUWorkspaceAllocator::snapshot] E --> G[构建 segments + device_traces] F --> G G --> H[py_symbolize 符号化调用栈] H --> I[返回 py::dict] I --> J[pickle.dump 写入文件] J --> K[_npu_saveDevMemUsageInfo NPU特有] K --> L{保存成功?} L -->|是| M[完成] L -->|否| N[启动 profiler 采集 NPU特有] N --> M入口分析
入口1:
torch.npu.memory._dump_snapshot文件路径:
torch_npu/npu/memory.py:994入口函数:
_dump_snapshot(filename="dump_snapshot.pickle", augment_with_fx_traces=False)职责:将内存快照 pickle 序列化写入文件,可选 FX trace 增强,并额外保存设备内存使用信息
调用场景:
oom_observer自动调用入口2:
torch.cuda.memory._dump_snapshot(原生实现)文件路径:
torch/cuda/memory.py:1156入口函数:
_dump_snapshot(filename="dump_snapshot.pickle", augment_with_fx_traces=False)职责:将内存快照 pickle 序列化写入文件,可选增强 FX trace 信息
调用场景:同上
接口使用说明
CUDA 原生用法:
import torch torch.cuda.memory._record_memory_history(enabled="all", context="all", stacks="python") # ... 执行模型代码 ... torch.cuda.memory._dump_snapshot("my_snapshot.pickle") torch.cuda.memory._record_memory_history(enabled=None) # 关闭记录NPU 当前用法:
import torch_npu torch_npu.npu.memory._record_memory_history(enabled="all", context="all", stacks="python") # ... 执行模型代码 ... torch_npu.npu.memory._dump_snapshot("my_snapshot.pickle") torch_npu.npu.memory._record_memory_history(enabled=None) # 关闭记录完整调用链分析
阶段1:Python 入口调用
函数:
_dump_snapshot(filename="dump_snapshot.pickle", augment_with_fx_traces=False)文件:
torch_npu/npu/memory.py:994职责:获取快照并写入文件
关键逻辑(
torch_npu/npu/memory.py:1006-1022):s = _snapshot(augment_with_fx_traces=augment_with_fx_traces) with open(filename, "wb") as f: pickle.dump(s, f) # NPU 特有:额外保存设备内存使用信息 device = torch_npu.npu.current_device() save_success = torch_npu._C._npu_saveDevMemUsageInfo(device) if not save_success: # 回退到 profiler 采集 prof_path = os.path.dirname(os.path.abspath(filename)) activities = {torch_npu.profiler.ProfilerActivity.CPU, torch_npu.profiler.ProfilerActivity.NPU} torch_npu._C._profiler._init_profiler(prof_path, activities) prof_config = [prof_path, False, True, False, False, False, torch_npu._C._profiler._ExperimentalConfig()] npu_prof_config = torch_npu._C._profiler.NpuProfilerConfig(*tuple(prof_config)) torch_npu._C._profiler._start_profiler(npu_prof_config, activities) torch_npu._C._profiler._stop_profiler() torch_npu._C._profiler._finalize_profiler()对比 CUDA 原生(
torch/cuda/memory.py:1172-1175):s = _snapshot(augment_with_fx_traces=augment_with_fx_traces) with open(filename, "wb") as f: pickle.dump(s, f)关键差异:
open(filename, "wb")(旧版 NPU 曾用os.fdopen(os.open(...)),现已统一)augment_with_fx_traces参数已对齐:NPU 与 CUDA 均支持该参数_npu_saveDevMemUsageInfo,CUDA 无此逻辑↓
阶段2:_snapshot 调用
函数:
_snapshot(device=None, augment_with_fx_traces=False)文件:
torch_npu/npu/memory.py:902职责:调用 C++ 绑定获取完整快照 dict,可选 FX trace 增强
关键逻辑(
torch_npu/npu/memory.py:987-991):s = torch_npu._C._npu_memorySnapshot(None) if augment_with_fx_traces: from torch._utils import _augment_memory_snapshot_stack_traces s = _augment_memory_snapshot_stack_traces(s) return s对比 CUDA 原生(
torch/cuda/memory.py:1150-1153):s = _C._cuda_memorySnapshot(None) if augment_with_fx_traces: s = _augment_memory_snapshot_stack_traces(s) return s关键差异:
augment_with_fx_traces,均调用_augment_memory_snapshot_stack_tracesfrom ... import(延迟导入),CUDA 在文件顶部 import_npu_memorySnapshot不接受参数(传None占位),CUDA 的_cuda_memorySnapshot接受(mempool_id_first, mempool_id_second, include_traces)元组device参数均未实际使用:两侧声明device但均未传给 C++↓
阶段3:C++ 绑定层 - THNPModule_memorySnapshot
函数:
THNPModule_memorySnapshot(PyObject*, PyObject*)文件:
torch_npu/csrc/npu/Module.cpp:1274职责:调用 allocator snapshot(),构建 Python dict 返回
关键逻辑:
c10_npu::NPUCachingAllocator::snapshot()获取主分配器快照c10_npu::NPUWorkspaceAllocator::snapshot()获取 workspace 分配器快照py_symbolize(to_gather_frames)对比 CUDA 原生
THCPModule_memorySnapshot:mempool_id和include_traces过滤segments,device_traces,allocator_settings,external_annotationssegments,device_tracesallocator_settings(分配器配置元数据)external_annotations(外部标注数据)time_us(时间戳)、compile_context、user_metadata、internal_metadata字段WORKSPACE_SNAPSHOTtrace action,CUDA 无此概念pool_id字段(与 CUDA 一致)annotateaction,NPU 无此概念↓
阶段4:C++ _record_memory_history
函数:
_record_memory_history(enabled, context, stacks, max_entries)文件:
torch_npu/csrc/npu/memory_snapshot.cpp:37职责:配置内存历史记录策略
关键逻辑:
c10_npu::NPUCachingAllocator::recordHistory(enabled.has_value(), recorder, max_entries, when); c10_npu::NPUWorkspaceAllocator::recordHistory(enabled.has_value(), recorder, when);对比 CUDA 原生:
clearHistory,compileContext,globalRecordAnnotations,skip_actions,record_pinned_host_memory,record_cudasetRecordFunctionCallbacks管理 annotation/compile context 回调clearHistory(清除历史记录)compileContext(编译上下文标注)globalRecordAnnotations(全局标注记录)skip_actions(跳过指定 action 类型的记录)record_pinned_host_memory(记录 host pinned 内存)record_cuda(控制是否记录 device 内存)NPUWorkspaceAllocator::recordHistory,CUDA 无 workspace 概念↓
阶段5:Python _record_memory_history 入口
函数:
_record_memory_history(enabled="all", *args, **kwargs)文件:
torch_npu/npu/memory.py:851/torch/cuda/memory.py:898NPU 实现(
torch_npu/npu/memory.py:851-888):def _record_memory_history(enabled="all", *args, **kwargs): return _record_memory_history_impl(enabled, *args, **kwargs)CUDA 实现(
torch/cuda/memory.py:898-996):def _record_memory_history(enabled="all", *args, **kwargs): if isinstance(enabled, bool): return _record_memory_history_legacy(enabled, *args, **kwargs) else: return _record_memory_history_impl(enabled, *args, **kwargs)关键差异:
enabled为 bool 时走_record_memory_history_legacy(torch/cuda/memory.py:867-895),NPU 无此分支__signature__覆盖:CUDA 在torch/cuda/memory.py:1027有_record_memory_history.__signature__ = signature(_record_memory_history_impl),NPU 无NPU
_record_memory_history_impl(torch_npu/npu/memory.py:891-899):def _record_memory_history_impl( enabled: Optional[str] = "all", context: Optional[str] = "all", stacks: str = "all", max_entries: int = sys.maxsize, device=None, ): torch_npu.npu._lazy_init() torch_npu._C._npu_record_memory_history(enabled, context, stacks, max_entries)CUDA
_record_memory_history_impl(torch/cuda/memory.py:999-1024):def _record_memory_history_impl( enabled: str | None = "all", context: str | None = "all", stacks: str = "all", max_entries: int = sys.maxsize, device: "Device" = None, clear_history: bool = False, compile_context: bool = False, global_record_annotations: bool = False, skip_actions: list[str] | None = None, record_pinned_host_memory: bool = False, record_cuda: bool = True, ): _C._cuda_record_memory_history( enabled, context, stacks, max_entries, clear_history, compile_context, global_record_annotations, skip_actions if skip_actions is not None else [], record_pinned_host_memory, record_cuda, )关键差异:
clear_history、compile_context、global_record_annotations、skip_actions、record_pinned_host_memory、record_cudatorch_npu.npu._lazy_init()调用,CUDA 无↓
阶段6:OOM 自动 dump
函数:
oom_observer(device, allocated, device_total, device_free)文件:
torch_npu/csrc/core/npu/register/OptionsManager.cpp职责:OOM 发生时自动调用 Python
_dump_snapshot关键逻辑:
auto module = THPObjectPtr(PyImport_ImportModule("torch_npu.npu.memory")); PyObject* p_func = PyObject_GetAttrString(module, "_dump_snapshot"); PyObject_CallObject(p_func, p_args);配置入口:环境变量
OOM_SNAPSHOT_ENABLE完整调用链总结
sequenceDiagram participant User as 用户代码 participant PyMem as npu/memory.py participant CBind as Module.cpp (C++绑定) participant Alloc as NPUCachingAllocator participant WS as NPUWorkspaceAllocator participant Sym as py_symbolize User->>PyMem: _dump_snapshot(filename) PyMem->>PyMem: _snapshot(augment_with_fx_traces) PyMem->>CBind: _npu_memorySnapshot() CBind->>Alloc: snapshot() Alloc-->>CBind: SnapshotInfo (segments, traces) CBind->>WS: snapshot() WS-->>CBind: SnapshotInfo (workspace segments, traces) CBind->>CBind: 合并 workspace 数据 CBind->>Sym: py_symbolize(tracebacks) Sym-->>CBind: 符号化后的 frames CBind-->>PyMem: py::dict {segments, device_traces} PyMem->>PyMem: pickle.dump 写入文件 PyMem->>Alloc: _npu_saveDevMemUsageInfo(device) alt 保存失败 PyMem->>PyMem: 启动 profiler 采集 end扩展点分析
NPU 与 CUDA 功能差异全景
_dump_snapshot参数filename,augment_with_fx_tracesfilename,augment_with_fx_traces_dump_snapshot文件写入open(filename, "wb")open(filename, "wb")os.fdopen(os.open(...)))_dump_snapshot兜底逻辑_npu_saveDevMemUsageInfo+ profiler 回退_snapshot参数device,augment_with_fx_tracesdevice,augment_with_fx_tracesdevice两侧均未实际使用)_snapshotFX 增强_augment_memory_snapshot_stack_traces_augment_memory_snapshot_stack_tracesmemory_snapshot参数mempool_id,include_tracesmempool_idinclude_traces(无法轻量快照)_npu_memorySnapshotC++ 参数mempool_id,include_tracesNone占位)segments,device_traces,allocator_settings,external_annotationssegments,device_tracesaction,addr/device_free,size,stream,time_us,pool_id,frames,compile_context,user_metadata,internal_metadataaction,addr/device_free,size,stream,pool_id,framesalloc,free_requested,free_completed,segment_alloc,segment_free,oom,snapshot,annotatealloc,free_requested,free_completed,segment_alloc,segment_free,oom,snapshot,WORKSPACE_SNAPSHOTannotate,多WORKSPACE_SNAPSHOT_record_memory_history参数enabled,context,stacks,max_entries,clear_history,compile_context,global_record_annotations,skip_actions,record_pinned_host_memory,record_cudaenabled,context,stacks,max_entries_record_memory_historybool 兼容_record_memory_history_legacy)_record_memory_history.__signature___allocation_tracebacktorch/cuda/memory.py:1030)_set_memory_metadata/_get_memory_metadatatorch/cuda/memory.py:1193,1231)_memory_metadata_supportedtorch/cuda/memory.py:1178)_record_memory_history_legacytorch/cuda/memory.py:867)caching_allocator_enable/caching_allocator_disabledtorch/cuda/memory.py:157,162)list_gpu_processestorch/cuda/memory.py:782)mem_get_infotorch/cuda/memory.py:848)_npu_saveDevMemUsageInfo+ profiler 回退WORKSPACE_SNAPSHOT可扩展点
memory_snapshot的include_traces参数torch_npu/npu/memory.py::memory_snapshotinclude_traces给 C++,需 C++ 绑定支持mempool_id/include_traces参数(C++ 绑定)torch_npu/csrc/npu/Module.cpp::THNPModule_memorySnapshotallocator_settings返回字段torch_npu/csrc/npu/Module.cpp::THNPModule_memorySnapshotexternal_annotations返回字段torch_npu/csrc/npu/Module.cpp::THNPModule_memorySnapshottime_us字段torch_npu/csrc/npu/Module.cpp::THNPModule_memorySnapshot,NPUCachingAllocator.h/cppnpu_memory_snapshot_timestamp_gap_analysis.md)compile_context字段Module.cpp, allocator 侧user_metadata/internal_metadata字段Module.cpp, allocator 侧annotateactionNPUCachingAllocator.hTraceEntry::Action 枚举_record_memory_history的clear_history参数memory_snapshot.cpp::_record_memory_history, Python 层NPUCachingAllocator::recordHistory_record_memory_history的skip_actions参数memory_snapshot.cpp::_record_memory_history, Python 层NPUCachingAllocator::recordHistory_record_memory_history的compile_context/global_record_annotationsmemory_snapshot.cpp, 需实现setRecordFunctionCallbacks_record_memory_history的record_pinned_host_memory/record_cudamemory_snapshot.cpp, allocator 侧_record_memory_history_legacy兼容路径torch_npu/npu/memory.py_record_memory_history.__signature__覆盖torch_npu/npu/memory.pyfrom inspect import signature后覆盖_allocation_traceback函数torch_npu/npu/memory.py, C++ 绑定NPUCachingAllocator::allocationTraceback_set_memory_metadata/_get_memory_metadata/_memory_metadata_supportedtorch_npu/npu/memory.py, C++ 绑定caching_allocator_enable/caching_allocator_disabledtorch_npu/npu/memory.py, C++ 绑定list_gpu_processestorch_npu/npu/memory.py, C++ 绑定mem_get_infotorch_npu/npu/memory.py, C++ 绑定修改已有行为
改
_dump_snapshot使其与 CUDA 一致:Python 层 (
torch_npu/npu/memory.py):_dump_snapshot:augment_with_fx_traces参数与 FX 增强已对齐,无需改动_snapshot:FX 增强已对齐,无需改动_npu_saveDevMemUsageInfo和 profiler 回退逻辑:NPU 独有扩展,建议保留(或作为可选扩展)open(filename, "wb")C++ 绑定层 (
torch_npu/csrc/npu/Module.cpp):THNPModule_memorySnapshot从 METH_NOARGS 改为 METH_O(mempool_id_0, mempool_id_1, include_traces)参数元组NPUCachingAllocator::snapshot(mempool_id, include_traces)allocator_settings和external_annotations构建逻辑C++ 实现层 (
torch_npu/csrc/npu/memory_snapshot.cpp):_record_memory_history增加clearHistory,compileContext,globalRecordAnnotations,skip_actions,record_pinned_host_memory,record_cuda参数setRecordFunctionCallbacks机制哪些逻辑不能乱改:
NPUCachingAllocator::snapshot()的数据结构SnapshotInfo是核心数据源,修改需谨慎py_symbolize的符号化逻辑与 traceback 采集紧密耦合,不可随意修改pybind11::gil_scoped_acquire) 必须保留_npu_saveDevMemUsageInfo+ profiler 回退是 NPU 独有扩展,与 time_us 补齐无耦合,建议保留常见扩展模式
TraceEntry::Action枚举中增加,然后在action_to_strlambda 中增加映射THNPModule_memorySnapshot构建 trace_entry dict 时增加 key-value,同时需在TraceEntry结构体中加字段allocator_settingsmemory.py中定义函数,在__all__中导出,必要时增加 C++ 绑定容易踩坑的地方
_npu_memorySnapshot是 METH_NOARGS,CUDA 是 METH_O。修改时需同步更新PyMethodDef表和函数签名,否则会 segfaultbegin()),修改合并逻辑需注意顺序对可视化工具的影响_dump_snapshot,如果_dump_snapshot内部逻辑变更导致需要额外 GIL 操作,可能死锁_npu_saveDevMemUsageInfo的 profiler 回退:当前回退逻辑硬编码了 profiler 配置参数(torch_npu/npu/memory.py:1014-1022),修改需确保与 profiler 模块兼容_dump_snapshot生成的 pickle 文件需与 pytorch.org/memory_viz 可视化工具兼容,新增字段不应破坏已有字段的格式_memory_snapshot_pickled与THNPModule_memorySnapshot双实现:两者逻辑需保持同步,修改一处需同步修改另一处(注意前者当前是死代码,未绑定到 Python)gather(),gather_with_cpp(),py_symbolize在不同平台调用不同实现,修改需覆盖两个平台C10_ARMTSC未启用,record_trace会回退到clock_gettimesyscall,热路径性能下降(详见npu_memory_snapshot_timestamp_gap_analysis.md)总结
feature 核心工作机制
_dump_snapshot通过调用 C++ 绑定_npu_memorySnapshot获取内存分配器的完整状态快照(segments 布局 + 分配/释放 trace 历史),将快照 dict pickle 序列化写入文件。NPU 侧额外调用_npu_saveDevMemUsageInfo保存设备级内存使用信息,失败时回退到 npu profiler 采集。核心调用路径
核心数据结构
SnapshotInfo:包含segments(vectordevice_traces(vector<vectorSegmentInfo:segment 地址、大小、pool id、expandable 标记、block 列表BlockInfo:block 地址、大小、requested_size、状态(active_allocated / active_pending_free / inactive)TraceEntry:action 类型、地址、大小、stream、调用栈上下文、pool_id(缺 time_us / compile_context / user_metadata / internal_metadata)当前对齐状态(已对齐项)
_dump_snapshot参数(filename,augment_with_fx_traces)_dump_snapshot文件写入方式(open(filename, "wb"))_snapshot参数与 FX trace 增强(_augment_memory_snapshot_stack_traces)memory_snapshot的mempool_id参数(部分对齐,NPU 缺include_traces)pool_id字段当前主要差异(未对齐项)
time_us:NPU 的 TraceEntry 无时间字段,无法做时间轴可视化(详见npu_memory_snapshot_timestamp_gap_analysis.md)compile_context/user_metadata/internal_metadata:NPU 无元数据标注能力annotate:NPU 无 annotation 事件类型allocator_settings/external_annotations:NPU 缺配置元数据和标注_record_memory_history缺 6 个参数:clear_history/compile_context/global_record_annotations/skip_actions/record_pinned_host_memory/record_cuda_record_memory_history_legacy与__signature__:无 bool 入参兼容,IDE 签名不完整_allocation_traceback:无法按 data_ptr 查询分配栈_set_memory_metadata/_get_memory_metadata/_memory_metadata_supported:无自定义元数据标注caching_allocator_enable/caching_allocator_disabled:无分配器启停控制list_gpu_processes/mem_get_info:无进程级内存查看、无全局空闲/总量查询memory_snapshot缺include_traces:无法轻量快照mempool_id/include_traces参数:无法按 pool 过滤推荐阅读顺序
torch_npu/npu/memory.py— Python 层入口,理解_dump_snapshot/_snapshot/_record_memory_history的接口torch/cuda/memory.py— CUDA 原生实现,理解目标对齐形态torch_npu/csrc/npu/Module.cpp— C++ 绑定层,理解快照 dict 的构建过程torch_npu/csrc/npu/memory_snapshot.cpp— C++ 实现层,理解 traceback 采集和 pickle 序列化torch/csrc/cuda/Module.cpp— CUDA C++ 绑定层,对比差异torch/csrc/cuda/memory_snapshot.cpp— CUDA C++ 实现层,对比差异torch_npu/csrc/core/npu/register/OptionsManager.cpp— OOM 自动 dump 机制npu_memory_snapshot_timestamp_gap_analysis.md(同目录) — time_us 缺失的根因分析与补齐方案如果要扩展,先看哪些文件
torch/cuda/memory.py对应实现,再改torch_npu/npu/memory.py和torch_npu/csrc/npu/Module.cpptorch/csrc/cuda/memory_snapshot.cpp,再改torch_npu/csrc/npu/memory_snapshot.cpptorch_npu/csrc/core/npu/register/OptionsManager.cppnpu_memory_snapshot_timestamp_gap_analysis.md,涉及 4 层联动修改建议下一步阅读路径
torch/cuda/memory.py的_dump_snapshot/_snapshot/_record_memory_history/_record_memory_history_legacy/_allocation_traceback/_set_memory_metadata完整实现torch_npu/npu/memory.py同名函数,逐项标记缺失功能THCPModule_memorySnapshotvsTHNPModule_memorySnapshot的返回值结构差异_record_memory_history的参数差异,评估哪些参数对 NPU 场景有实际价值npu_memory_snapshot_timestamp_gap_analysis.md,理解 time_us 缺失的根因与补齐方案time_us,allocator_settings),其次对齐参数接口