📅 创建时间:2026-06-03 🏷️ 标签:#torch.compile #Dynamo #Inductor #AOTAutograd #FXGraph #TritonBackend #FullGraph #Dynamism 📚 前置知识:[[/04-ai/01-llm-engineering/07-llm-evolution]](LLM 发展脉络) 📚 相关知识:[[03-graph-representation]](计算图表示) [[11-dynamic-shape]](动态 Shape)
torch.compile:Dynamo、AOTAutograd、Inductor 与 Triton / Torch Compile with Dynamo, AOTAutograd, Inductor, and Triton
┌─────────────────────────────────────────────────────────────────────────────┐
│ 场景:torch.compile 第一次调用慢 5 秒,后续 20ms —— 为什么? │
├─────────────────────────────────────────────────────────────────────────────┤
│ 你在生产环境用 torch.compile 部署模型,第一次调用延迟 5 秒,后续只需 20ms。 │
│ │
│ 你觉得这很诡异: │
│ - 5 秒的延迟是从哪里来的? │
│ - 是 Dynamo graph capture 慢?还是 Inductor codegen 慢? │
│ - 为什么 warmup 后这么快?JIT 编译的产物能被缓存吗? │
│ │
│ 你的模型还有 Python control flow(if/else, for 循环), │
│ torch.compile 对动态控制流支持如何? │
└─────────────────────────────────────────────────────────────────────────────┘第1节 torch.compile 全景
1.1 PyTorch 2.0 编译栈架构
torch.compile 是 PyTorch 2.0 的核心特性,提供了一个可扩展的编译栈:
┌─────────────────────────────────────────────────────────────────────────────┐
│ torch.compile Architecture │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ User Code (Python) │
│ model = MyModel() │
│ compiled = torch.compile(model) │
│ output = compiled(input) │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Layer 1: TorchDynamo │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Graph Capture │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ bytecode │ │ FX │ │ graph │ │ graph │ │ │
│ │ │ analysis │──▶ break │──▶ capture │──▶ break │ │ │
│ │ │ │ │ detection│ │ │ │ detection │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Layer 2: AOTAutograd │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Forward + Backward Graph │ │
│ │ │ │
│ │ Forward Graph Backward Graph │ │
│ │ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Linear │──▶ ... ──▶│ Loss │ │ │
│ │ └─────────┘ └────┬────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌─────────┐ │ │
│ │ │GradSum │ │ │
│ │ └─────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Layer 3: Inductor (Backend) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ CPU Backend: GPU Backend: │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ C++ CodeGen │ │Triton Kernel│ │ │
│ │ │ │ │ Generation │ │ │
│ │ │ GEMM, Conv │ │ │ │ │
│ │ │ Element-wise│ │ Matmul, Softmax │ │
│ │ │ Loops │ │ Layernorm │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ GCC/Clang │ │ Triton JIT │ │ │
│ │ │ compile │ │ compile │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Compiled Artifacts (Cached) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ C++ .so │ │ .ptx │ │ metadata │ │
│ │ (CPU) │ │ (CUDA) │ │ (tracing) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────────┘1.2 延迟分解:5 秒去哪了
python
import torch
import time
model = MyModel()
input_tensor = torch.randn(1, 512, 768)
# 第一次调用:包含编译时间
start = time.time()
compiled_model = torch.compile(model, mode="reduce-overhead")
# ^^^^^^^^^^^^ 编译模式
# warmup 运行(实际触发编译)
output = compiled_model(input_tensor) # 首次调用:JIT 编译
torch.cuda.synchronize() if torch.cuda.is_available() else None
first_call_time = time.time() - start
print(f"First call (including compile): {first_call_time:.2f}s")
# 后续调用:纯执行
times = []
for _ in range(100):
start = time.time()
output = compiled_model(input_tensor)
times.append(time.time() - start)
avg_time = sum(times) / len(times)
print(f"Subsequent call (pure execution): {avg_time*1000:.2f}ms")
# 延迟分解分析
"""
5 秒的组成(典型值):
TorchDynamo Graph Capture: ~0.5s
- Python bytecode 分析: ~0.2s
- FX Graph construction: ~0.2s
- Graph break handling: ~0.1s
AOTAutograd (Backward Capture): ~1.0s
- Reverse-mode AD: ~0.8s
- Graph construction: ~0.2s
Inductor CodeGen: ~3.5s
- Scheduler optimization: ~0.5s
- Triton/C++ kernel 生成: ~2.5s
- Kernel 编译 (Triton JIT): ~0.5s
总计: ~5.0s
"""第2节 TorchDynamo:Graph Capture 层
2.1 两种 Capture 策略
TorchDynamo 支持两种图捕获策略:
python
import torch
from torch._dynamo import optimize
# 策略1:Symbolic(符号化推理)- nopython=True
# 尝试完整捕获,失败时报错
@torch.compile(backend="inductor", fullgraph=True)
def forward_symbolic(x):
return model(x)
# 策略2:Tracing(跟踪)- nopython=False(默认)
# 遇到不支持的操作时自动 graph break
@torch.compile(backend="inductor")
def forward_tracing(x):
return model(x)2.2 Graph Break 详解
Graph Break 是 TorchDynamo 遇到不支持的 Python 特性时自动分割图的机制:
python
# 触发 Graph Break 的情况
# 1. Python built-in 函数(不是 torch 操作)
@torch.compile
def fn1(x):
result = [] # Python list
for i in range(10): # Python range (非 tensor)
result.append(x * i) # list.append (非 torch)
return torch.stack(result) # 这里才回到 torch
# 触发多个 graph break:
# Graph 1: x → x*0 → x*1 → ... → stack
# (每个 iteration 都是独立的 graph)
# 2. 数据依赖的分支
@torch.compile
def fn2(x):
if x.sum() > 0: # 数据依赖的 if
return x * 2
else:
return x / 2
# Graph Break: 运行时才知道走哪个分支
# 3. 非 tensor 操作
@torch.compile
def fn3(x):
print(x.shape) # Python print (side effect)
return x.relu()
# 4. 动态索引(数据依赖)
@torch.compile
def fn4(x, idx):
return x[idx] # idx 的值运行时才知道2.3 Graph Break 检测和优化
python
import torch
from torch._dynamo import list_graph_breakReasons
from torch._dynamo.config import config
# 检测 graph break
model = MyModel()
optimized_model = torch.compile(model, backend="inductor")
# 运行时会记录 graph break
# 通过环境变量查看
# TORCH_LOGS="dynamo" python train.py
# 在代码中检测
@torch.compile
def monitored_fn(x):
# 这会有 graph break
y = x.clone()
y.add_(1) # In-place operation
return y
# 优化建议:减少 graph break
@torch.compile
def optimized_fn(x):
y = x + 1 # Out-of-place (无 graph break)
return y2.4 fullgraph=True 强制完整图
python
# fullgraph=True:强制捕获完整图,遇到不支持的操作报错
@torch.compile(fullgraph=True) # 强制完整图
def simple_forward(x):
return torch.nn.functional.relu(torch.matmul(x, x.T))
# 正常运行
x = torch.randn(100, 100)
print(simple_forward(x).shape) # torch.Size([100, 100])
# 遇到不支持的操作会报错
@torch.compile(fullgraph=True)
def problematic_forward(x):
if x.sum() > 0: # 数据依赖的分支 - 不支持!
return x * 2
return x
try:
problematic_forward(torch.randn(10))
except torch._dynamo.exc.UserError as e:
print(f"Error: {e}")
# UserError: Graph break in user code
# reason: generic_jump2.5 dynamo.config 配置
python
from torch._dynamo import config
# 关键配置项
torch._dynamo.config.configure_mockables = [] # mock 外部库
torch._dynamo.config.replay_record_options = None
torch._dynamo.config.error_on_recompile = False # recompile 时不报错
# 常用运行时配置
@torch.compile(dynamic=True) # 启用动态 shape 支持
def dynamic_fn(x):
return torch.matmul(x, x.T)
# 调试配置
# export TORCH_LOGS="dynamo,graph_break"
# export TORCHDYNAMO_VERBOSE=1第3节 AOTAutograd:反向图捕获
3.1 为什么需要 AOTAutograd
JIT 编译需要完整的 forward + backward 图,因为 GPU 使用反向模式自动微分,backward 必须在 forward 之前就确定:
python
import torch
model = torch.nn.Linear(256, 10)
optimizer = torch.optim.Adam(model.parameters())
# 动态图模式(Eager)
for data, target in dataloader:
optimizer.zero_grad()
output = model(data)
loss = loss_fn(output, target)
loss.backward() # 反向传播在运行时决定
optimizer.step()
# 编译模式(需要 AOT)
compiled_model = torch.compile(model, backend="inductor")
compiled_optimizer = torch.compile(optimizer.step, backend="inductor")
for data, target in dataloader:
optimizer.zero_grad()
output = compiled_model(data) # forward
loss = loss_fn(output, target)
loss.backward() # backward
# 需要捕获 backward 图!
compiled_optimizer() # 参数更新3.2 AOTAutograd 的工作原理
python
# AOTAutograd 的转换过程
"""
原始计算(Eager Mode):
Forward: Backward:
x ──▶ Linear ──▶ Relu ──▶ grad ←─┬─┐
grad ←──┘ │
│
▼
grad ──▶ Relu' ←── Linear' ←── grad
AOTAutograd 捕获后的图:
AOT Forward: AOT Backward:
x ──▶ Linear ──▶ Relu ──▶ grad ──▶ Relu' ──▶ Linear' ──▶ grad
两者被作为一个整体 JIT 编译,
backward kernel 和 forward kernel 一起生成
"""3.3 AOTAutograd 配置
python
# AOTAutograd 相关配置
from torch._inductor import config
# 1. cudagraphs:减少内存分配开销
config.triton.cudagraphs = True # 启用 CUDA Graphs
# 2. 内存规划
config.memory_planning = True # 预分配内存
# 3. 组合使用
model = torch.compile(
model,
backend="inductor",
mode="reduce-overhead",
options={"cudagraphs": True}
)第4节 Inductor:代码生成层
4.1 Inductor 整体架构
┌─────────────────────────────────────────────────────────────────────────────┐
│ Inductor Architecture │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ FX Graph Input │
│ (来自 TorchDynamo + AOTAutograd) │
└──────────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────┐
│ Inductor Scheduler │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 1. Decouple:分解依赖关系 │ │
│ │ 2. Fusion:合并可融合的操作 │ │
│ │ 3. Schedule:确定执行顺序 │ │
│ │ 4. Tile:分块以适应缓存 │ │
│ └────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ CPU Backend │ │ GPU Backend │
│ ┌────────────────────────┐ │ │ ┌────────────────────────┐ │
│ │ C++ CodeGen │ │ │ │ Triton KernelGen │ │
│ │ │ │ │ │ │ │
│ │ - Loop nests │ │ │ │ - TritonGPU dialect │ │
│ │ - SIMD intrinsics │ │ │ │ - Tile-level fusion │ │
│ │ - OpenMP parallelism │ │ │ │ - Shared memory opt │ │
│ │ │ │ │ │ │ │
│ └───────────┬────────────┘ │ │ └───────────┬────────────┘ │
│ │ │ │ │ │
└──────────────┼────────────────┘ └──────────────┼────────────────┘
│ │
▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────┐
│ GCC/Clang 编译 │ │ Triton JIT 编译 │
│ .so 文件 │ │ PTX/CUBIN │
└──────────────────────────────┘ └──────────────────────────────┘4.2 CPU Backend:C++ CodeGen
python
# Inductor 生成的 C++ 代码示例
# 模型: output = (input @ weight).relu()
# 生成代码:
#include <torch/extension.h>
#include <vector>
// GEMM + ReLU kernel
void compiled_kernel(float* output, const float* input,
const float* weight, int M, int N, int K) {
#pragma omp parallel for collapse(2)
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
float sum = 0.0f;
for (int k = 0; k < K; k++) {
sum += input[i * K + k] * weight[j * K + k]; // 注意:weight layout
}
output[i * N + j] = sum > 0 ? sum : 0; // ReLU fused
}
}
}
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
m.def("forward", &compiled_kernel, "Compiled kernel");
}4.3 GPU Backend:Triton KernelGen
python
# Inductor 生成的 Triton 代码示例
# 模型: 矩阵乘法 + ReLU
@triton.jit
def triton_kernel(
# Pointers
input_ptr, weight_ptr, output_ptr,
# Strides
stride_input, stride_weight, stride_output,
# Shapes
M, N, K,
# Block sizes
BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr,
# Other
GROUP_M: tl.constexpr
):
# pid 的计算
pid = tl.program_id(axis=0)
num_pid_m = tl.cdiv(M, BLOCK_M)
num_pid_n = tl.cdiv(N, BLOCK_N)
num_pid_in_group = GROUP_M * num_pid_n
group_id = pid // num_pid_in_group
first_pid_m = group_id * GROUP_M
group_size_m = min(num_pid_m - first_pid_m, GROUP_M)
pid_m = first_pid_m + (pid % group_size_m)
pid_n = (pid % num_pid_in_group) // group_size_m
# 加载数据到 shared memory
offs_m = (pid_m * BLOCK_M + tl.arange(0, BLOCK_M)) % M
offs_n = (pid_n * BLOCK_N + tl.arange(0, BLOCK_N)) % N
offs_k = tl.arange(0, BLOCK_K)
input_ptrs = input_ptr + offs_m[:, None] * stride_input + offs_k[None, :]
weight_ptrs = weight_ptr + offs_k[:, None] * stride_weight + offs_n[None, :]
input = tl.load(input_ptrs)
weight = tl.load(weight_ptrs)
# 计算 matmul
accumulator = tl.dot(input, weight)
# ReLU activation fused
accumulator = tl.where(accumulator > 0, accumulator, 0.0)
# 存储结果
offs_m = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
offs_n = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
output_ptrs = output_ptr + offs_m[:, None] * stride_output + offs_n[None, :]
tl.store(output_ptrs, accumulator)第5节 CompiledAutograd
5.1 什么是 CompiledAutograd
CompiledAutograd 把 autograd 引擎本身也 JIT 编译,进一步减少 Python overhead:
python
import torch
from torch._dynamo import compiled_autograd
# 不使用 CompiledAutograd
# backward 仍然有 Python overhead
# 使用 CompiledAutograd
def train_step(model, data, target):
with compiled_autograd.enable():
output = model(data)
loss = loss_fn(output, target)
loss.backward() # 这里也被 JIT 编译!
optimizer.step()
optimizer.zero_grad()
# 或者用装饰器
@torch.compile(backend="inductor")
def train_step(model, optimizer, data, target):
output = model(data)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
optimizer.zero_grad()第6节 AOTInductor:离线编译
6.1 什么是 AOTInductor
AOTInductor 允许离线编译模型,不依赖 Python 运行时:
python
# AOTInductor 使用流程
# 1. 使用 AOTInductor 导出
import torch
from torch.export import export
from torch._inductor.aot import aot_compile
model = MyModel().eval()
example_inputs = (torch.randn(1, 512, 768),)
# AOT 编译
compiled_model = aot_compile(
model,
example_inputs,
options={
"aot_inductor.output_path": "/tmp/model.so"
}
)
# 2. 部署到目标环境(不需要 PyTorch)
import ctypes
lib = ctypes.CDLL("/tmp/model.so")
# 设置输入输出
input_ptr = lib.get_input_ptr(0)
output_ptr = lib.get_output_ptr(0)
# 调用
lib.run()第7节 性能调优
7.1 编译模式选择
| 模式 | 适用场景 | 编译时间 | 运行性能 |
|---|---|---|---|
| default | 通用 | 中等 | 好 |
| reduce-overhead | 小 batch、频繁调用 | 较长 | 最优 overhead |
| max-autotune | 大 batch、追求极限性能 | 最长 | 最优性能 |
| fwdeduction | forward 为主 | 中等 | forward 快 |
python
import torch
model = MyModel()
# 模式1:默认
compiled_default = torch.compile(model, mode="default")
# 模式2:减少 Python overhead(适合小 batch)
compiled_reduce_overhead = torch.compile(model, mode="reduce-overhead")
# 模式3:自动搜索最优 kernel 配置(适合大 batch)
compiled_max_autotune = torch.compile(model, mode="max-autotune")
# 模式4:只编译 forward(backward 仍然用 eager)
compiled_fwdeduction = torch.compile(model, mode="fwdeduction")7.2 Inductor 配置
python
from torch._inductor import config
# 1. Triton 配置
config.triton.autotune_pointwise = True # 自动调优 pointwise ops
config.triton.cudagraphs = True # CUDA Graphs
config.triton.num_autotune_threads = 8 # 并行搜索线程数
# 2. C++ Backend 配置
config.cpp.dynamic_scale = True # 动态缩放
config.cpp threads = 16 # C++ 线程数
# 3. 内存优化
config.memory_planning = True # 内存规划
config.max_autotune = True # 最大自动调优
# 应用配置
model = torch.compile(model, options={
"triton.cudagraphs": True,
"max_autotune": True,
"inductor.config": config
})7.3 常见问题诊断
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 第一次调用太慢 | JIT 编译 | 使用 torch._dynamo.config 预编译 |
| Graph break 过多 | 动态 control flow | 简化模型或使用 fullgraph=True |
| Recompilation 爆炸 | 动态 shape | 使用 dynamic=True |
| 内存占用过高 | CUDA graphs | 禁用或减少 batch size |
| 数值不正确 | 精度问题 | 检查 dtype,使用 torch.float32 |
升华
┌─────────────────────────────────────────────────────────────────────────────┐
│ torch.compile 核心原则 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 三层分离 │
│ TorchDynamo 捕获图 → AOTAutograd 捕获反向图 → Inductor 生成代码 │
│ │
│ 2. 首次调用慢是正常的 │
│ 5 秒来自 graph capture + codegen,后续调用才是纯执行 │
│ │
│ 3. Graph break 是双刃剑 │
│ 允许动态 control flow,但会降低编译优化效果 │
│ │
│ 4. 编译模式选择决定性能 │
│ reduce-overhead(小 batch)、max-autotune(大 batch) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘"AI 可查 vs 必须理解"清单
必须理解(不理解就等于不会):
- 🔴 torch.compile 三层架构:TorchDynamo(捕获)→ AOTAutograd(反向图)→ Inductor(代码生成)
- 🔴 第一次调用 5 秒的来源:主要是 Inductor 代码生成(Triton/C++ JIT 编译)
- 🔴 Graph Break 的机制:遇到不支持的 Python 特性时自动分割图,牺牲优化换灵活性
- 🔴 AOTAutograd 的必要性:JIT 需要完整的 forward + backward 图,eager 的动态 backward 不够
- 🔴 Inductor 的双 backend:CPU 用 C++/OpenMP,GPU 用 Triton
AI 可查(知道去哪查就行):
- ✅ 具体 graph break 原因代码:查看
torch._dynamo.graph_break源码 - ✅ Triton kernel 的完整语法:Triton 官方文档
- ✅ C++ backend 的 SIMD 选项:Inductor 源码
config.cpp.* - ✅ max-autotune 的搜索空间:Inductor scheduler 源码
- ✅ AOTInductor 完整部署流程:PyTorch AOTInductor 教程
学习状态:🟡 开始学习