
PyTorch torch.compile 入门实战指南从三角函数融合到预训练模型加速【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch本文是 PyTorch 官方 Torch Compiler 用户指南的入门章节对应仓库文档 torch.compiler_get_started.md的深度展开。文章围绕torch.compile的推理用法展开先用一个三角函数点运算示例建立对内核融合fusion的直觉再通过调试环境变量查看 TorchInductor 生成的实际 Triton 内核最后以 ResNet50、HuggingFace BERT 与 TIMM 预训练模型演示一行代码接入编译加速的完整路径。读完本文你将掌握torch.compile(backendinductor)的基本调用方式、后端选择方法、调试与代码生成检查手段并能在 CPU / NVIDIA GPU / Intel GPU 上复现全部示例。建议先阅读总览章节 torch.compiler_dynamo_overview.md即文档中的torch.compiler_overview引用目标以建立整体概念。第一个 torch.compile 示例从点运算理解内核融合TorchDynamo 与 TorchInductor 的设计目标之一是让torch.compile对用户编写的模型开箱即用。我们先看一个最简单的推理示例——它演示了torch.cos()与torch.sin()这两个典型的逐元素pointwise算子import torch def fn(x): a torch.cos(x) b torch.sin(a) return b new_fn torch.compile(fn, backendinductor) input_tensor torch.randn(10000).to(devicecuda:0) a new_fn(input_tensor)::: note 运行该脚本需要机器上至少有一块 GPU。如果没有 GPU可以删掉代码中的.to(devicecuda:0)脚本就会在 CPU 上运行也可以把设备改为xpu:0从而在 Intel® GPU 上运行。 :::这个例子可能不会带来显著的性能提升但它能帮你建立对torch.compile工作方式最直观的认识你只需要把想要优化的函数或模型用torch.compile包一层然后像调用普通函数一样调用编译后的对象即可。从仓库源码看torch.compile在 torch/compiler/init.py 中被统一暴露底层由 TorchDynamo 负责图捕获再交由指定的后端默认与最常用的是inductor完成代码生成与优化。torch.relu()是另一个更广为人知的点运算算子。在 eager 模式下点运算的效率是不理想的每一个点运算算子都需要从内存读取一个张量、做一点修改、再把结果写回内存。当多个点运算串联时这种读-写-读-写的往返会成倍放大内存流量。TorchInductor 执行的最重要的优化就是融合fusion。以上面的例子为例eager 模式需要 2 次读取x、a和 2 次写入a、b而融合后只需要 1 次读取x和 1 次写入b。这对于新一代 GPU 尤其关键因为此时性能瓶颈已经从计算能力GPU 每秒能完成的浮点运算量转移到了内存带宽数据送达 GPU 的速度。TorchInductor 的另一大优化CUDA Graphs除内核融合外TorchInductor 提供的另一项重要优化是对 CUDA graphs 的自动支持。CUDA graphs 通过一次性捕获一段 GPU 操作序列并整体重放消除了从 Python 程序逐个启动 kernel 所带来的 CPU 开销即 launch overhead。对于现代 GPU 上那些执行时间极短、kernel 数量众多的计算图来说这种启动开销往往占据主导地位因此 CUDA graphs 的意义十分突出。仓库中的 torch/_dynamo/backends/cudagraphs.py 完整实现了 CUDA graphs 后端支持模块文档明确说明其职责包括为前向与反向传播创建和管理 CUDA graph、检测并处理输入突变、进行设备兼容性检查、与 TorchInductor 的 cudagraph trees 集成等。它支持两种主要模式cudagraphs完整的 CUDA graph 支持同时优化前向与反向传播cudagraphs_inner用于基准测试的低层 CUDA graph 实现。这也是官方文档在介绍完inductor后建议读者用cudagraphs后端做下一个尝试的原因。用 TORCH_COMPILE_DEBUG 查看生成的内核代码TorchDynamo 支持多种后端而 TorchInductor 的工作方式是生成 Triton 内核。把上面的示例保存为example.py然后运行TORCH_COMPILE_DEBUG1 python example.py脚本执行过程中终端会打印出DEBUG日志。在日志接近末尾的位置你会看到一个指向某个文件夹的路径其中包含torchinductor_你的用户名目录。在这个目录中可以找到output_code.py文件里面就是生成的 kernel 代码类似下面这样pointwise(size_hints[16384], filename__file__, triton_meta{signature: {in_ptr0: *fp32, out_ptr0: *fp32, xnumel: i32}, device: 0, constants: {}, mutated_arg_names: [], configs: [AttrsDescriptor(divisible_by_16(0, 1, 2), equal_to_1())]}) triton.jit def triton_(in_ptr0, out_ptr0, xnumel, XBLOCK : tl.constexpr): xnumel 10000 xoffset tl.program_id(0) * XBLOCK xindex xoffset tl.arange(0, XBLOCK)[:] xmask xindex xnumel x0 xindex tmp0 tl.load(in_ptr0 (x0), xmask, other0.0) tmp1 tl.cos(tmp0) tmp2 tl.sin(tmp1) tl.store(out_ptr0 (x0 tl.zeros([XBLOCK], tl.int32)), tmp2, xmask)::: note 以上代码片段只是一个示例。根据硬件不同你实际看到的生成代码可能有所差异。 :::你可以借此验证cos和sin的融合确实发生了cos与sin位于同一个 Triton kernel内部中间的临时变量保存在访问速度极快的寄存器中而不是被写回显存。整段内核代码是 Python 写的即便你从未编写过太多 CUDA kernel也比较容易读懂。从仓库源码看TORCH_COMPILE_DEBUG是 TorchInductor 调试机制的总开关。在 torch/_inductor/config.py 中可以看到# master switch for all debugging flags below enabled os.environ.get(TORCH_COMPILE_DEBUG, 0) 1除总开关外还有一系列配套环境变量例如TORCH_COMPILE_DEBUG_SAVE_REAL保存真实张量便于排查数值问题TORCH_COMPILE_DEBUG_EXTEND把 Inductor kernel 的栈追踪信息回填到 PyTorch profiler 时间线中TORCH_COMPILE_DEBUG_MAX_EVENTSprofiler 时间线后处理的最大 trace 事件数默认 500000超出会跳过溯源处理以避免内存溢出INDUCTOR_PROVENANCE控制溯源跟踪级别1 为正常2 为 basic。调试目录的实际创建逻辑位于 torch/_inductor/debug.py 的DebugContext.create_debug_dir()它会基于config.trace.debug_dir或系统临时目录生成torchinductor子目录。想深入学习 Triton 的性能特性可以阅读 Triton 官方文档triton-lang.org。选择后端torch.compiler.list_backends()inductor并不是唯一可用的后端。你可以在 Python REPL 中运行torch.compiler.list_backends()查看当前环境中所有可用的后端名称然后尝试其中的cudagraphs。从源码看后端发现与注册机制位于 torch/_dynamo/backends/registry.pyregister_backend(compiler_fn, nameNone, tags())把编译函数注册到后端表中register_debug_backend与register_experimental_backend分别是带debug、experimental标签的便捷注册方式lookup_backend(compiler_fn)把后端字符串展开为对应的编译函数如果名称不存在会借助difflib.get_close_matches给出相近名称的纠错建议并抛出InvalidBackendlist_backends(exclude_tags(debug, experimental))返回所有可传入torch.compile(..., backendname)的合法字符串默认排除debug与experimental标签的后端_lazy_import()会加载torch._dynamo.backends下所有子模块并通过entry_points分组名torch_dynamo_backends发现第三方注册的后端。对应地公开 API torch.compiler.list_backends 在torch/compiler/__init__.py中做了转发封装。仓库自带的动态后端模块位于 torch/_dynamo/backends 目录包括inductor.py、cudagraphs.py、onnxrt.py、tensorrt.py、tvm.py、torchxla.py、distributed.py以及debugging.py等。第三方库也可以通过声明torch_dynamo_backendsentry point 贡献自己的后端——这就是list_backends()的输出会随环境而变化的原因。上手真实模型ResNet50接下来用一个真实模型——来自 PyTorch Hub 的 ResNet50——来体验torch.compileimport torch model torch.hub.load(pytorch/vision:v0.10.0, resnet50, pretrainedTrue) opt_model torch.compile(model, backendinductor) opt_model(torch.randn(1, 3, 64, 64))torch.hub.load会从pytorch/vision仓库拉取模型定义与预训练权重torch.compile在首次调用时完成图捕获与代码生成后续调用即可复用编译产物。使用预训练模型HuggingFace Transformers 与 TIMMPyTorch 用户经常使用来自 transformers 或 TIMM 的预训练模型而 TorchDynamo 与 TorchInductor 的设计目标之一就是与人们想要编写的任何模型开箱即用地配合工作。优化 HuggingFace BERT下面直接从 HuggingFace Hub 下载一个预训练 BERT 模型并优化它import torch from transformers import BertTokenizer, BertModel tokenizer BertTokenizer.from_pretrained(bert-base-uncased) model BertModel.from_pretrained(bert-base-uncased).to(devicecuda:0) model torch.compile(model, backendinductor) # 这是唯一改动的一行代码 text Replace me by any text youd like. encoded_input tokenizer(text, return_tensorspt).to(devicecuda:0) output model(**encoded_input)这里值得强调对于既有模型接入编译优化通常只需要修改一行代码——把model用torch.compile(model, backendinductor)包起来其余推理代码完全不用动。这正是 TorchDynamo 图捕获能力的体现它能在 Python 层透明地捕获模型前向计算再交给 Inductor 做后端代码生成。如果你把model和encoded_input上的to(devicecuda:0)都删掉那么 Triton 将改而生成经过优化、面向 CPU 运行的C 内核。你可以同样去查看 BERT 对应的 Triton 或 C 内核——它们比前面的三角函数示例复杂得多但同样可以快速浏览体会 PyTorch 的计算图是如何被翻译为底层 kernel 的。优化 TIMM 模型再来看一个 TIMM 的例子import timm import torch model timm.create_model(resnext101_32x8d, pretrainedTrue, num_classes2) opt_model torch.compile(model, backendinductor) opt_model(torch.randn(64, 3, 7, 7))注意这里输入形状是(64, 3, 7, 7)与通常的 ImageNet 分辨率不同——torch.compile并不要求输入必须与预训练时的形状完全一致它会基于实际运行时的张量形状与元数据进行编译与优化动态形状与重编译的细节可进一步阅读仓库中的 torch.compiler_dynamic_shapes.md。下一步学习路径本节通过几个推理示例帮助你建立了对torch.compile工作方式的基本理解。接下来可以按以下路径继续深入训练场景阅读 PyTorch 官方《torch.compile tutorial on training》教程了解编译对训练循环的加速API 参考查看 torch.compiler_api.md即文档中的torch.compiler_api引用目标掌握torch.compile的完整参数如mode、fullgraph、dynamic、options等细粒度追踪阅读 torch.compiler_fine_grain_apis.md即文档中的torchdynamo_fine_grain_tracing引用目标学习如何对模型中的特定子图进行精确控制进一步深入仓库的 docs/source/user_guide/torch_compiler 目录下还有大量专题文档包括 torch.compiler_dynamo_deepdive.md、torch.compiler_custom_backends.md、torch.compiler_profiling_torch_compile.md 与 torch.compiler_troubleshooting.md分别覆盖原理深潜、自定义后端、性能剖析与问题排查。简而言之torch.compile的接入成本极低一行代码却能借助 TorchDynamo 的图捕获与 TorchInductor 的融合、CUDA graphs 等优化为从点运算到大型预训练 Transformer 的各类推理负载带来可观的执行效率提升。理解其内核生成与调试手段是进一步在生产环境落地编译优化、并排查性能瓶颈的起点。【免费下载链接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration项目地址: https://gitcode.com/GitHub_Trending/py/pytorch创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考