评价此页

TorchInductor GPU Profiling#

创建于:2023年7月28日 | 最后更新于:2025年6月10日

本节列出了有助于深入了解 TorchInductor 中模型性能的有用命令和工作流程。当模型运行速度不如预期时,您可能需要检查模型的各个内核。通常,那些占用大部分 GPU 时间的内核最值得关注。之后,您可能还想直接运行各个内核并检查其性能。PyTorch 提供了工具来覆盖上述所有内容。

相关环境变量#

您可以在分析中使用以下环境变量

  • TORCHINDUCTOR_UNIQUE_KERNEL_NAMES

    • 默认情况下,TorchInductor 将 Triton 内核命名为 `‘triton_’`。启用此环境变量后,Inductor 会在跟踪中生成更具意义的内核名称,例如 `triton_poi_fused_cat_155`,其中包含内核类别(点对点操作为 `poi`)和原始 ATen 运算符。此配置默认禁用,以提高编译缓存命中的可能性。

  • TORCHINDUCTOR_BENCHMARK_KERNEL

    • 启用此项将使 Inductor 代码生成工具能够对单个 Triton 内核进行基准测试。

  • TORCHINDUCTOR_MAX_AUTOTUNE

    • Inductor 自动调优器将对更多 `triton.Configs` 进行基准测试,并选择性能最佳的一个。这将增加编译时间,以期提高性能。

分解模型 GPU 时间#

以下是将其分解为各个内核的执行时间。我们以 `mixnet_l` 为例。

  1. 运行模型的基准测试脚本

       TORCHINDUCTOR_UNIQUE_KERNEL_NAMES=1 TORCHINDUCTOR_BENCHMARK_KERNEL=1
       python -u benchmarks/dynamo/timm_models.py –backend inductor –amp
       –performance –dashboard –only mixnet_l –disable-cudagraphs –training
    

    注意

    该工具依赖于内核名称来决定其类别。启用 `TORCHINDUCTOR_UNIQUE_KERNEL_NAMES` 至关重要。

  2. 在输出日志中,查找以下行

       **Compiled module path:
       /tmp/torchinductor_shunting/qz/cqz7hvhood7y3psp7fy6msjxsxyli7qiwiybizdwtjw6ffyq5wwd.py**
    

我们为每个编译的模块有一行。如果没有额外的图中断,我们将看到日志中有 2 行此类行,一行用于前向图,一行用于后向图。

对于我们的示例命令,我们分别获得前向图和后向图的以下编译模块

  1. 现在我们可以深入了解每个已编译模块的性能。为了说明起见,我们选择前向图的模块。为了方便起见,我将其命名为 `fwd.py`。使用 `-p` 参数直接运行它

       **> python fwd.py -p**
    

在此 示例 gist 中查看完整的输出日志

在输出中,您会注意到以下内容

  • 我们为配置文件编写了一个 chrome 跟踪文件,以便我们可以加载跟踪并与其进行交互。在日志中,查找类似以下内容的行以查找跟踪文件的路径。

    配置文件的 Chrome 跟踪文件已写入 /tmp/compiled_module_profile.json

    将跟踪加载到 Chrome 中(在 chrome 浏览器中访问 chrome://tracing 并按 UI 提示加载文件)将显示如下 UI

    _images/trace.png

    您可以放大和缩小以检查配置文件。

  • 我们通过日志行报告 GPU 时间占总时间的百分比,例如

    GPU 忙碌时间的百分比:102.88%

    有时您可能会看到大于 100% 的值。原因是 PyTorch 在启用配置文件时使用内核执行时间,而在禁用配置文件时使用墙上时间。配置文件可能会稍微扭曲内核的执行时间。但总的来说,这不应该是一个大问题。

    如果我们像 `densenet121` 一样以小批量大小运行模型,我们会看到 GPU 忙碌时间百分比较低

      (Forward graph) Percent of time when GPU is busy: 32.69%
    

    这意味着模型存在大量 CPU 开销。这与启用 cudagraphs 大大提高了 densenet121 的性能这一事实是一致的。

  • 我们可以将 GPU 时间分解为不同类别的内核。在 `mixnet_l` 示例中,我们看到

    • 点对点内核占 28.58%

    • 归约内核占 13.85%

    • 持久归约内核占 3.89%

    • 其余的是用于 mm/conv 的 cutlass/cudnn 内核,占 56.57%

    这些信息可以在每个内核类别的报告的摘要行(最后一行)中找到。

  • 我们还可以深入了解某一类内核。例如,让我们检查归约内核

    _images/kernel_breakdown.png

    我们可以看到每个单独的归约内核的执行时间按顺序排列的表。我们还看到了内核的执行次数。这很有帮助,原因如下

    • 如果一个内核只花费很少的时间,例如 0.1%,改进它最多只会带来 0.1% 的整体增益。投入大量精力在上面是不值得的。

    • 如果一个内核占用了 2% 的时间,将其改进 2 倍可以带来 1% 的整体增益,这使得投入精力是值得的。

基准测试单个 Triton 内核#

假设我们想仔细看看 `triton_red_fused\__native_batch_norm_legit_functional_16`,它是最耗时的归约内核,占前向图总墙上时间的 2.19%。

我们可以查找 `fwd.py` 中的内核名称,并找到类似这样的注释

# kernel path: /tmp/torchinductor_shunting/jk/cjk2vm3446xrk7rth7hr6pun7xxo3dnzubwcn6ydrpifal4eykrz.py

_images/inductor_code.png

为了方便起见,我将其重命名为 k.py。这是此 文件 的粘贴。

`k.py` 是一个独立的 Python 模块,包含内核代码及其基准测试。

直接运行 `k.py` 将报告其执行时间和带宽

_images/terminal_printout.png

我们可以通过运行以下命令来检查最大自动调优是否对该内核有帮助

   **TORCHINDUCTOR_MAX_AUTOTUNE=1 python /tmp/k.py**

我们还可以暂时添加更多归约启发式并再次运行脚本,以检查它们如何帮助该内核。