ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

PyTorch TorchElastic 错误传播机制全解析:record / ProcessFailure / ChildFailedError 实战指南

PyTorch TorchElastic 错误传播机制全解析:record / ProcessFailure / ChildFailedError 实战指南 PyTorch TorchElastic 错误传播机制全解析record / ProcessFailure / ChildFailedError 实战指南【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch本文聚焦 PyTorch 分布式弹性训练TorchElastic中一个常被忽视却至关重要的模块torch.distributed.elastic.multiprocessing.errors文档入口见 errors.md。当训练脚本由 TorchElastic Agent 以多子进程方式拉起时worker 进程中的异常无法被 Agent 用简单的try/except捕获本文从源码出发完整讲解「错误文件 record装饰器 ProcessFailure/ChildFailedError」这套文件级跨进程错误传播机制读完后你将能正确装饰训练入口、读懂传播出的根因错误、并学会通过自定义ErrorHandler扩展错误处理。一、为什么需要错误传播多进程下的异常困境TorchElastic 在一台主机上的典型进程拓扑是每个节点运行一个 TorchElastic Agent训练脚本则以多个 worker 子进程的形式由 Agent 启动api.py 中通过start_processes/MultiprocessContext拉起。这种架构下存在一个天然问题异常产生于 worker 进程而需要感知异常的是 Agent以及更上层的调度器。跨进程的异常不能像同进程那样通过try/except传播因此模块 docstring见 errors/init.py 顶部说明把错误的处理归纳为一次分类——序列化——聚合——上抛的过程。1.1 TorchElastic 的三类错误划分源码在模块 docstring 中用一张清晰的表格划分了三类错误类别子类别说明处理方式User ErrorInput ErrorTorchElastic API 的非法输入如min max节点数在 Agent 进程内直接以标准 Python 异常抛出User ErrorWorker Failureworker 子进程上发生的任何失败走本文所述的文件级跨进程错误传播Platform Error—由 Agent 自身引发的故障由 Agent 进程抛出或使其崩溃Infra Error—超出 Agent 与 worker 管辖范围的故障如宿主机宕机依赖集群/调度层处理除Worker Failure之外其余错误要么在 Agent 进程中「规范地抛出」要么「显式/隐式地让 Agent 崩溃」因此通用的 Python 异常处理策略即可覆盖。唯独Worker Failure特殊失败发生在与 Agent 不同的进程里必须走跨进程通道。二、核心机制文件级file-based跨进程错误传播TorchElastic 选择了一种轻量而稳健的方案——通过文件传递错误。2.1 传播的三步走整体链路可概括为依据 errors/init.py 模块说明写文件worker 侧任何被record装饰的函数或二进制入口捕获到未处理异常后会把异常及其 traceback写入由环境变量TORCHELASTIC_ERROR_FILE指定的文件设文件Agent 侧父进程Agent在启动每个子进程时设置该环境变量见 api.py 中每个 worker 的 env 处理从而为每个子进程指定独立的错误文件聚合传播Agent 侧Agent 收集所有子进程的错误文件选取时间戳最小即最先发生的那个错误作为根因继续向上层传播。这里选「第一个失败」作为根因是刻意的设计多 worker 场景下后续 worker 的失败往往是第一个 worker 失败引发的连锁反应最先出错者才是真正需要上报的 root cause。2.2 Agent 如何为每个 worker 指定错误文件在 api.py 的进程启动段中Agent 按local_rank为每个子进程配置独立的错误文件error_files {} if log_dir: # 简化示意保留源码意图 error_file os.path.join(clogdir, error.json) error_files[local_rank] error_file envs[local_rank][TORCHELASTIC_ERROR_FILE] error_file对应地MultiprocessContext 运行失败时会把每个失败进程包装成ProcessFailure并带上其专属错误文件路径而 SubprocessHandler 路径则由 _capture_process_failures 轮询各进程退出码对exitcode ! 0的进程同样构造ProcessFailure记录。两类入口最终殊途同归错误文件是根因信息的唯一权威来源。三、入口装饰器record一行代码接入错误上报3.1 用法record是面向使用者的核心 API装饰进程的顶层入口函数即可。其 docstring 给出的典型写法是import torch.distributed.elastic.multiprocessing.errors as errors errors.record def main(): # 你的训练主逻辑 ... if __name__ __main__: main()⚠️ 源码明确提示record每个进程只应在顶层方法通常是 main上使用一次不要嵌套装饰内部函数。3.2 装饰器内部到底做了什么从 record 的实现看它等价于下面这段显式代码error_handler get_error_handler() # 默认 ErrorHandler() error_handler.set_entrypoint_fn_name(main.__qualname__) error_handler.initialize() # 注册信号/故障处理 try: main() except ChildFailedError as e: _, failure e.get_first_failure() error_handler.dump_error_file(failure.error_file, failure.exitcode) raise # 原样继续上抛 except Exception as e: error_handler.record_exception(e) # 写入 JSON 错误文件 raise error_handler.record_success() # 正常返回时记录成功几个值得注意的细节SystemExit被特殊处理当入口通过run_path方式执行时exit code 0的SystemExit会被当作正常结束返回None避免假失败捕获到ChildFailedError时说明本进程是承载多个子进程的父/保姆进程会选择其中最先失败的子进程将其错误文件透传到本进程自己的错误文件dump_error_file再继续上抛——这就是「根因一路传导到最顶层」的实现方式之所以依赖错误文件而非单纯靠异常传递是因为该机制同时要支持函数式启动与二进制/脚本式启动两种形态。四、ErrorHandler错误文件的写入者与扩展点4.1 默认行为ErrorHandler 类是默认错误处理器通过 handlers.py 的get_error_handler()获取。核心职责initialize()在运行待观测代码前调用默认执行faulthandler.enable(all_threadsTrue)从而在子进程因段错误等原因崩溃时也能输出线程栈若系统不支持会给出警告而非失败record_exception(e)把异常序列化为结构化 JSON 写入TORCHELASTIC_ERROR_FILE指向的文件若环境变量未设置则退化为仅打日志保证不中断程序。写入格式如下{ message: { message: RuntimeError: 具体异常信息, extraInfo: { py_callstack: 完整的 Python traceback 文本, timestamp: 1699000000 } } }record_success()入口函数正常返回时被record调用基类仅记录 debug 日志供子类覆写以产出结构化成功遥测dump_error_file()把「根因子进程的错误文件」整体搬移到当前进程自己的错误文件若子进程是被SIGSEGV等信号击杀而无法自行写入错误码还会调用override_error_code_in_rootcause_data()用父进程观测到的exitcode回填errorCodemaybe_enrich_signal_failure_message()对信号类失败负退出码子类可覆写以追加设备侧故障上下文例如 GPU 故障信息基类为 no-op。另外由于使用 Pythonmultiprocessing启动时子进程默认继承父进程环境变量存在「子进程在包装函数生效前收到信号、把内容写进父进程错误文件」的风险。dump_error_file的写前清理逻辑会在覆盖前先记录原文件内容再删除重建正是为防御此类边界情况。4.2 自定义扩展ErrorHandler是一个面向扩展设计的公开类源码 docstring 明确建议子类覆写initialize()与record_exception()即可定制错误处理行为set_entrypoint_fn_name()会在initialize()之前由record调用将入口函数的__qualname__注入处理器状态_fn_name这样在覆写上述方法时无需改动方法签名即可拿到入口函数归属信息。典型场景包括追加自定义元数据、接入自有监控或上报系统。五、ProcessFailure统一描述一次进程失败ProcessFailure实现见 errors/init.py是一个 dataclass承载一次失败进程的结构化结果字段为字段含义local_rank该进程在本机 worker 中的本地 rankpid失败进程的进程号exitcode退出码为负时表示被信号终止如-11对应SIGSEGVerror_file指向该进程错误文件的路径构造时__post_init__会尝试读取错误文件并解析 JSON错误文件存在解析出message与timestamp时间戳同时兼容字符串 message 与嵌套 dict 两种格式见_get_error_data错误文件不存在error_file会被置为N/A时间戳取当前时间message置空后按退出码补充推断exitcode 0被信号杀死时生成如Signal 11 (SIGSEGV) received by PID xxx的说明通过signal_name()将负退出码映射为标准信号名映射失败回退为N/A且刻意不因查信号名而杀死进程exitcode 0且无文件数据时标记为system_terminated_error不可重试并提示用户对入口加record以获取 traceback。ProcessFailure还提供timestamp_isoformat()把时间戳格式化为YYYY-MM-DD_HH:MM:SS便于在聚合报告中展示。注意源码假定错误文件由ErrorHandler写入若文件来自其他来源则行为未定义——这也是「worker 入口必须加record」的原因之一。六、ChildFailedError聚合子进程失败并定位根因ChildFailedError实现见 errors/init.py用于「父进程是纯保姆nanny、子进程才承担实际计算」的场景。当父进程检测到某个子进程失败时抛出ChildFailedError(name, failures)其中failures是{global_rank: ProcessFailure}的字典。它有两个关键方法get_first_failure()返回(rank, failure)rank 取所有失败中timestamp最小的那个——即最先观测到的失败作为根因format_msg()按统一的模板生成人类可读的多行报告把根因与其余失败分开排版 trainer FAILED ---------------------------- Failures: [1]: time : 2026-09-08_02:43:30 host : node-01 rank : 2 (local_rank: 2) exitcode : 1 (pid: 12345) error_file: /tmp/trainer_2/error.json traceback : RuntimeError: ... ---------------------------- Root Cause (first observed failure): [0]: ... 格式化时对嵌套的 dict message 会优先提取extraInfo.py_callstack真实 traceback展示并对换行做缩进处理对信号类失败会走maybe_enrich_signal_failure_message钩子仅在退出码为负时触发且异常安全——钩子出错只告警绝不破坏报告渲染。若同时只存在单一失败则其余失败区显示NO_OTHER_FAILURES。七、端到端调用链从 worker 崩溃到调度器看到根因把上述组件串起来一次完整的错误传播依据 errors/init.py 中的进程树示例大致如下0: scheduler-init-process └─ 1: torchelastic_agent ├─ 2: trainer_0 (ok) ├─ 3: trainer_1 (fail) ── 写 error.json └─ ...trainer_1的入口被record装饰异常发生后由ErrorHandler.record_exception把异常与 traceback 写入该进程的error.jsonAgent 通过轮询退出码或捕获ProcessRaisedException/ProcessExitedException见 api.py感知失败为该local_rank构造ProcessFailureAgent 抛出ChildFailedError把各失败子进程聚合并携带其错误文件路径Agent 自身入口若同样被record装饰则在捕获ChildFailedError后调用get_first_failure()取出根因、dump_error_file()将子进程错误文件透传到 Agent 的错误文件再原样上抛调度器 / 启动器如torchrun见 torch/distributed/run.py读取错误文件即可获得带完整 traceback 的真正根因据此执行重试策略并向用户呈现准确的失败状态。需要说明的是上述第 4 步描述的是record的标准语义见其 docstring 中与装饰等价的手写 try/except 代码实际顶层编排是否逐级透传取决于具体启动器实现。八、实战建议与常见陷阱结合源码整理几条可直接落地的实践每个 worker 进程的顶层入口务必加record。不加的后果很直接进程崩溃后没有错误文件上层只能看到system_terminated_error这类几乎无信息的描述无法定位根因日志中甚至会收到「local_rank N FAILED with no error file. Decorate your entrypoint fn with record」的提示。只装饰顶层 main不要层层装饰。record内部会初始化 handler、注册faulthandler重复装饰会带来多余开销并可能掩盖真实的异常来源。理解根因 最早失败分析多 worker 同时失败的报告时先看Root Cause (first observed failure)段落其余 worker 的失败多为次生故障。读取 JSON 而不是只靠日志错误文件中的message.extraInfo.py_callstack才是可机器消费的完整 traceback可用于告警系统自动提取timestamp用于失败排序。信号类失败是「无 traceback」的常态被OOM killer、SIGSEGV等信号终止的进程往往来不及写文件此时exitcode 0的判断与signal_name()是诊断的第一手线索。需要定制上报时继承ErrorHandler覆写initialize()/record_exception()必要时record_success()通过_fn_name拿到入口函数归属通过覆写maybe_enrich_signal_failure_message()为信号失败补充设备侧上下文再用get_error_handler()的返回点替换默认实例即可接入自有体系。延伸阅读模块级设计总览与错误分类torch/distributed/elastic/multiprocessing/errors/__init__.py的模块 docstring装饰器record、ProcessFailure、ChildFailedError完整实现同上文件的对应类/函数定义ErrorHandler默认实现与扩展接口error_handler.py处理器工厂入口handlers.pyAgent 侧如何设置错误文件与构造失败对象api.pyTorchElastic 相关文档目录docs/source/elastic/。【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表