PyTorch `torch.compile().aot_compile()` 全解析:提前编译、序列化产物与部署实战
2026/9/11 20:54:27 网站建设 项目流程

PyTorchtorch.compile().aot_compile()全解析:提前编译、序列化产物与部署实战

【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch

aot_compile()是 PyTorch 在torch.compile之上提供的提前编译(AOT, Ahead-of-Time)接口:与默认"首次调用才编译"的惰性流程不同,它会把图追踪、Inductor 代码生成、Triton 内核编译与自动调优全部前移到编译阶段,将结果打包成可落盘的序列化产物,供生产环境冷启动、跨进程/跨机器部署以及基于 fake tensor 的交叉编译使用。本文以官方用户指南 docs/source/user_guide/torch_compiler/torch.compiler_aot_compile.md 为主线,结合仓库源码与测试用例,从 API 用法、序列化机制、后端选择、闭包/外部引用处理到分布式训练完整展开,帮助你掌握一条"编译一次、处处加载"的 PyTorch 部署链路。

⚠️实验性功能:官方文档明确标注该特性处于实验阶段,API 可能随版本调整,请以当前仓库实现为准。

一、什么是aot_compile():与标准torch.compile的差异

标准torch.compile(fn)采用惰性编译:函数在第一次真实调用时才被追踪、编译并缓存。而torch.compile(fn).aot_compile(example_inputs)则在编译期就完成:

  • 图追踪(graph tracing):用示例输入(example inputs)对函数做一次完整的前向追踪;
  • Inductor 代码生成:将追踪得到的 FX Graph 交给后端生成高性能内核;
  • Triton 内核编译与自动调优(autotuning):针对目标硬件编译/调优内核;
  • 产物打包:将编译结果、guard 状态、原始字节码、运行时环境等打包为可序列化的AOTCompiledFunction

从源码看,这一整条链路集中在 torch/_dynamo/aot_compile.py 的aot_compile_fullgraph中实现。其内部通过convert_frame.fullgraph_capture(model, args, kwargs)完成一次性图捕获,随后在torch._guards.tracing(...)strict_autograd_cachebundled_autograd_cache等 functorch 配置开启的上下文中调用 backend 生成可序列化可调用对象,最后组装出CompileArtifacts(见 torch/_dynamo/aot_compile.py#L45-L61)。

AOT 编译适合以下三类场景:

  1. 消除生产环境冷启动编译延迟:把最耗时的编译/调优放在部署之前完成;
  2. 序列化编译产物:跨进程、跨机器部署,运行端不再重复编译;
  3. 交叉编译:在宿主机上用 fake tensor 追踪,为不同目标设备生成产物。

二、aot_compile()与 AOTInductor 的定位区别

维度AOTInductoraot_compile()
输入对象torch.export导出的模型torch.compile包装的函数/模块
产物形态共享库(C++ 部署)序列化 artifact,加载回 Python 可调用对象
运行环境非 Python 环境Python runtime
典型场景C++ 服务端/移动端部署留在 Python 生态内、预计算编译

两者参考实现分别在 torch/_inductor/aot_inductor.py 系列与 torch/_dynamo/aot_compile.py。选择原则很简单:需要 C++ 部署选 AOTInductor;希望留在 Python 中且要预计算编译,选aot_compile()

三、快速上手

3.1 编译一个自由函数(free function)

import torch def fn(x, y): return x + y # Step 1: AOT 编译。必须 fullgraph=True(不支持 graph break)。 # example_inputs 是 (args_tuple, kwargs_dict) 二元组。 compiled_fn = torch.compile(fn, fullgraph=True).aot_compile( ((torch.randn(3, 4), torch.randn(3, 4)), {}) ) # Step 2: 运行编译后的函数。 result = compiled_fn(torch.randn(3, 4), torch.randn(3, 4)) # Step 3: 保存产物到磁盘。 compiled_fn.save_compiled_function("compiled_add.pt") # Step 4: 在另一进程加载运行(无需重新编译)。 with open("compiled_add.pt", "rb") as f: loaded_fn = torch.compiler.load_compiled_function(f) result = loaded_fn(torch.randn(3, 4), torch.randn(3, 4))

几点实现层面的说明:

  • fullgraph=True是硬性要求。在 torch/_dynamo/eval_frame.py#L1020-L1023 中,若self.fullgraph为假,会直接抛出"Graph breaks are not supported with aot compile. Please use torch.compile(fullgraph=True)."
  • 缓存是必需的:torch._inductor.config.force_disable_caches=Trueaot_compile会直接报错(见同文件 L1015-L1018);
  • 保存采用原子写atomic_write_binary先写临时文件并fsync,再os.replace落盘,避免写入中断产生损坏文件(torch/_dynamo/aot_compile.py#L177-L188)。

3.2 编译一个nn.Module

编译模块时,调用编译后模块的.forward.aot_compile(...)。由于forward的第一个参数是self,编译产物在调用时需要把模块实例作为第一个参数传入:

import torch import torch.nn as nn class MyModel(nn.Module): def __init__(self): super().__init__() self.linear = nn.Linear(4, 4) def forward(self, x): return self.linear(x) model = MyModel() # AOT 编译 forward 方法。 compiled_fn = torch.compile( model, fullgraph=True ).forward.aot_compile(((torch.randn(3, 4),), {})) # 以模块实例作为第一个参数调用。 result = compiled_fn(model, torch.randn(3, 4)) # 因为模型参数 requires_grad,反向传播可穿透编译函数。 loss = result.sum() loss.backward() print(model.linear.weight.grad) # 梯度正确流回模型参数 # 保存与加载。 compiled_fn.save_compiled_function("compiled_model.pt") with open("compiled_model.pt", "rb") as f: loaded_fn = torch.compiler.load_compiled_function(f) # 从磁盘加载后反向依然可用。 model.zero_grad() result = loaded_fn(model, torch.randn(3, 4)) result.sum().backward() print(model.linear.weight.grad)

编译模块这一用法在测试 test/dynamo/test_aot_compile.py#L970-L973(test_aot_compile_module)中有对应覆盖。值得一提的还有super()调用场景:test_aot_compile_with_super_call(test/dynamo/test_aot_compile.py#L1176-L1190)验证了__class__作为自由变量被正确序列化、加载后调用结果与原始 eager 一致。

四、API 参考

4.1torch.compile(...).aot_compile(example_inputs)

torch.compile()包装的函数做提前编译。

参数:

  • example_inputstuple[tuple[Any, ...], dict[str, Any]])——(args, kwargs)二元组,作为追踪示例输入。它决定了产物生效的张量形状、dtype 与设备

返回值:AOTCompiledFunction——行为与原函数一致、但执行预编译代码的可调用对象,额外暴露:

  • save_compiled_function(path)——将编译产物序列化到磁盘;
  • disable_guard_check()——关闭运行时 guard 校验(高级用法,见下文"guard 机制")。

前置要求:

  • 必须给torch.compile()fullgraph=True,AOT 编译不支持 graph break;
  • backend 必须可调用(字符串 backend 如"inductor""eager""aot_eager"均受支持)。

4.2torch.compiler.load_compiled_function(file, *, f_globals=None, external_data=None)

从文件加载之前保存的 AOT 编译函数。

参数:

  • file——以二进制读模式打开的文件类对象,内含序列化的编译函数;
  • f_globalsdict | None)——编译函数的可选全局作用域。当原函数引用了用户自定义类型或其他非常规全局对象时必需;
  • external_datadict | None)——加载到运行时环境的可选数据。当原函数捕获了无法序列化的对象(如nn.Module实例)时必需,其键需与save_compiled_function(external_data=...)传入的一致。

返回值:已从磁盘预加载编译结果的 callable。

该入口实现在 torch/compiler/init.py#L988-L1015,其核心是读取字节后调用AOTCompiledFunction.deserialize(data, f_globals, external_data);反序列化过程会以torch._inductor.config.patch(enable_autograd_for_aot=True)包裹编译函数的还原,保证训练语义一致(torch/_dynamo/aot_compile.py#L303-L304)。

4.3 guard 机制与disable_guard_check()

AOTCompiledFunction.__call__每次调用前都会执行 guard 校验:形状/设备/dtype 与编译时示例输入不一致时会抛RuntimeError(torch/_dynamo/aot_compile.py#L237-L244)。disable_guard_check()可关闭该校验,属于高级用法,测试见test_aot_compile_disable_guard_check(test/dynamo/test_aot_compile.py#L803)。另外,编译时还会对 guard 做序列化安全过滤(过滤全局变量与不支持的 guard 类型),并保留 guard 状态与 guard manager,跨进程加载后 guard 校验能力依旧完整。

五、选择后端:SerializableCallable接口

aot_compile()可与任何实现了SerializableCallable接口的后端协同。该抽象定义于 torch/_dynamo/aot_compile_types.py#L71-L85,要求实现serialize_compile_artifactsdeserialize_compile_artifacts__call__三个成员。内置的"inductor""eager""aot_eager"后端开箱即用:

# 默认 inductor:优化代码生成。 compiled_fn = torch.compile(fn, fullgraph=True, backend="inductor").aot_compile( ((torch.randn(3, 4),), {}) ) # eager:无代码生成,便于调试。 compiled_fn = torch.compile(fn, fullgraph=True, backend="eager").aot_compile( ((torch.randn(3, 4),), {}) )

从源码看,当使用 Inductor 或基于 AOTAutograd 的后端时,编译结果会被包装成BundledAOTAutogradSerializableCallable再参与序列化(torch/_dynamo/aot_compile.py#L399-L415);若产物未实现SerializableCallable,则直接报错提示该后端不兼容(L417-L425)。GraphModuleSerializableCallable(torch/_dynamo/aot_compile_types.py#L87-L134)则是另一条纯 FX Graph 的序列化路径,反序列化时在新建的FakeTensorMode中重建图模块并recompile()

序列化细节补充:自定义 Triton 内核无法直接 pickle(其 JITFunction 含不可序列化的_thread.RLock)。aot_compile_types.py通过"Triton Kernel Side Table"机制,在序列化时记录内核的(module_path, function_name),反序列化时按导入路径重新导入并恢复全局kernel_side_table(见该文件头部注释 L43-L68)。

六、闭包与外部引用(closures / external references)

6.1 闭包自由变量自动序列化

捕获自由变量的函数(闭包)受支持,闭包状态会随编译产物一起序列化:

scale = 2 def fn(x, y): return (x + y) * scale compiled_fn = torch.compile(fn, fullgraph=True).aot_compile( ((torch.randn(3, 4), torch.randn(3, 4)), {}) ) compiled_fn.save_compiled_function("scaled_add.pt") with open("scaled_add.pt", "rb") as f: loaded_fn = torch.compiler.load_compiled_function(f)

闭包支持的底层实现:AOTCompilePickler.reducer_override对 cell、code、module、绑定方法及嵌套函数分别注册了还原器(torch/_dynamo/aot_compile.py#L119-L153);运行时会从runtime_env中按co_freevars重建f_localsprepare_f_locals,L197-L210)。对应测试test_aot_compile_with_closure_save_and_load(test/dynamo/test_aot_compile.py#L1157-L1174)验证了闭包产物保存/加载后结果与原始函数一致。

6.2f_globals:为用户自定义类型提供命名空间

当函数引用的用户自定义类型无法被反序列化器找到时,用f_globals提供所需命名空间:

with open("my_fn.pt", "rb") as f: loaded_fn = torch.compiler.load_compiled_function( f, f_globals=my_module.__dict__ )

6.3external_data:非可序列化对象的捕获

当函数捕获了不可序列化对象(如nn.Module实例)时,通过external_data显式注入:

# 保存。 compiled_fn.save_compiled_function( "fn_with_model.pt", external_data={"model": model}, ) # 加载。 with open("fn_with_model.pt", "rb") as f: loaded_fn = torch.compiler.load_compiled_function( f, external_data={"model": model} )

其原理:AOTCompilePickler.persistent_id会把external_data中的对象映射为持久 ID;若序列化过程中仍遇到其他nn.Module(不在external_data中),会收集进errors并在pickler.dump后抛出,提示用户将这些对象标记为 external data(torch/_dynamo/aot_compile.py#L64-L80、L277-L281)。反序列化端AOTCompileUnpickler.persistent_load若找不到对应键,会给出明确的"Missing required external reference to data"错误(L156-L169)。捕获模块的完整用例见test_aot_compile_with_captured_module(test/dynamo/test_aot_compile.py#L1342)。

七、训练支持

aot_compile()开箱即用地支持训练:只要有参数requires_grad,编译会自动追踪joint forward+backward 图,将其分区并分别编译两个半图。得到的函数具备 autograd 感知——对其输出调用.backward()行为与预期一致。

复用上文MyModel

model = MyModel() compiled_fn = torch.compile( model, fullgraph=True ).forward.aot_compile(((torch.randn(3, 4),), {})) # 使用 AOT 编译函数的训练循环。 optimizer = torch.optim.SGD(model.parameters(), lr=0.01) for _ in range(3): optimizer.zero_grad() output = compiled_fn(model, torch.randn(3, 4)) loss = output.sum() loss.backward() optimizer.step()

保存/加载保持 autograd 支持——从磁盘加载后反向依然可用:

compiled_fn.save_compiled_function("train_model.pt") with open("train_model.pt", "rb") as f: loaded_fn = torch.compiler.load_compiled_function(f) output = loaded_fn(model, torch.randn(3, 4)) output.sum().backward() # 梯度正确流动

训练语义的序列化在实现上有专门处理:序列化/反序列化均处于bundled_autograd_cache=True的 functorch 配置下(torch/_dynamo/aot_compile_types.py#L177-L181、torch/_dynamo/aot_compile.py#L303-L304),BundledAOTAutogradSerializableCallable本质上包装了 AOTAutograd 生成的serialize()结果。测试test_aot_module_simplified_serializable_autograd(test/dynamo/test_aot_compile.py#L973)专门验证了序列化后的 autograd 行为。

7.1 分布式训练:DTensor +compile_on_one_rank

对使用DTensortorch.distributed.tensor.DTensor)做张量并行的模型,aot_compile()可与compile_on_one_rank组合,产出与 rank 无关的编译图

  • 不带该标志:mesh 坐标、shard 偏移等 rank 相关值会作为常量烘焙进编译图,导致每个 rank 一张不同的图;
  • 开启后:这些值变为符号化,在运行时计算,所有 rank 共享同一产物。

配置方式一(torch.distributed.config.patch):

import torch.distributed.config as dist_config with dist_config.patch(compile_on_one_rank=True): compiled_fn = torch.compile( model, fullgraph=True ).forward.aot_compile(((example_input,), {}))

配置方式二:环境变量TORCH_DISTRIBUTED_COMPILE_ON_ONE_RANK=1

该开关在 torch/distributed/config.py#L18-L21 中定义(注意其 deprecation 提示:新写法为torch.compiler.config.compile_on_one_rank)。

完整示例:torchrun多卡张量并行训练

# train_tp.py -- 运行方式: torchrun --nproc_per_node=8 train_tp.py import torch import torch.distributed as dist import torch.distributed.config as dist_config import torch.nn as nn import torch.nn.functional as F from torch.distributed.device_mesh import init_device_mesh from torch.distributed.tensor import DTensor, Replicate from torch.distributed.tensor.parallel import ( ColwiseParallel, RowwiseParallel, parallelize_module, ) class FeedForward(nn.Module): def __init__(self, dim, hidden_dim): super().__init__() self.linear1 = nn.Linear(dim, hidden_dim) self.linear2 = nn.Linear(hidden_dim, dim) def forward(self, x): return self.linear2(F.relu(self.linear1(x))) def main(): dist.init_process_group(backend="nccl") rank = dist.get_rank() torch.cuda.set_device(rank) mesh = init_device_mesh("cuda", (dist.get_world_size(),)) model = FeedForward(64, 128).cuda() parallelize_module(model, mesh, { "linear1": ColwiseParallel(), "linear2": RowwiseParallel(), }) x = DTensor.from_local( torch.randn(4, 64, device=f"cuda:{rank}"), mesh, [Replicate()], run_check=False, ) # 以 compile_on_one_rank 编译——所有 rank 得到相同图。 with dist_config.patch(compile_on_one_rank=True): compiled_fn = torch.compile( model, fullgraph=True, ).forward.aot_compile(((x,), {})) # 训练循环。 optimizer = torch.optim.Adam(model.parameters(), lr=1e-3) for step in range(5): optimizer.zero_grad() x = DTensor.from_local( torch.randn(4, 64, device=f"cuda:{rank}"), mesh, [Replicate()], run_check=False, ) out = compiled_fn(model, x) loss = out.to_local().sum() loss.backward() optimizer.step() if rank == 0: print(f"step {step}: loss = {loss.item():.4f}") dist.destroy_process_group() if __name__ == "__main__": main()

保存/加载方式与单进程一致——在任意 rank 调用save_compiled_function,在任意 rank 调用load_compiled_function。由于compile_on_one_rank=True产出 rank 无关图,同一份产物可在每个 rank 直接加载,无需逐 rank 编译。

八、局限性(Limitations)

  1. 必须fullgraph=Truetorch.compile一旦遇到 graph break,aot_compile()直接报错。测试test_aot_compile_graph_break_error_fmt(test/dynamo/test_aot_compile.py#L885)覆盖了错误信息格式;
  2. 输入形状被特化:产物仅对 example inputs 给定的形状、dtype、设备有效;不同形状的输入在运行时触发 guard 失败(除非显式disable_guard_check());
  3. 并非所有后端都支持:自定义后端必须实现SerializableCallable接口才能兼容保存/加载。

九、补充实践要点

  • 默认参数:带默认参数的函数同样支持编译与序列化(见test_aot_compile_with_default_args,test/dynamo/test_aot_compile.py#L1204-L1216);
  • 全局张量引用:函数内引用模块级张量(如EPS)在 eager 与编译产物间行为一致(test_aot_compile_with_global_tensor,test/dynamo/test_aot_compile.py#L1192-L1202);
  • source_info 溯源AOTCompiledFunction.source_info()返回追踪到的源码信息(SourceInfo),便于调试定位编译来源;
  • 交叉编译:借助 fake tensor 的跨设备追踪(test_cross_aot_compiletest_cross_compile_realistic_transformer_model,test/dynamo/test_aot_compile.py#L1494)可实现宿主机为其他目标设备编译。

参考链接

  • 官方用户指南:docs/source/user_guide/torch_compiler/torch.compiler_aot_compile.md
  • 核心实现:torch/_dynamo/aot_compile.py、torch/_dynamo/aot_compile_types.py
  • 入口封装:torch/_dynamo/eval_frame.py(aot_compile方法)、torch/compiler/init.py(load_compiled_function
  • 分布式开关:torch/distributed/config.py
  • 测试覆盖:test/dynamo/test_aot_compile.py、test/distributed/tensor/test_dtensor_compile.py

【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询