4. TensorRT自动融合:让模型跑得更快

说到TensorRT,我最想聊的就是它的自动融合能力。说实话,我第一次接触这个功能时,心里还挺怀疑的——编译器自动优化,能比我手写算子强?结果试了一次,真香。

TensorRT的图优化引擎,说白了就是帮你把多个小算子合并成一个大算子。为什么要这么做?你想想看,每个算子执行时都有启动开销、内存读写开销。把这些小算子合并成一个,这些开销就省下来了。

4.1 TensorRT的图优化引擎

TensorRT的优化流程大致分三步:

  1. 图解析:读取你的模型,构建计算图
  2. 图优化:应用各种融合策略,合并算子
  3. 代码生成:生成优化后的推理引擎

其中第二步是关键。我习惯把TensorRT的融合策略分成几类:

融合类型 说明 典型例子
垂直融合 将连续的算子合并 Conv + BN + ReLU → CBR
水平融合 将并行的算子合并 多个Concat输入合并处理
消除冗余 删除无用的操作 去除恒等映射、死代码

核心思想:减少算子数量 = 减少启动开销 + 减少内存访问 + 提高缓存命中率

我在项目中遇到过这样一个案例:一个ResNet-50模型,原始ONNX有176个算子。经过TensorRT自动融合后,只剩下了62个算子。推理延迟从12ms降到了5ms。嗯,效果就是这么明显。

4.2 使用trtexec进行自动融合

trtexec是TensorRT自带的命令行工具。我个人觉得,它是调试和验证融合效果最方便的工具,没有之一。

基本用法很简单:

# 将ONNX模型转为TensorRT引擎
trtexec --onnx=model.onnx --saveEngine=model.engine

# 查看详细的优化信息
trtexec --onnx=model.onnx --saveEngine=model.engine --verbose

# 指定精度和批大小
trtexec --onnx=model.onnx --saveEngine=model.engine \
        --fp16 --batch=1 --workspace=1024

这里有几个参数我特别想强调:

  • --verbose:输出详细的融合日志,能看到每个算子被怎么处理了
  • --fp16:启用半精度推理,融合策略会有所不同
  • --workspace:给TensorRT分配的工作空间,越大融合效果可能越好

小技巧:我习惯先用--verbose跑一遍,看看融合日志。如果发现某些算子没被融合,可以手动调整模型结构来配合TensorRT的优化。

4.3 查看融合后的网络结构

融合完了,怎么知道TensorRT到底做了什么?有三种方式可以查看:

方式一:trtexec日志

加上--verbose参数后,日志里会显示类似这样的信息:

[04/22/2023-10:15:30] [I] [TRT] Engine Layer Information:
Layer(Convolution): conv1 + relu, Tactic: 0x1234
Layer(Convolution): conv2_1 + conv2_2 + add + relu, Tactic: 0x5678
...

看到没?原来分开的Conv和ReLU,现在合并成了一个Layer。这就是融合的效果。

方式二:使用Polygraphy

NVIDIA的Polygraphy工具可以更直观地查看网络结构:

# 查看ONNX和TensorRT引擎的结构对比
polygraphy inspect model model.onnx --mode=basic
polygraphy inspect model model.engine --mode=basic

# 导出为可读的文本格式
polygraphy inspect model model.engine --show layers > engine_layers.txt

方式三:使用NVIDIA Nsight

Nsight可以图形化展示网络结构。我个人觉得,对于复杂模型,图形化比看日志直观多了。

注意:TensorRT的融合策略是黑盒的。你无法直接控制它融合哪些算子。但你可以通过调整模型结构来间接影响融合效果。比如,把BN层和Conv层写在一起,TensorRT就更容易识别并融合它们。

4.4 融合效果的可视化

为了让你更直观地理解融合过程,我画了一张图:

融合前 Conv BN ReLU 融合 融合后 CBR Conv + BN + ReLU 性能对比 3次内核启动 1次内核启动 延迟: 0.15ms 延迟: 0.08ms

这张图展示了一个典型的Conv+BN+ReLU融合过程。左边是三个独立的算子,每个都需要单独启动内核、读写数据。右边融合成一个CBR算子,一次启动就搞定所有计算。

4.5 实战经验总结

最后,分享几个我在项目中积累的经验:

  • 先跑trtexec看日志:这是最快了解融合效果的方式
  • 关注没被融合的算子:如果某个算子没被融合,往往是性能瓶颈
  • 适当调整模型结构:比如把BN层权重融合到Conv里,能帮助TensorRT更好地优化
  • 不同精度融合策略不同:FP16和INT8的融合规则不一样,要分别验证

一句话总结:TensorRT自动融合是降低推理延迟最直接的手段。用好trtexec,看懂融合日志,你就能把模型的性能榨干。

想要更多资料,联系可可