目录

常见问题解答

概括地说,TorchDynamo 堆栈由以下位置的图形捕获组成: 使用 dynamo 和后端编译器的 Python 代码。在此示例中, 后端编译器由使用 AOTAutograd 的反向图形跟踪组成 以及使用 TorchInductor 降低图形。当然还有更多 编译器可在此处找到,但在本文中,我们将重点介绍 Inductor 作为一个激励性示例。

Torchdynamo 支持训练,使用 AotAutograd 向后捕获:

  1. 该图并由 TorchDynamo 的 Python EvalFrame 前端捕获.forward()optimizer.step()

  2. 对于该 torchdynamo 捕获的每个 Segment,它使用 AotAutograd 生成一个向后图形 Segment.forward()

  3. 每对 Forward, Backward 图形都(可选)进行 min-cut 分区,以保存 forward/backward 之间的最小状态

  4. 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+,需要管道并行和其他高级 分片策略 * 依赖 TensorRTAITemplate 等推理优化器 比训练优化器更积极地融合 * 移动训练或 推理。torch._dynamo()torch.dynamo(..., nopython=True)

未来的工作将包括将通信操作跟踪到图形中, 将这些操作与计算优化协调,并优化 通信操作。

为什么我的代码崩溃了?

如果您的代码在没有 dynamo 的情况下运行良好,并且开始使用 dynamo 崩溃 enabled 的,那么最重要的第一步是弄清楚 发生故障的堆栈,因此请尝试运行下面的内容 order 并仅在上一步成功时尝试下一步。

  1. torch.compile(..., backend="eager")它只运行 torchdynamo forward graph capture,然后使用 PyTorch 运行捕获的图形。如果此操作失败 那么 TorchDynamo 就有问题了。

  2. torch.compile(..., backend="aot_eager")它运行 torchdynamo 来捕获前向图,然后运行 AOTAutograd 在没有任何额外后端编译器的情况下跟踪向后图形 步骤。然后,PyTorch eager 将用于向前和向后运行 图。如果失败,则 AOTAutograd 存在问题。

  3. torch.compile(..., backend="inductor")它运行 torchdynamo 来捕获 forward 图,然后使用 AOTAutograd 来追踪 backward 图 TorchInductor 编译器。如果失败,则 TorchInductor 存在问题

TorchDynamo 错误

如果生成的错误发生在后端,则 torchdynamo 是最可能的错误来源。"eager"

要调试这些问题,我们建议将 set 以获取对 TorchDynamo 中的错误和用户代码。除了这个标志之外, 您还可以通过 设置 TorchDynamo 的 。可用级别包括 以下:打印每条指令 除了以下所有日志级别之外还遇到 - : 打印编译的每个函数(原始和修改后的字节码) 以及除以下所有日志级别之外捕获的图形 -(默认):除了所有 低于日志级别 - : 仅打印错误torch._dynamo.config.verbose=Truelog_leveltorch._dynamo.config.log_levellogging.DEBUGlogging.INFOlogging.WARNINGlogging.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 处发生的错误 层,您应该按以下顺序尝试。

  1. env TORCHDYNAMO_REPRO_AFTER="aot" python your_model.py

  2. env TORCHDYNAMO_REPRO_AFTER="dynamo" python your_model.py

缩小错误是修复错误的最快途径。

缩小器实际上会在该位置为您创建一个 set by so 使您拥有对 那个目录。然后,您可以运行并确认 您收到相同的错误。repro.pyenv TORCHDYNAMO_REPRO_DIRpython 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 种:

  1. 通过垂直融合进行内核融合,融合顺序操作以避免 过多的读/写。例如,fuse 2 subsequent cosines 表示 u 可以执行 1 读 1 写,而不是 2 读 2 写 2。水平熔合: 最简单的示例是 batching,其中单个矩阵相乘 包含一批示例,但更一般的情况是分组的 GEMM 其中,一组矩阵乘法被调度在一起

  2. 乱序执行:通过前瞻性对编译器进行一般优化 在图中的确切数据依赖关系中,我们可以决定最多的 执行节点的合适时间以及哪些缓冲区可以重复使用

  3. 自动工作放置:类似于 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=4TORCHDYNAMO_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.pytorch._inductor.config.triton.cudagraphs = False

文档

访问 PyTorch 的全面开发人员文档

查看文档

教程

获取面向初学者和高级开发人员的深入教程

查看教程

资源

查找开发资源并解答您的问题

查看资源