Shortcuts

TorchInductor GPU 性能分析

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

相关环境变量

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

  • TORCHINDUCTOR_UNIQUE_KERNEL_NAMES

    • 默认情况下,TorchInductor 将 Triton 内核命名为 ‘triton\_’。当启用此环境变量时,inductor 会在跟踪中生成一个更有意义的内核名称,例如,triton_poi_fused_cat_155,其中包含内核类别(poi 表示逐点操作)和原始 ATen 操作符。默认情况下,此配置是禁用的,以提高编译缓存命中的机会。

  • TORCHINDUCTOR_BENCHMARK_KERNEL

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

  • TORCHINDUCTOR_MAX_AUTOTUNE

    • 电感自动调谐器将基准测试更多的 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. 在输出日志中,查找以下行:

    **编译后的模块路径:
    /tmp/torchinductor_shunting/qz/cqz7hvhood7y3psp7fy6msjxsxyli7qiwiybizdwtjw6ffyq5wwd.py**
    

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

对于我们的示例命令,我们分别得到了以下用于前向和反向图的编译模块:

  1. 现在我们可以深入了解每个单独编译模块的性能。 让我们选择用于前向图的那个作为示例。 为了方便起见,我将其命名为 fwd.py。直接使用 -p 参数运行它:

    **> python fwd.py -p**
    

请参阅此示例要点中的完整输出日志。

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

  • 我们为该配置文件编写了一个chrome跟踪文件,以便我们可以加载跟踪文件并与之交互。在日志中,查找如下所示的行以找到跟踪文件的路径。

分析的Chrome跟踪记录已写入 /tmp/compiled_module_profile.json

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

_images/trace.png

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

  • 我们通过日志行报告GPU时间相对于挂钟时间的百分比,例如:

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

    有时你可能会看到一个大于100%的值。原因是PyTorch在使用启用分析时使用内核执行时间,而在禁用分析时使用挂钟时间。分析可能会稍微扭曲内核执行时间。但总体来说,这应该不是什么大问题。

    如果我们以小批量运行模型如 densenet121,我们会看到 GPU 忙碌的时间百分比较低:

    (前向图) GPU 忙碌时间百分比: 32.69%
    

    这意味着模型有大量的CPU开销。这与启用cudagraphs可以大幅提升densenet121性能的事实是一致的。

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

    • 逐点核函数占用28.58%

    • reduction kernel 占用了 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 中查找内核名称,并找到类似以下注释:

# 内核路径: /tmp/torchinductor_shunting/jk/cjk2vm3446xrk7rth7hr6pun7xxo3dnzubwcn6ydrpifal4eykrz.py

_images/inductor_code.png

为了方便起见,我将它重命名为 k.py。这里是该文件的粘贴链接: file

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

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

_images/terminal_printout.png

我们可以通过运行以下命令来检查max-autotune是否对内核有帮助:

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

我们也可以暂时添加更多的简化启发式方法,并再次运行脚本来检查这对内核的帮助效果。

优云智算