首页优化地图方法索引基础概念
返回技术汇总表 ↗

M018 · 独立详解

图编译、形状专门化与自动调优:执行方案是怎样选出来的

把Python调用、计算图、设备内核和缓存分开,理解guards、graph break、动态形状与编译成本。

算子与编译调度与并行详解已编写
视频讲解待制作

这篇文章的小视频尚未制作;可以先阅读下方文字详解。

本页目录

01 · 同样的算式,可以有不同执行方案

模型代码通常写成很多Tensor操作。逐个执行时,框架不断解释调用、分配中间结果、选择内核。图编译尝试先看一段计算的整体依赖,再重写数据流、选择布局与内核,生成可复用的执行方案。

例如Y=ReLU(X+B)可以执行两个独立内核,也可以采用本地融合内核。图编译是找到并实现这种方案的一种途径,融合是它可能采用的优化之一。

02 · 从模型代码到设备执行的路径

环节它做什么产物或边界
捕获可编译区域分析Tensor操作及依赖计算图;不是CUDA任务重放记录。
记录假设检查shape、dtype、布局或相关状态guards:复用方案必须满足的条件。
图变换与降低表示层次融合、布局选择、安排临时存储等更接近目标设备的操作表示。
内核选择与代码生成用库内核、生成内核或候选调优可执行方案。
运行和复用检查条件,执行缓存方案条件不符时可能重编译或回退。

PyTorch的典型Inductor路径由Dynamo获取可编译区域;涉及训练时,AOTAutograd参与前后向图处理,再由后端降低和生成执行代码。这只是典型路径,后端可以替换,实际编译边界由框架与版本决定。

依据:PyTorch · torch.compile入门教程 ↗ · PyTorch 2.14 · 编译与Autograd常见问题 ↗

03 · shape为什么会影响方案

同一个矩阵乘法,M=1的Decode与M很大的Prefill可能需要不同的分块和并行粒度。知道维度、步幅和数据类型,编译器更容易消除分支、设置循环边界和选择候选内核。固定这些条件生成方案,就是形状专门化的一部分。

教学示意 · 不代表设备实测
教学请求的矩阵行数:1, 1, 2, 4, 2, 1

精确形状专门化:可能分别缓存M=1、2、4的方案
动态方案:尝试一个方案服务一段可变M范围
分档方案:例如M=3进入容量4的档位,额外行必须正确屏蔽

shape相同,但dtype、stride或相关状态变化,也可能不满足复用条件。

这不是torch.compile必然产生三份代码的预测:实际guards、动态维度、后端限制和缓存策略决定结果。专门化换取优化机会,但大量形状可能增加编译时间与缓存容量;动态方案减少某些重编译,却不保证每个形状都最优。

依据:PyTorch 2.14 · torch.compile参数、模式与后端 ↗ · PyTorch 2.14 · guards与重编译 ↗

04 · graph break与重编译是两件事

情况发生什么怎么定位
graph break:图中断不能继续捕获当前区域,可能分段编译并执行未编译代码看中断位置与原因,先处理热点区域。
guard不满足已有方案的假设不适用于本次输入或状态看重编译原因与实际输入分布。
内核慢已经成功编译,但方案在设备上效率低看真实kernel、布局和调优结果。

依赖Tensor数值的Python分支、某些标量读取或不支持的调用可能造成图中断;支持范围会随配置与版本变化。默认分段执行可保持程序继续运行,但过多碎片可能损失融合机会。fullgraph=True可帮助暴露不能完整入图的部分,不能强行让不支持的代码变成设备内核。

PyTorch可以用TORCH_LOGS中的graph_breaks和recompiles查看对应原因。不要为了压低日志数量删除必要的控制逻辑或不安全地关闭guards。

依据:PyTorch 2.14 · 常见graph break ↗ · PyTorch 2.14 · guards与重编译 ↗

05 · 自动调优在选什么

对一个给定矩阵形状,可能有几种块大小、线程组织或库实现。自动调优运行或评估候选,再选一个适合当前形状和设备的方案。它通常不是搜索新的模型结构,而是搜索同一计算的实现方式。

自设候选此形状设备时间选择结果
方案A:较小计算块0.8 ms可用
方案B:较大计算块0.6 ms此教学形状中较快
方案C:另一布局0.9 ms此教学形状中较慢

换到更小矩阵后,A可能更好;设备型号、dtype、布局变化也会改变结果。上表是自设数据,只演示选择逻辑。调优还可能增加首次启动时间,候选测量必须正确同步并控制噪声。

PyTorch的max-autotune等模式提供调优入口;某些模式还组合CUDA Graphs。一个开关同时启用多项机制时,比较要核对配置,不能把全部收益都归因于代码生成。

依据:PyTorch 2.14 · torch.compile参数、模式与后端 ↗

06 · 编译什么时候才回本

自设例子:未编译每次1 ms,稳定编译执行每次0.6 ms,一份方案的额外编译成本200 ms。执行K次时,总成本分别是K和200+0.6K ms。K=100时编译总共260 ms,比100 ms慢;K=1000时为800 ms,比1000 ms快。

首次准备成本,要执行多少次才能摊平

自设未编译每次1 ms、编译稳态每次0.6 ms;V份方案都实际编译,累计准备成本V×C。为隔离成本,假设所有形状耗时相同;不预测任何后端性能。
教学示意 · 不代表设备实测
严格回本条件:K × (T_eager − T_compiled) > C_total
本例:K × 0.4 > 200,故整数K至少为501
若稳定执行并不更快,就没有这个模型下的回本点。

07 · 推理和训练,编译范围有何不同

推理通常关注前向与服务中的稳定热点;训练还要关注反向、激活保存、重计算和优化器。把model编译起来,不代表DataLoader、Python训练循环、集合通信和optimizer.step全部入图。

前后向图可以选择不同的融合或保存策略。与激活重计算、混合精度、状态分片组合时,应再次比较梯度和一次参数更新,再测整步时间与内存。不同更新频率、状态修改或学习率值也可能影响缓存方案的复用。

依据:PyTorch 2.14 · 编译与Autograd常见问题 ↗

08 · 从一个稳定函数开始接入

PyTorch接入示意 · dynamic=False用于观察专门化,不是通用最佳配置
def region(x, b):
    return torch.relu(x + b)

optimized = torch.compile(region, dynamic=False)
# 分开记录:首次调用成本、预热后成本、形状变化后的成本
# 先核对输出,再看Profiler、graph_breaks与recompiles。

下方CPU教学脚本模拟精确形状guards、缓存命中、布局变化和选定动态维度范围,另外验证分档中有效行的结果。它不运行Dynamo、Inductor或TorchAir,不预测它们的实际编译次数。

NVIDIA环境常见PyTorch+Inductor,也有其他部署编译路径;昇腾的TorchAir提供torch.compile的NPU后端入口,仓库中还有动态分档文档。二者的后端、算子覆盖与形状约束不同,接口表面相似不代表选项或产物相同。

依据:PyTorch · torch.compile入门教程 ↗ · Ascend · TorchAir官方仓库与NPU后端入口 ↗ · TorchAir · 动态分档策略(master) ↗

09 · 先看输入分布,再选择编译策略

  • 形状重复、热点稳定:更容易摊销编译成本。
  • 形状/分支非常多:记录命中率、缓存量、重编译及尾延迟。
  • 动态批处理、分页KV和MoE:数据结构及路由变化需要后端支持,不能默认整段稳定。
  • 分档引入额外行:屏蔽、位置、输出截取、KV写入和Loss分母都必须正确。
  • 已经计算受限的库内核:图编译未必给主矩阵计算带来额外收益。

10 · 五个问题建立排查顺序

  • 编译图有三个节点,为什么不代表只启动一个kernel?
  • 图中断与guard失败各发生在哪个环节?
  • dynamic=True为什么仍不等于所有形状都不用重编译?
  • 只运行100次时,上述编译例子为什么更慢?
  • 增加了分档padding,怎样确认无效行不会改变Attention、缓存和训练目标?

流程位置与关联方法

这个方法可能出现在多个位置;计算线包含模型计算与算子实现,执行线包含运行时、内存和通信。

资料与核查范围

原理依据以原始论文和官方文档为主;算例与时间线是教学推导。文档核查日期:2026-10-10。引用 main / latest 文档时,实际接口以安装版本为准。

代码为接入片段或教学伪代码;本页没有声称完成 NVIDIA / 昇腾硬件实测。

← 返回总表中的 M018继续阅读:执行图捕获与重放 →
AI Infra 知识库 · 总览 → 独立讲解网站更新 2026-10-08
搜索知识库

搜索方法与基础概念 · Esc 关闭