常见问题解答¶
概括地说,TorchDynamo 堆栈由以下位置的图形捕获组成: 使用 dynamo 和后端编译器的 Python 代码。在此示例中, 后端编译器由使用 AOTAutograd 的反向图形跟踪组成 以及使用 TorchInductor 降低图形。当然还有更多 编译器可在此处找到,但在本文中,我们将重点介绍 Inductor 作为一个激励性示例。
Torchdynamo 支持训练,使用 AotAutograd 向后捕获:
该图并由 TorchDynamo 的 Python EvalFrame 前端捕获
.forward()
optimizer.step()
对于该 torchdynamo 捕获的每个 Segment,它使用 AotAutograd 生成一个向后图形 Segment
.forward()
每对 Forward, Backward 图形都(可选)进行 min-cut 分区,以保存 forward/backward 之间的最小状态
forward、backward 对包装在 autograd.function 模块 5 中。用户代码调用仍然会触发 Eager 的 Autograd 引擎,该引擎将每个 “编译后” 的图形作为一个 Op 来运行,同时还运行任何未编译的 Eager Ops 的 .backward() 函数
.backward()
您是否支持分布式代码?¶
DDP 已经过测试并运行,支持其他分布式训练 libraries 正在讨论中。
分布式代码在 dynamo 中具有挑战性的主要原因是 因为 AOTAutograd 会展开向前和向后传递,并且 提供 2 个图表供后端优化。这是个问题 分布式代码,因为我们希望理想情况下重叠通信 操作。Eager pytorch 在 DDP/FSDP 的不同方式 - 使用 autograd 钩子、模块钩子和 模块状态的修改/变更。在 dynamo,应在操作后直接运行的 hooks。 backwards 可以延迟到 backward ops,由于 AOTAutograd 编译函数与 Dispatcher 钩子。
distributed.py 中概述了使用 Dynamo 优化 DDP 的基本策略,其中主要思想是在 DDP 存储桶上进行图形分割 边界。
当 DDP 中的每个节点都需要将其权重与其他节点同步时 节点中,它将其梯度和参数组织到存储桶中,这些存储桶 减少通信时间,并允许节点广播一小部分 它的梯度到其他等待节点。
分布式代码中的图形中断意味着您可以期待 dynamo 及其 backends 来优化分布式程序的计算开销,但 而不是它的通信开销。图形中断可能会干扰 编译加速,如果减小的图形大小剥夺了编译器的 融合机会。然而,收益递减 自大多数当前计算优化以来,图形大小增加 是局部融合。因此,在实践中,这种方法可能就足够了。
我还需要导出整个图形吗?¶
对于绝大多数模型,您可能不需要,您可以按原样使用 optimize,但在某些情况下
完整的图表是必要的,你可以通过简单地确保一个完整的图表
运行 * 大规模训练
运行,想想 $250K+,需要管道并行和其他高级
分片策略 * 依赖 TensorRT 或 AITemplate 等推理优化器
比训练优化器更积极地融合 * 移动训练或
推理。torch._dynamo()
torch.dynamo(..., nopython=True)
未来的工作将包括将通信操作跟踪到图形中, 将这些操作与计算优化协调,并优化 通信操作。
为什么我的代码崩溃了?¶
如果您的代码在没有 dynamo 的情况下运行良好,并且开始使用 dynamo 崩溃 enabled 的,那么最重要的第一步是弄清楚 发生故障的堆栈,因此请尝试运行下面的内容 order 并仅在上一步成功时尝试下一步。
torch.compile(..., backend="eager")
它只运行 torchdynamo forward graph capture,然后使用 PyTorch 运行捕获的图形。如果此操作失败 那么 TorchDynamo 就有问题了。torch.compile(..., backend="aot_eager")
它运行 torchdynamo 来捕获前向图,然后运行 AOTAutograd 在没有任何额外后端编译器的情况下跟踪向后图形 步骤。然后,PyTorch eager 将用于向前和向后运行 图。如果失败,则 AOTAutograd 存在问题。torch.compile(..., backend="inductor")
它运行 torchdynamo 来捕获 forward 图,然后使用 AOTAutograd 来追踪 backward 图 TorchInductor 编译器。如果失败,则 TorchInductor 存在问题
TorchDynamo 错误¶
如果生成的错误发生在后端,则
torchdynamo 是最可能的错误来源。"eager"
要调试这些问题,我们建议将 set 以获取对
TorchDynamo 中的错误和用户代码。除了这个标志之外,
您还可以通过 设置 TorchDynamo 的 。可用级别包括
以下:打印每条指令
除了以下所有日志级别之外还遇到 - :
打印编译的每个函数(原始和修改后的字节码)
以及除以下所有日志级别之外捕获的图形 -(默认):除了所有
低于日志级别 - : 仅打印错误torch._dynamo.config.verbose=True
log_level
torch._dynamo.config.log_level
logging.DEBUG
logging.INFO
logging.WARNING
logging.ERROR
如果模型足够大,则日志可能会变得不堪重负。如果 错误发生在模型的 Python 代码深处,它可能很有用 仅执行发生错误的帧以启用更轻松 调试。有 2 个工具可用于启用此功能:
env TORCHDYNAMO_DEBUG_FUNCTION=<desired_function_name>
将仅在具有该名称的函数上运行 TorchDynamo。env torch._dynamo.config.replay_record_enabled = True
),该函数在遇到错误时转储执行记录。然后,可以重播此记录以仅运行发生错误的帧。
TorchInductor 错误¶
将 TorchInductor 作为选择的后端,AOTAutograd 用于 从 捕获的 Forward Graph 生成 Backward 图 Torch发电机。请务必注意,在此期间可能会发生错误 tracing 以及 TorchInductor 降低 forward 和 backward 图形转换为 GPU 代码或 C++。
一个模型通常由数百或数千个 FX 节点组成,因此 缩小发生此问题的确切节点可能非常 困难,这就是为什么我们强烈建议您使用我们的缩小器 创建您看到的故障的微小可重现示例。我们可以 缩小在 AOTAutograd 层或 Inductor 处发生的错误 层,您应该按以下顺序尝试。
env TORCHDYNAMO_REPRO_AFTER="aot" python your_model.py
env TORCHDYNAMO_REPRO_AFTER="dynamo" python your_model.py
缩小错误是修复错误的最快途径。
缩小器实际上会在该位置为您创建一个
set by so 使您拥有对
那个目录。然后,您可以运行并确认
您收到相同的错误。repro.py
env TORCHDYNAMO_REPRO_DIR
python repro.py
注意
对于其他编译器(例如 nvfuser),过程类似,但
相反,您将利用 .env TORCHDYNAMO_REPRO_AFTER="dynamo" python your_model.py
为什么编译速度慢?¶
Dynamo 编译¶
TorchDynamo 具有用于收集和显示的内置统计函数
每个编译阶段所花费的时间。这些统计数据可以通过以下方式访问
在执行 .默认情况下,这将返回
按名称在每个 TorchDynamo 函数中花费的编译时间。torch._dynamo.utils.compile_times()
torch._dynamo
电感器编译¶
TorchInductor 内置了 stats 和 trace 功能,用于显示时间
在每个编译阶段花费、输出代码、输出图形可视化
和 IR 转储。.这是一个
调试工具,旨在更轻松地调试/理解
internals 的 TorchInductor 的 internal 函数,其输出将如下所示env TORCH_COMPILE_DEBUG=1 python repro.py
该调试跟踪中的每个文件都可以通过以下方式启用/禁用。配置文件和关系图都是
默认情况下禁用,因为它们的生成成本很高。请参阅示例 debug 目录
output 以获取更多示例。torch._inductor.config.trace.*
过度重新编译¶
当 TorchDynamo 编译一个函数(或一个函数的一部分)时,它会确保
关于局部变量和全局变量的假设,以便允许编译器
优化,并将这些假设表示为检查
运行时的特定值。如果这些防护中的任何一个失效,Dynamo 将
重新编译该函数(或部分)最多多次。如果您的程序是
达到缓存限制时,您首先需要确定哪个守卫是
失败以及程序的哪个部分触发了它。torch._dynamo.config.cache_size_limit
recompilation profiler 会自动将 将 TorchDynamo 的缓存限制设置为 1 并运行 程序在仅观察的 “编译器” 下,记录 任何守卫故障。您应该确保至少运行您的程序 与你遇到 trouble,分析器将在此持续时间内累积统计信息。
from torch._dynamo.utils import CompileProfiler
prof = CompileProfiler()
def my_model():
...
profiler_model = torch.compile(my_model, backend=prof)
profiler_model()
print(prof.report())
图形中断和过度重新编译的许多原因包括 已修复即将支持跟踪动态张量的问题 形状 / 更谨慎地选择守卫和更好的启发式方法。
为什么要在生产环境中重新编译?¶
在某些情况下,您可能不希望在程序 热身。例如,如果您在 延迟关键型应用程序。为此,TorchDynamo 提供了一个 使用先前编译的图形,但没有使用新图形的替代模式 生成:
frozen_toy_example = dynamo.run(toy_example)
frozen_toy_example(torch.randn(10), torch.randn(10))
你们如何加快我的代码速度?¶
加速 PyTorch 代码的方法主要有 3 种:
通过垂直融合进行内核融合,融合顺序操作以避免 过多的读/写。例如,fuse 2 subsequent cosines 表示 u 可以执行 1 读 1 写,而不是 2 读 2 写 2。水平熔合: 最简单的示例是 batching,其中单个矩阵相乘 包含一批示例,但更一般的情况是分组的 GEMM 其中,一组矩阵乘法被调度在一起
乱序执行:通过前瞻性对编译器进行一般优化 在图中的确切数据依赖关系中,我们可以决定最多的 执行节点的合适时间以及哪些缓冲区可以重复使用
自动工作放置:类似于 Out of order 执行点, 而是通过将图形的节点与物理硬件或 内存 我们可以设计合适的时间表
以上是加速 PyTorch 代码的一般原则,但 不同的后端将各自对 优化。例如,Inductor 首先负责熔断它 可以并且只有这样才能生成 Triton 内核。它还可以
此外,由于自动内存,Triton 还提供加速 每个 Streaming 中的合并、内存管理和调度 Multiprocessor 的 Alpha Shan,旨在处理平铺计算。
但是,无论您使用哪种后端,最好使用基准测试 并查看方法,因此请尝试 PyTorch 分析器,目视检查 生成的内核,并尝试自己看看发生了什么。
为什么我看不到加速?¶
图形中断¶
使用 dynamo 看不到您想要的加速的主要原因 是过多的图形中断。那么什么是图形中断呢?
给定一个程序,如下所示:
def some_fun(x):
...
torch.compile(some_fun)(x)
...
Torchdynamo 将尝试编译所有 torch/tensor 操作
合并到单个 FX 图表中,但可能无法捕获
所有内容都集成到一个图表中。some_fun()
一些图形中断原因对于 TorchDynamo 来说是无法克服的,例如调用 转换为 torch 以外的 C 扩展对 torchdynamo 不可见,并且 可以做任意的事情,而 TorchDynamo 无法引入 必要的守卫来确保编译后的程序可以安全地重用。
为了最大限度地提高性能,尽可能少的图形中断非常重要 尽可能。
确定图形中断的原因¶
识别程序中的所有图形中断以及
可以使用中断。此工具运行
TorchDynamo 并聚合图形分隔
遇到的。下面是一个示例用法:torch._dynamo.explain
import torch
import torch._dynamo as dynamo
def toy_example(a, b):
x = a / (torch.abs(a) + 1)
print("woo")
if b.sum() < 0:
b = b * -1
return x * b
explanation, out_guards, graphs, ops_per_graph = dynamo.explain(toy_example, torch.randn(10), torch.randn(10))
print(explanation)
"""
Dynamo produced 3 graphs, with 2 graph break and 6 ops.
Break reasons:
1. call_function BuiltinVariable(print) [ConstantVariable(str)] {}
File "t2.py", line 16, in toy_example
print("woo")
2. generic_jump
File "t2.py", line 17, in toy_example
if b.sum() < 0:
"""
要在遇到的第一个图形中断时引发错误,您可以使用
使用 禁用 Python 回退,这应该是
如果您曾使用过基于导出的编译器,则很熟悉。nopython=True
def toy_example(a, b):
...
torch.compile(toy_example, fullgraph=True, backend=<compiler>)
为什么我的代码在更改时没有重新编译?¶
如果您继续并通过 then your code 启用动态形状
不会在形状更改时重新编译。我们添加了对动态形状的支持
这可避免在形状变化小于
系数为 2。这在可变图像等场景中特别有用
大小或 NLP 中的可变序列长度。在推理场景中
通常无法事先知道批量大小是多少
因为您从不同的客户端应用程序获取了可以获得的东西。env TORCHDYNAMO_DYNAMIC_SHAPES=1 python model.py
通常,TorchDynamo 会非常努力地不重新编译 例如,如果 torchdynamo 找到 3 个图形,并且您的 仅更改修改后的 1 个图形,则只有该图形将重新编译。所以 避免可能较慢的编译时间的另一个技巧是预热 model 中编译它,之后后续的编译将是 快得多。冷启动编译时间仍然是我们跟踪的一个指标 明显。
为什么我得到的结果不正确?¶
如果您设置环境变量,它也可以缩小准确性问题,它与类似的 git bisect 一起操作
model 和完全复制可能类似于
我们需要这是下游编译器将 codegen 代码是否是
Triton 代码或 C++ 后端,来自下游的数字
编译器可能以细微的方式有所不同,但对
您的训练稳定性。所以 accuracy 调试器对我们来说非常有用
来检测 Codegen 中的错误或使用后端编译器。TORCHDYNAMO_REPRO_LEVEL=4
TORCHDYNAMO_REPRO_AFTER="aot" TORCHDYNAMO_REPRO_LEVEL=4
为什么我会收到 OOM?¶
Dynamo 仍然是一个 Alpha 产品,因此有一些 OOM 源,如果
您看到一个 OOM 尝试在此
order 然后在 Github 上打开一个 issue,以便我们解决根本问题
1. 如果您使用的是动态形状,请尝试禁用它们,我们已禁用
默认情况下:2.
默认情况下,带有 Triton 的 CUDA 图形在 inductor 中处于启用状态,但删除
它们可能会缓解一些 OOM 问题: .env TORCHDYNAMO_DYNAMIC_SHAPES=0 python model.py
torch._inductor.config.triton.cudagraphs = False