torch.compile 做了什么?
和 eager mode 有什么区别?

英文问法What does torch.compile do under the hood? · What's the difference between eager mode and compiled mode? · When does it recompile, and what can't it fuse?

考过别家面经原题:百度 Summer Camp 大模型平台策略推理优化实习一面,2026-04-02「eager 模式与 compiler 模式的区别?」(知乎)· 滴滴 AI Infra 算法岗二面,2026-09-30「Torch Compile、Dynamo、Inductor 底层原理是什么?」「CUDA Graph 与 Torch 编译优化的关系?」(牛客)· Unity 自己的材料:FlashVector Case 2 的 baseline 是 "AOT-compiled with PyTorch AOTInductor, FP16 quantized";一个已关闭的旧版 Offline 岗在要求里列了 "Torch Compilation"(hitmarker)· 没有 Unity 自己的面经原题

先弄清楚:每个概念是什么

Eager mode
PyTorch 默认的跑法。Python 执行到一个 op 就 launch 一个 kernel,结果写回 HBM。y = relu(x * w + b) 是 3 个 op,也就是 3 个 kernel。
TorchDynamo
在 Python 执行函数之前把它拦下来,把里面的 tensor op 记成一张 FX graph。
AOTAutograd
提前把 backward 的 graph 也 trace 出来,forward 和 backward 都能交给 compiler 优化。
TorchInductor
默认的 backend compiler,对 graph 里的 op 做 fusion,在 GPU 上生成 Triton kernel(见 Triton 和 CUDA)。
Guard
编译结果能复用的前提条件,每次调用先查。tensor 的 guard 会查 dtype、device 和 shape,shape 变了就失败。

看图:编译的三步,和之后每次调用

第一次调用,先编译 Python 函数 ① TorchDynamo ② AOTAutograd ③ TorchInductor 捕获成 FX graph 同时生成 guard forward 和 backward 的 graph 一起拿到 op 之间做 fusion 生成 Triton kernel ① 遇到不支持的 Python 代码会 graph break,这一段回 Python 跑 之后每次调用,先查 guard 查 guard 比如输入的 shape 通过,复用编译好的 kernel 失败,recompile(三步再走一遍) recompile 到上限(默认 8),回到 eager
图 1:上面一行是第一次调用时的编译,下面是之后的每次调用。guard 通过就复用,失败就 recompile,到上限后回到 eager。

30 秒回答

In eager mode, Python runs one op at a time: each op launches its own kernel and writes its result back to GPU memory. torch.compile does three things. TorchDynamo captures the Python function into a graph. AOTAutograd traces the backward pass ahead of time, so both directions can be optimized. TorchInductor fuses ops and generates Triton kernels on GPU, so there are fewer launches and less memory traffic. Compiled code is protected by guards, for example on input shapes; when a guard fails, it recompiles. The compiler can only fuse ops it can see into.

三步:捕获 graph,拿到 backward,生成 kernel

步骤组件要记住的一点
①TorchDynamo捕获 graph 时同时生成 guard。遇到不支持的代码会 graph break,已捕获的部分先编译,这一段回 Python 跑,后面另起一张 graph
②AOTAutogradforward 和 backward 都能被加速,所以训练也能用
③TorchInductorfusion 的收益见 kernel fusion

和 eager 的区别,以及 recompile

Eagertorch.compile
执行单位一个 op 一个 kernel,Python 逐个调度一张 graph 编译一次,fusion 后 kernel 更少
第一次调用直接跑要先编译,前几次调用更慢
输入的 shape 变了没有影响guard 失败,recompile

已有的编译结果 guard 全都不通过时才 recompile。参数 dynamic 默认是 None,发现 shape 变过,recompile 时会生成一个更 dynamic 的 kernel。次数到上限(默认 8)后这个函数回到 eager,shape 一直在变的输入最后可能还是跑 eager。

Compiler 的边界:FlashVector 的 embedding bag

FlashVector Case 2 的 baseline 用 AOTInductor 编译。torch.compile 是 JIT,AOTInductor 是 TorchInductor 的提前编译版本,把 torch.export 导出的模型编译成 shared library,部署时不需要 Python。

编译过之后 embedding bag 仍然占 28.1% 的 GPU time,因为 PyTorch 把它映射成一个 "opaque ATen dispatcher kernel",compiler 看不到里面。agent 把 masked-mean pooling 拆成基本的 PyTorch op,compiler 才把它们融成一个 Triton kernel。这是 agent 能补上的地方。

必答点

追问

CUDA Graph 和 torch.compile 是什么关系?(滴滴二面问过)

省的东西不同。torch.compile 靠 fusion 减少 kernel 的数量和访存,CUDA Graph 减少每次 launch 的 overhead。reduce-overhead 模式把两者叠在一起用,文档说它适合小 batch。

什么时候 torch.compile 反而更慢?

前几次调用要付编译时间,shape 一直变会反复 recompile,graph break 会把 graph 切碎。

benchmark 时为什么要和 torch.compile 比?

eager 太弱,快过它说明不了什么,所以 CUDA Agent 的 reward 把快过 torch.compile 单列一档(见 kernel RL 的 reward)。

来源PyTorch 文档:torch.compiler(TorchDynamo、AOT Autograd、TorchInductor 的定义)· torch.compile API(mode、dynamic)· torch.compile troubleshooting(graph break、guard、recompile 上限)· AOTInductor · FlashVector §5.2 · ml-八股 08 第 12 题 · 面经出处见 面试题与 JD 1.4、3.4 节和 NVIDIA 题库笔记 1.9 节