搞推理优化的同学应该都绕不过这个组合:TensorRT 7.2.2配上Ubuntu 18.04、CUDA 10.2、cuDNN 8.0.5。这个版本组合至今仍在不少存量生产环境里跑着,尤其是那些显卡驱动锁死在某一代、不方便整体升级的老机器。我也在这套环境上踩过不少坑,从下载源选错到动态库路径对不上,前前后后重装过四五次。这篇就把整个流程完整拆解一遍,从下载到安装,再到实际跑通一个模型,把该避开的雷都提前标出来。
这套环境搭配的适用人群很明确:一是做服务端推理部署的工程师,二是维护旧项目需要复现环境的同学,三是对TensorRT感兴趣但手头正好是这套老配置的研究者。无论你是用Python写推理脚本,还是用C++做高性能服务,这篇文章都能让你少走弯路。
1. 环境与版本匹配关系梳理
1.1 为什么选TensorRT 7.2.2而不是新版
TensorRT 7.2.2在2021年初发布,当时主打的特性是支持了更丰富的ONNX算子覆盖、改进了动态shape处理,以及对Ampere架构的优化。放到今天来看,它的定位很特殊:它是少数能同时兼容CUDA 10.2和较新cuDNN 8.x的版本。CUDA 10.2是Turing架构和部分老驱动下的常见配置,很多工业现场的机器都停留在这个版本上,因为驱动一换,整个CUDA生态都要跟着动,风险太大。
另外,TensorRT 7.2.2对Python 3.6到3.8的支持很稳定,这个Python版本区间也正好是很多老旧服务的依赖范围。所以在这个环境组合下,7.2.2不是"可用"而是"最合适"的选择。新版TensorRT虽然功能更强,但它对CUDA版本的要求往往更高,强行在CUDA 10.2上安装新版反而会因依赖不全而失败。
1.2 版本兼容性一览
在动手之前,先把兼容关系理清楚,这是整个安装过程顺利与否的前提。
| 组件 | 版本要求 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu 18.04 x86_64 | 16.04也可装但18.04更稳妥 |
| CUDA | 10.2 | 必须是10.2,其他小版本如10.2.89均可 |
| cuDNN | 8.0.5 | 8.0.5与CUDA 10.2的搭配已验证 |
| TensorRT | 7.2.2 | 对应安装包名含"7.2.2"字样 |
| GCC | <= 8 | 9以上编译C++样例会报错 |
| Python | 3.6 / 3.7 / 3.8 | 对应不同wheel包名称 |
这里特别强调一下GCC版本。TensorRT 7.2.2的C++头文件和库是用较老的ABI编译的,如果你用Ubuntu 18.04自带的GCC 7.5基本没问题,但如果系统装了GCC 9以上再手动切过去,编译出来的程序链接TensorRT时很容易出现GLIBCXX版本不兼容的报错。这个细节经常被忽略,但实际踩中的人非常多。
还有一个容易混淆的点:cuDNN的版本号要精确匹配。TensorRT 7.2.2官方验证过的cuDNN版本是8.0.5,你换成8.2.x虽然也能跑,但偶尔会出现精度不一致或者内存越界这种"玄学"报错。所以能用官方搭配就千万别自行升级。
1.3 动手前检查这三样
下载安装之前,先把机器上的现状摸清楚。依次执行下面几条命令并记录输出:
# 检查显卡驱动和GPU型号 nvidia-smi # 检查CUDA版本 nvcc -V # 检查cuDNN版本 cat /usr/local/cuda/include/cudnn_version.h | grep CUDNN_MAJOR -A 2第一条命令看驱动版本和显存大小。TensorRT做推理的时候显存占用比训练小很多,但如果你要开FP16或者INT8模式,还是建议显存不低于4GB。第二条命令确认CUDA是10.2,小版本是10.2.89没关系,但如果是10.2.100之类的更新小版本,理论上兼容,实测也没问题。第三条命令确认cuDNN是8.0.5,如果没找到这个文件,说明cuDNN还没装,需要先去NVIDIA官网下载。
Python版本也要确认一下:
python3 -V建议用系统自带的Python 3.6,或者自己装一个3.7/3.8的虚拟环境。TensorRT 7.2.2的Python wheel包是按Python小版本区分的,选错版本会直接报"No matching distribution"。
2. 下载环节的操作与坑点
2.1 下载入口和版本选择
TensorRT的下载入口一般通过NVIDIA官网进入,但官网页面布局经常变,最省事的是直接从NVIDIA的归档服务器下载。
在归档目录下,你需要认准带"7.2.2"和"Ubuntu 18.04"字样的安装包,文件名大致长这样:
TensorRT-7.2.2.3.Ubuntu-18.04.x86_64-gnu.cuda-10.2.cudnn8.0.5.tar.gz注意文件名里的三处关键信息:7.2.2.3(版本号,最后一位是内部构建号)、cuda-10.2(CUDA版本)、cudnn8.0.5(cuDNN版本)。这三个字段必须同时出现在文件名里,缺一个都不对。
下载前先确认你用的是不是Ubuntu 18.04,有个常见的乌龙是:在CentOS机器上尝试装Ubuntu的tar包,解压后动态库一律无法加载,因为发行版的glibc版本和库的依赖关系不同。TensorRT没有提供CentOS的官方包,所以这种环境下一般就走容器方案了。
2.2 tar包、deb包和Python wheel包怎么选
TensorRT 7.2.2的安装包分三种形态,很多人第一次接触时不知道选哪个。
tar包是最通用的方式,解压后手动配置环境变量,适合所有场景,也好卸载(删目录即可)。deb包是Ubuntu系的安装方式,会自动把库文件放到系统目录并注册ldconfig,但卸载时要小心残留。Python wheel包只包含Python API和对应的动态库,适合只用Python开发、不碰C++的朋友。
我个人的建议是:如果你要写C++代码,用tar包;如果纯Python开发,优先在虚拟环境里装wheel包。两种方式可以混用,但混用带来的问题就是动态库路径容易乱,新手不建议一开始就搞混装。
2.3 下载后的完整性校验
下载完成后,别急着解压。先做个哈希校验,防止下载过程文件损坏:
md5sum TensorRT-7.2.2.3.Ubuntu-18.04.x86_64-gnu.cuda-10.2.cudnn8.0.5.tar.gz把输出的MD5值和官网给出的参考值比对。官网一般不会直接显示MD5,但NVIDIA下载页面通常提供SHA256,所以更准确的做法是:
sha256sum TensorRT-7.2.2.3.Ubuntu-18.04.x86_64-gnu.cuda-10.2.cudnn8.0.5.tar.gz校验文件的完整性这一步看似多余,但实际上我遇到过一次下载包损坏导致的Python导入时报"undefined symbol"错误,排查了很久才发现是tar包本身不完整。所以这个步骤别跳过。
3. 安装流程与配置细节
3.1 tar包安装
tar包的安装步骤很简单,但有几个细节必须处理好。
先把包解压到统一目录:
sudo mkdir -p /opt/nvidia sudo tar -xzf TensorRT-7.2.2.3.Ubuntu-18.04.x86_64-gnu.cuda-10.2.cudnn8.0.5.tar.gz -C /opt/nvidia解压后目录结构是:
/opt/nvidia/TensorRT-7.2.2.3/ ├── bin/ ├── include/ ├── lib/ ├── python/ ├── samples/ ├── targets/ ├── doc/ └── uff/然后配置环境变量,把这几行加进~/.bashrc或者你的环境配置文件:
export LD_LIBRARY_PATH=/opt/nvidia/TensorRT-7.2.2.3/lib:$LD_LIBRARY_PATH export PATH=/opt/nvidia/TensorRT-7.2.2.3/bin:$PATH这里有个关键点:LD_LIBRARY_PATH必须设置,否则Python导入tensorrt时提示找不到.so文件。但如果你是通过systemd启动的服务进程,LD_LIBRARY_PATH传不进去,这种情况下建议用ldconfig注册路径:
echo "/opt/nvidia/TensorRT-7.2.2.3/lib" | sudo tee /etc/ld.so.conf.d/tensorrt.conf sudo ldconfig用ldconfig的好处是全局生效,不需要每个shell都设置环境变量。但坏处是,如果你以后装了多个版本的TensorRT来回切换,ldconfig的注册路径会冲突。所以我的习惯是:开发调试用LD_LIBRARY_PATH,生产部署用ldconfig。
3.2 cuDNN 8.0.5的安装确认
tar包形式的TensorRT依赖系统已有的cuDNN,所以在安装TensorRT之前,确认cuDNN 8.0.5已正确安装。如果你之前是通过dpkg方式安装的cuDNN,一般位于/usr/include/x86_64-linux-gnu/和/usr/lib/x86_64-linux-gnu/。如果是手动解压的cuDNN tar包,通常在你指定的CUDA目录下。
排查方式很简单:
find /usr -name "cudnn_version.h" 2>/dev/null find /usr -name "libcudnn.so.8" 2>/dev/null只要有libcudnn.so.8存在,并且能通过ldconfig -p | grep cudnn看到它,说明cuDNN基本就绪。另外一个检查方式是看TensorRT自带的检测脚本:
python3 /opt/nvidia/TensorRT-7.2.2.3/python/check.py不过这个脚本需要装了Python wheel之后才能用,后面再说。
3.3 Python wheel包安装
如果你的工作流以Python为主,下一步就是装Python API的wheel包。tar包里自带的python目录下就有针对不同Python版本的wheel文件:
ls /opt/nvidia/TensorRT-7.2.2.3/python/输出大致是:
tensorrt-7.2.2.3-cp36-none-linux_x86_64.whl tensorrt-7.2.2.3-cp37-none-linux_x86_64.whl tensorrt-7.2.2.3-cp38-none-linux_x86_64.whl根据你当前Python版本选择对应cp36、cp37还是cp38。注意文件名里的none字段,这意味着这个wheel不依赖特定的Python ABI,理论上同一大版本下的Python小版本都能装。
然后创建虚拟环境并安装:
python3 -m venv trt_env source trt_env/bin/activate pip install /opt/nvidia/TensorRT-7.2.2.3/python/tensorrt-7.2.2.3-cp37-none-linux_x86_64.whl注意:强烈建议在虚拟环境里用pip安装,不要用系统的pip直接装。因为系统的pip往往会关联到不同的Python版本,如果系统默认Python是3.6而你装了cp37的wheel,导入时会直接报"module 'tensorrt' has no attribute 'Runtime'"之类的错。
3.4 C++样例验证编译
如果你做C++开发,建议把samples目录里的样例编译一遍,既能验证环境完备性,也能给后续开发打底。samples的编译用的还是老一套的CMake方式:
cd /opt/nvidia/TensorRT-7.2.2.3/samples mkdir build && cd build cmake .. make -j$(nproc)这里可能会遇到一个问题:CMake版本太老导致找不到CUDA。Ubuntu 18.04系统自带的CMake是3.10.2,对CUDA 10.2的探测有些老,最好是装CMake 3.13以上版本。如果编译报错找不到CUDA,可以先:
sudo apt update sudo apt install cmake看看能不能升级到更高版本。如果apt里版本还是旧版,可以手动装一个CMake 3.16的源码包,这个版本对CUDA 10.2的支持很完善。
编译完成后,build/bin目录下会多出可执行的样例程序。找一个跑一下:
./build/bin/sample_mnist -d /opt/nvidia/TensorRT-7.2.2.3/samples/data/mnist如果输出中包含"Accurate"或者"PASSED"字样,说明整条链路已经通了一半。这一步跑通之后,TensorRT的安装环节才算真正结束。
4. 实际使用:把模型跑起来
4.1 用trtexec快速测试
安装完成后,第一个要认识的工具是trtexec。它在tar包的bin目录下,是TensorRT自带的命令行工具,作用是把ONNX模型转成TensorRT引擎,或者直接对已有引擎做性能测试。
先试一个最简单的ONNX模型转换:
trtexec --onnx=model.onnx --saveEngine=model.engine这里注意几个参数的含义:
--onnx=xxx:指定输入的ONNX文件。--saveEngine=xxx:保存生成的engine文件。--fp16:开启FP16精度。--int8:开启INT8,但需要指定校准数据集。--minShape/minShapes/optShape/maxShape:设置动态shape范围。
如果模型输入是固定尺寸,直接跑上面这条命令即可。但大部分实际模型都是动态尺寸,例如输入维度包含可变batch大小,那么转换时就要指定动态shape:
trtexec --onnx=model.onnx \ --minShapes=input:1x3x224x224 \ --optShapes=input:8x3x224x224 \ --maxShapes=input:16x3x224x224 \ --saveEngine=model.engine执行过程会输出每一层的构建日志。如果报错说某个ONNX算子不支持,说明这个版本的TensorRT还没覆盖该算子,需要把对应的层用Plugin实现或者修改模型结构。TensorRT 7.2.2的算子覆盖相比老版本已经好了很多,但遇到LayerNorm、GELU等Transformer结构里的常用算子时,还是有可能要写Plugin。这个问题放在后面的排查章节详细说。
生成engine之后,可以直接用trtexec做性能测试:
trtexec --loadEngine=model.engine --shapes=input:1x3x224x224输出的关键指标是Throughput和GPU Compute Time,通过它们能判断模型在GPU上的实际推理耗时。这个方法在对比不同batch、不同精度下的性能差异时尤其好用。
4.2 Python API的完整推理流程
trtexec适合快速验证和测性能,但工程上要用Python做推理服务时,就得写代码了。下面给出一个完整的Python示例,将ONNX模型转换为engine并执行推理。
import tensorrt as trt import numpy as np import pycuda.autoinit import pycuda.driver as cuda TRT_LOGGER = trt.Logger(trt.Logger.WARNING) def build_engine(onnx_path, engine_path): builder = trt.Builder(TRT_LOGGER) network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) parser = trt.OnnxParser(network, TRT_LOGGER) with open(onnx_path, 'rb') as model: if not parser.parse(model.read()): for i in range(parser.num_errors): print(parser.get_error(i)) return None config = builder.create_builder_config() config.max_workspace_size = 1 << 30 # 1GB # 动态shape设置 profile = builder.create_optimization_profile() profile.set_shape("input", (1, 3, 224, 224), (4, 3, 224, 224), (8, 3, 224, 224)) config.add_optimization_profile(profile) engine = builder.build_engine(network, config) if engine is not None: with open(engine_path, 'wb') as f: f.write(engine.serialize()) return engine def inference(engine_path, input_data, input_name="input", output_name="output"): engine = None with open(engine_path, 'rb') as f: runtime = trt.Runtime(TRT_LOGGER) engine = runtime.deserialize_cuda_engine(f.read()) # 绑定输入输出缓冲区 h_input = input_data.astype(np.float32).ravel() h_output = np.empty(engine.get_binding_shape(output_name).volume(), dtype=np.float32) d_input = cuda.mem_alloc(h_input.nbytes) d_output = cuda.mem_alloc(h_output.nbytes) context = engine.create_execution_context() # 如果是动态shape,需要设置实际的输入尺寸 context.set_binding_shape(engine.get_binding_index(input_name), input_data.shape) cuda.memcpy_htod(d_input, h_input) context.execute_v2([int(d_input), int(d_output)]) cuda.memcpy_dtoh(h_output, d_output) return h_output提示:这个例子依赖pycuda,需要先pip安装:
pip install pycuda。另外还要确认CUDA 10.2对应的pycuda版本,直接用最新版即可。
这段代码里的两个关键点:
第一,EXPLICIT_BATCH标志。TensorRT 7.2.2开始推荐使用显式batch模式,所有动态shape都通过Profile来定义。如果你用老的implicit batch模式,即使network里写了动态维度,转换时也会报错。
第二,max_workspace_size不能设太大也不能太小。太小了会导致某些算子无法融合,性能折损严重;太大了在显存有限的设备上可能报显存不足。我通常设置为显存大小的1/4到1/3。
4.3 动态shape的坑以及FP16/INT8使用建议
动态shape是TensorRT使用中最容易踩坑的部分。要点在于:你在Profile里设定的三个shape必须覆盖所有实际运行时会遇到的输入尺寸,而且最优shape要偏向你的主流实际尺寸。
比如你的服务典型场景是batch=4,那么optShapes就设置成4,这样TensorRT在做算子融合时能针对性优化。如果你的服务偶尔来一个batch=16的请求,而你没有设定maxShapes=16,推理时会直接报"out of memory"或者"shape out of range"。
FP16是TensorRT加速效果最明显的一个模式,很多模型的FP16推理速度是FP32的两倍甚至更多。开启方式很简单,在builder config里加一行:
config.set_flag(trt.BuilderFlag.FP16)但对某些精度敏感的模型,比如目标检测里的小目标回归头,FP16可能带来精度损失。建议开启后验证一遍精度的可接受程度,再决定是否上线。
INT8模式更复杂一些,需要准备校准数据集。TensorRT 7.2.2的INT8校准默认使用继承自TensorRT 7.1的直觉校准算法,你只需要提供一个校准数据生成器。具体代码在官方sample里有,但要注意校准数据的代表性——如果校准数据跟线上数据分布差异太大,INT8之后的精度可能会严重下降。
我实测过,在同样的Batch=8下,FP16比FP32能提升大约1.8倍,INT8比FP32能提升大约3倍。但INT8的调试成本远高于FP16,所以建议新手从FP16开始,稳定跑通之后再考虑INT8。
4.4 使用C++ API的注意事项
Python API做原型验证很方便,但生产环境追求更低延迟时,C++ API往往是首选。C++方面,建议阅读官方samples里的sampleOnnxMNIST和sampleDynamicReshape。它们展示了:
- 如何从ONNX构建engine:流程跟Python类似,但错误处理要繁琐一些。
- 如何做动态shape:核心是设置
IOptimizationProfile并在执行时调用setBindingDimensions。 - 如何管理GPU显存:用
cudaMalloc/cudaMemcpy方式做输入输出拷贝。
C++开发中最常见的坑是:忘记在每次推理前设置输入维度。即使engine本身是动态的,context还是会记住上一次推理的形状,如果不重新设置,输出尺寸就是上一次的,结果会牛头不对马嘴。
另外,C++多线程环境下,每个线程要创建独立的ExecutionContext,不能共用同一个context,否则会出现不确定的数据竞争。
5. 常见问题与排查技巧
5.1 动态库加载失败
现象:Python执行import tensorrt时报错:
ImportError: libnvinfer.so.7: cannot open shared object file: No such file or directory原因基本就是LD_LIBRARY_PATH没设置好。按前面说的方式配置完之后,可以用这个命令快速确认:
ldd /path/to/tensorrt/python/tensorrt/tensorrt.so | grep "not found"如果输出里有libnvinfer.so.7的"not found",说明路径没生效。检查一下/opt/nvidia/TensorRT-7.2.2.3/lib目录下是否有libnvinfer.so.7,如果没有,说明包解压不完整,重新下载解压。
另一种情况是:明明设置了LD_LIBRARY_PATH,但启动Python的进程不是从shell启动的(比如从systemd启动的Jupyter服务),这时候环境变量没传进去。解决方法是把这个目录写进/etc/ld.so.conf.d/tensorrt.conf然后执行ldconfig。
5.2 Python版本不匹配
现象:装完对应wheel后,import tensorrt依然报错,或者虽然导入成功但API缺少某些属性。
排查步骤:
pip show tensorrt python3 -c "import tensorrt; print(tensorrt.__version__)"如果pip show能看到包但导入报错,多半是装了错误的Python大版本wheel。另一个线索是,如果导入时出现AttributeError: module 'tensorrt' has no attribute 'Builder',一般是因为装了低版本的TensorRT,或者是不同Python版本之间路径冲突。
5.3 ONNX算子不支持
现象:trtexec转换ONNX时报类似信息:
[E] [ONNX] ModelImporter.cpp:... Assertion failed: ... Unsupported ONNX operator ...TensorRT 7.2.2支持了大多数常见算子,但部分新模型里的算子还是没有内置支持。处理手段有三种:
- 方法一:修改模型结构,把不支持的部分用等价算子替代。比如把
LayerNormalization拆成多个基础算子。这种方式最简单,但改起来费时。 - 方法二:自定义Plugin。TensorRT支持写C++插件来扩展算子,需要继承
IPluginV2系列接口,封装成动态库,加载到engine里。这个方案性能最好但工作量最大。 - 方法三:用ONNX simplifier工具先对图做简化。安装onnxsim后先优化一遍模型,有时能消除一部分不支持的模式。
比较推荐的排查顺序是:先做onnxsim图优化,再考虑改模型,最后才是写Plugin。
5.4 显存不足与性能调优
显存不足方面,除了降低BatchSize或关掉INT8,还有一个容易忽略的点:记得在builder config里调整max_workspace_size。如果这个值设得过大,构建engine时会直接OOM;不是运行时OOM,而是构建时OOM,因为TensorRT为了找最优实现会把所有候选kernel的workspace都预留出来。
性能调优方面,几个实测下来有效的建议:
- 用
--verbose参数看每一层的耗时:定位瓶颈层是哪个算子,重点优化它。 - 尝试调整
optShapes:让优化shape尽量贴近实际推理shape,性能可能立刻提升10%以上。 - 检查数据预处理开销:很多场景下,预处理(resize、normalize)占据了总耗时的一半以上。考虑把预处理合入TensorRT的输入层,或者用DALI做硬件加速预处理。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 解决方式 |
|---|---|---|
libnvinfer.so.7: cannot open shared object file | LD_LIBRARY_PATH未生效 | 配置环境变量或ldconfig |
No module named 'tensorrt' | wheel包未安装或Python版本不对 | 按Python大版本选择wheel并安装 |
| ONNX解析失败 | ONNX算子不支持或图结构异常 | 先用onnxsim简化图再转换 |
| 动态shape推理时报错 | 输入shape超出Profile范围 | 检查min/max shape设置 |
| 构建engine时显存溢出 | max_workspace_size过大 | 降低数值或使用更低精度 |
| 转换后精度明显下降 | 使用了INT8但没有做校准 | 准备代表性校准数据集重新校准 |
| CUDA初始化失败 | 运行环境没有GPU权限 | 检查nvidia-smi和使用用户权限 |
| 多线程推理结果错乱 | 多个线程共享了同一ExecutionContext | 每个线程独立创建Context |
6. 工程落地的一些经验
6.1 从"能跑"到"能上线"的完整链路
很多人安装TensorRT只是为了在本机验证一个模型,但真正要部署成服务时,还要考虑一些额外的环节。以我自己参与过的某跨平台系统推理服务为例,当时整套流程是这样的:
- 训练好的模型导出为ONNX。
- 用trtexec或Python API将ONNX转成TensorRT engine。
- 将engine文件打包到镜像或服务目录,加载时反序列化。
- 启动推理服务,接收请求并返回推理结果。
- 压测调优,必要时重新构建engine。
有几个经验值得一说:
第一,engine是跟GPU架构绑定的。在Turing架构上构建的engine不能直接在Ampere上跑,反过来也一样。所以打包部署时一定要确认目标机器的GPU架构,或者直接在目标机器上现场构建。这也是为什么很多部署流程会选择在容器启动时做一次engine构建,而不是提前预制engine。
第二,engine文件里的kernel是编译出来的机器码,反序列化时不会做二次优化,因此构建engine的机器应该跟运行机器有相同的算力配置。举个例子,你在开发机上用A100构建的engine,放到T4上加载时会报错或性能崩坏。
第三,版本锁定的重要性。TensorRT 7.2.2的engine序列化格式在7.2.x内部是一致的,但如果升级到7.3,旧engine不一定能加载。我建议把TensorRT版本写进部署配置,避免团队协作时其他人误升版本。
6.2 环境隔离与多版本共存
做深度学习基础设施的人经常需要同时维护多个版本。TensorRT 7.2.2和更新版本可以在同一台机器上共存,只要目录分开,环境变量按需切换。我的习惯是:
- 不同版本的TensorRT放在
/opt/nvidia/tensorrt_7.2.2和/opt/nvidia/tensorrt_8.x这样的目录下。 - 用环境变量或Docker镜像隔离,而不是全局设置ldconfig。
- 每种TensorRT版本配套一个专用的Python虚拟环境。
在Docker场景下,比较推荐的方式是把TensorRT安装命令写进Dockerfile,每次构建镜像时现场安装。这样既能保证版本一致,也避免了宿主机环境的污染。
6.3 性能调优的心得总结
跑通之后,下一步就是让模型跑得更快。TensorRT调优的核心思路是:给TensorRT最大的自由度,让它自己做选择。
具体来说,多做这几件事:
- 把输入数据的NCHW/NHWC选择权留给TensorRT。有人习惯在预处理阶段就把数据转成NHWC,但TensorRT对NCHW和NHWC的支持在不同算子间表现不同,提前转格式反而限制了它的优化空间。
- 允许TensorRT重新排列网络层序。默认配置下TensorRT会做层融合和重排,如果担心精度问题刻意关闭某些优化,性能会大打折扣。建议先全开,再验证精度。
- 多测几次不同的
optShapes。固定shape的模型没得选,但动态shape模型在设定不同优化shape时的性能差异可能达到20%以上。
我还遇到过一种情况:模型输入是一个特别小的图,比如288x288,TensorRT反而因为过度优化导致构图时间很长,而推理时间并没有下降多少。这时候需要在BuilderConfig里降低算法选择的速度,直接用set_tactic_sources限制一部分可选tactic,构建速度会明显改善。
6.4 扩展建议
TensorRT 7.2.2虽然已经有些年头,但它的使用价值依然很高。如果你手头的环境也是这套老配置,有几个方向可以继续深入:
- 将TensorRT引擎集成到推理服务框架,支持RESTful或gRPC接口,实现模型热更新。
- 用TensorRT自带的NMS插件做目标检测后处理,省去在CPU上跑后处理的开销。
- 尝试把多个小模型拼成一个engine做多任务推理,减少dispatch开销。
- 利用TensorRT 7.2.2的sparsity特性,如果你有Ampere架构GPU,可以进一步压缩模型。
这三个方向里,集成推理服务框架是最实际的一个,很多做模型部署的人最终都会走到这一步。核心思路是用一个C++或Python的常驻进程加载engine,接收请求时只做反序列化和推理,不重复构建engine,这样可以把单次推理延迟稳定控制在毫秒级。
我自己在这套环境上跑了快一年,从最初连trtexec怎么用都不知道,到后来能熟练处理动态shape、写Plugin、调INT8,中间踩过的坑确实不少。好在TensorRT的生态足够成熟,遇到的问题大多有解法。希望这篇能把后来者心里"装不上、跑不通、跑不快"这三个坎都填上。