TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

图编译器与运行时:算子融合、torch.compile、ONNX Runtime 与 TensorRT

状态:草稿(2026-10-16),部分数据为实测。 torch.compile、内存布局与 cuDNN 设置是在家里的 8GB 笔记本级单 GPU 上通过一次性 Pod 测得(PyTorch 2.9,预热 20 次后 50 次 × 5 组的中位数)。融合原理通过笔记本 CPU 示例进行确认(code/03_fusion.py,数秒)。由于实验镜像中没有 ONNX Runtime 和 TensorRT,故为 未实测(未下载)。 系列文章《视觉模型解读 3》第 3 篇。精度降低相关内容在 第 2 篇中。

目录

1. 直觉:厨师与冰箱

GPU 的计算单元非常快,但数据存放在芯片外部的内存(冰箱)中。eager 模式会分别执行“卷积 → 批归一化 → ReLU → 相加”。卷积结果被存入冰箱,批归一化将其取出修改后再存入,ReLU 接着取出并存入。对于较大的特征图,存取的时间往往长于计算时间。 算子融合则是指“既然取出来了,就把四项工作做完,只存入一次”。

연산 융합과 컴파일러·런타임 융합 전: 연산마다 커널 하나, 메모리를 오간다 합성곱 BN ReLU 더하기 GPU 메모리 — 읽고 쓰기 4번, 커널 부르기 4번 융합 뒤: 한 커널이 값을 칩 안에 둔 채 끝까지 합성곱 + BN(가중치에 접음) + ReLU + 더하기 GPU 메모리 — 읽고 쓰기 1번, 커널 부르기 1번 같은 모델이 GPU 커널이 되기까지 두 길 PyTorch 모델 torch.compile 그래프 잡기 Inductor Triton 커널 생성 ONNX 파일 표준 그래프 ONNX Runtime TensorRT 엔진
图:融合前,每个算子都需要一个内核来回访问内存;融合后,单个内核将数值保留在芯片内部直至结束。下方是通过 torch.compile 或 ONNX 将 PyTorch 模型转化为 GPU 内核的两条路径

融合分为两种:

BN 접기:  y = γ·(W∗x + b − μ)/√(σ²+ε) + β
        = (γ/√(σ²+ε))·W ∗ x + (γ·(b − μ)/√(σ²+ε) + β)
        = W' ∗ x + b'           ← 합성곱 하나로

2. 动手尝试(笔记本 CPU)

code/03_fusion.py 在笔记本的单 CPU 线程上运行(7 次采样的中位数)。

实验 结果
折叠卷积(32→64, 3×3, 输入 144×256)与 BN 最大差异 4.77e-06(FP32 四舍五入水平)。耗时 5.80 → 5.80ms(无差异)
逐元素链式运算 relu(x·1.5 + 0.5)·0.9,元素 1,677 万个 eager 模式 9.7ms → torch.compile 2.4ms(4.1 倍),结果差为 0
对手动原地(in-place)运算的相同链式 7.4ms
将 102 万个元素的加法拆分为 1,000 次调用 0.13 → 0.88ms,每次调用约 0.8µs

3. 图编译器究竟做了什么

TVM(Chen 等人,2018) 接收模型图并将算子分为四类:逐元素操作(injective)、规约操作(reduction)、可在输出端附加逐元素操作的复杂操作(如卷积等 complex-out-fusable),以及无法融合的操作。它根据规则将前三类进行融合。仅靠算子融合就提速了 1.2–2 倍(2015 年桌面级 GPU,包含卷积+BN+ReLU 等 4 个任务,见第 3 节图 4),相较于其他框架,终端性能达到 1.6–3.8 倍(6.1 节)。

PyTorch 2(Ansel 等人,2024) 的 torch.compile 分为两部分:

在数据中心 GPU 上,62 个 TIMM 图像模型的推理速度比 eager 模式在 FP32 下快 2.48 倍,在 FP16 下快 2.77 倍(表 3)。如果同时关闭融合与内联,速度反而会降至 eager 模式的 0.80 倍(表 4,HuggingFace FP16 推理)。 这说明绝大部分速度提升来源于融合。绿色部分的“推理 2.27 倍”未能在正文中确认是哪种组合的平均值,因此此处仅采用表 3 和表 4 的数值。

4. ONNX 与运行时:模型的便携格式

torch.compile 运行于 PyTorch 内部。如果你不希望在服务器上部署 Python 和 PyTorch,或者需要在浏览器和手机上运行,则必须将模型导出为 标准图文件。

5. 实测:在球体模型上应用 torch.compile

我们在实验 GPU 上测试了 TT Score 的球体模型(BlurBall,3 帧窗口,512×288,批次大小 1)。

执行方式 FP32 FP16 首次调用(编译)
eager 模式(当前运行方式) 15.09ms 9.77ms —
eager 模式,关闭 cuDNN 自动调优 15.08ms 9.75ms —
eager 模式,channels_last(NHWC) 18.62ms 9.02ms —
torch.compile 默认模式 12.91ms(1.17 倍) 4.61ms(2.12 倍) 10–11 秒
torch.compile reduce-overhead(CUDA 图) 12.55ms 4.34ms(2.25 倍) 7–8 秒
torch.compile max-autotune(无 CUDA 图) 12.79ms 4.48ms 41–46 秒

(FP16 为 autocast。倍数对比相同精度的 eager 模式。)

通过性能分析器(Profiler)观察相同的执行过程,单次 FP32 eager 模式调用了 478 个 CUDA 内核,在 GPU 总工作时间 14.88ms 中,卷积占了 8.09ms(72 次),批归一化占了 3.27ms(71 次,占 22%)。由于内核耗时总和几乎等于墙钟时间(15.09ms),说明 GPU 一直在满负荷运转。因此问题不在于调用开销,而在于 频繁访问内存的逐元素算子,我们推测编译器通过合并消除了这些开销(未统计编译后版本的内核数量)。

解读如下。

6. 局限性

7. 与我们项目的关联

TT 的总结。 做同样的菜,少开了几次冰箱门,速度就快了一倍。食材(权重)和菜谱(模型)明明都一样!

参考文献

登录后即可点赞

评论

还没有评论。

登录后即可发表评论