[Bug已解决] torch.compile 多次编译调用后简单 ModuleList 模型触发段错误 segmentation fault 解决方案

[Bug已解决] torch.compile 多次编译调用后简单 ModuleList 模型触发段错误 segmentation fault 解决方案
[Bug已解决] torch.compile 多次编译调用后简单 ModuleList 模型触发段错误segmentation fault解决方案一、现象长什么样你有一个很简单的nn.ModuleList模型并且反复对它调用torch.compile比如每次换输入形状都重新编译或在循环里多次 compileimport torch import torch.nn as nn model nn.ModuleList([nn.Linear(4, 4) for _ in range(3)]) for i in range(10): compiled torch.compile(model) x torch.randn(2, 4) out compiled(x) # 第几次之后直接段错误结果不是报 Python 异常而是进程直接段错误segmentation fault / SIGSEGV崩溃没有 Python 栈只有一条Segmentation fault (core dumped)。 本 issuepytorch/pytorch#160399点出对nn.ModuleList模型多次调用torch.compile后会触发 C 层的段错误。简单模型、多次编译、ModuleList 是关键触发条件。 本文聚焦为什么反复 compile ModuleList 会段错误、怎么避免缓存编译结果 / 不重复 compile、怎么定位 C 崩溃。二、背景torch.compile 的缓存与 ModuleList 的特殊性torch.compile第一次编译某模型时Dynamo 会捕获模型 forward 为 FX 图把子模块包括 ModuleList 里的每个标记为「可被 trace 的 callable」生成并缓存编译后的代码默认有torch._dynamo.config.cache_size_limit等限制。nn.ModuleList的特殊性它是一个「模块列表容器」forward 里通常遍历它for layer in self.layers:。Dynamo 在 tracefor循环 ModuleList 时要做「循环展开 / 对每个子模块生成守卫」并维护每个子模块的代码缓存。多次torch.compile同一个 ModuleList 模型尤其每次有不同的输入特征 / 形状会让 Dynamo 反复重建这些缓存缓存结构在回收 / 重建时若出错就踩到已释放内存 → 段错误。 段错误SIGSEGV意味着C 层访问了非法指针——通常是「编译缓存对象被提前释放又被引用」或「guard 代码重复生成导致内存越界」。三、为什么多次 compile 会段错误关键机制torch.compile对同一对象多次调用理想情况下应复用编译缓存。但每次torch.compile(model)都创建新的「编译句柄」如果旧句柄没正确释放引用残留而 Dynamo 内部按id(model)复用缓存新句柄和旧缓存交互时状态混乱ModuleList 的循环展开 guard每次 compile 输入不同形状/特征值不同Dynamo 认为「需要新特化」生成新 guard 分支多次之后 guard 树 / 缓存累计某次释放顺序出错C 对象生命周期编译产物Triton 内核句柄、guard 闭包捕获的 C 对象在 Python GC 与 C 析构交错时出现「use-after-free」→ 段错误。 总之反复 compile 同一 ModuleList 模型触发了 Dynamo 编译缓存 / guard 的 C 内存生命周期 bug导致段错误。四、最小可运行复现带守卫结构示意下面演示「多次 compile 同一 ModuleList」这种易触发结构实际段错误需特定版本/输入用守卫与说明import torch import torch.nn as nn def build_ml(): return nn.ModuleList([nn.Linear(4, 4) for _ in range(3)]) def demo_repeated_compile(): model build_ml() # 反模式在循环里反复 torch.compile 同一模型 for i in range(10): # 多次 compile 易触发段错误 try: compiled torch.compile(model) x torch.randn(2, 4) out compiled(x) except Exception as e: print(f第 {i} 次 compile 抛异常非段错误时:, e) break print(若进程直接段错误退出则命中 #160399) if __name__ __main__: demo_repeated_compile()要点反复torch.compile(model)是反模式正确做法是 compile 一次、复用。下面给正解。五、解决方案一compile 一次反复复用最关键不要在循环里反复torch.compile同一模型。编译一次缓存句柄反复调用import torch import torch.nn as nn model nn.ModuleList([nn.Linear(4, 4) for _ in range(3)]) compiled torch.compile(model) # 只编译一次 for i in range(100): x torch.randn(2, 4) out compiled(x) # 复用同一编译结果安全torch.compile返回的compiled内部有Guard 特化缓存不同输入形状会被它自动特化在cache_size_limit内无需你重新 compile。重新 compile 同一对象反而破坏缓存、触发 bug。六、解决方案二重用编译句柄避免重复编译同一对象如果你有多组不同输入需处理依然只 compile 一次让它自动适应开启 dynamic 或靠 guardimport torch compiled torch.compile(model, dynamicTrue) # dynamic 让形状变化不重新特化 for shape in [(2, 4), (4, 4), (8, 4)]: x torch.randn(*shape) out compiled(x) # 同一个 compiled安全或者若必须重新编译如模型权重被load_state_dict替换先 del 旧句柄确保释放compiled torch.compile(model) out compiled(x) del compiled # 显式释放旧编译句柄 # 之后再重新 compile compiled torch.compile(model)del帮助 Python GC 及时回收降低 C 对象生命周期混乱的概率。七、解决方案三提高缓存上限 / 关掉反复特化若你确实需要多种输入特化调高缓存上限避免 Dynamo 因「超出 limit」丢弃/重建缓存导致状态错乱import torch._dynamo torch._dynamo.config.cache_size_limit 64 # 默认较小调大 torch._dynamo.config.accumulated_cache_size_limit 256 # 或干脆 dynamic减少特化次数 compiled torch.compile(model, dynamicTrue)注意这只是降低触发概率根本修复仍需升级 PyTorch。八、解决方案四升级并用 faulthandler 定位段错误没有 Python 栈用faulthandler拿 C 栈定位import faulthandler faulthandler.enable() # 段错误时打印 C 栈帮助确认在 Dynamo/Inductor import torch # ... 复现 ...升级到修复版本pip install --upgrade torch --index-url https://download.pytorch.org/whl/cu124 python -c import torch; print(torch.__version__)判断修复同样「多次 compile ModuleList」不再 SIGSEGV。修复前坚持「compile 一次复用」即可完全避开。九、排查清单进程直接Segmentation fault且无 Python 栈 → 用faulthandler.enable()拿 C 栈确认在 Dynamo/Inductor。是否反复torch.compile同一模型是 →compile 一次循环里复用立刻消除段错误。多输入形状用dynamicTrue或调高cache_size_limit避免重复特化重建缓存。必须重编译时先del compiled释放旧句柄。升级 PyTorch 到修复版本CI 里禁止在循环内重复 compile 同一对象。通用原则torch.compile是「编译一次、到处调用」不是「每次前向都 compile」。十、小结torch.compile causes segmentation fault with simple ModuleList model after multiple compile calls#160399的本质是对nn.ModuleList模型反复调用torch.compile触发了 Dynamo 编译缓存 / guard 的 C 内存生命周期 buguse-after-free / 越界导致进程段错误。ModuleList 的循环展开 多次特化让缓存重建频繁更易踩中。 应对compile 一次复用绝不在循环里反复torch.compile同一模型缓存句柄反复调用最安全dynamic / 提缓存上限多形状用dynamicTrue或调大cache_size_limit减少特化重建必须重编译先 deldel compiled释放旧句柄再重编faulthandler 定位 升级段错误用faulthandler拿 C 栈升级到修复版本。 记住torch.compile是「编译一次、到处调用」——它不是每次前向都该调用的 API。反复 compile 同一对象不仅慢还会踩中这类 C 内存生命周期段错误。

最新新闻

日新闻

周新闻

月新闻