TensorRT做推理加速这几年基本是NVIDIA GPU上绕不开的一环。不管你是做自动驾驶、安防监控、互联网推荐还是搞大模型的线上部署只要跟深度学习推理沾边迟早会碰到它。我最早接触TensorRT是某次项目里要把一个检测模型压到20ms以内折腾了整整一周才把流程走通。现在回头看看其实大部分时间都耗在了环境匹配和调试上真正理解原理之后整个链路就清晰了。这篇东西我不会写成文档翻译就按我自己的实操经验来把一个完整的流程捋清楚TensorRT到底是什么、为什么它能加速、怎么装、装完怎么测、以及那些文档里根本不会告诉你的坑。1. 先搞懂TensorRT在做什么不只是“加速”二字很多人一上来就急着装装完跑一遍demo发现确实快了但说不清为什么快。这其实挺危险的因为后面一旦遇到性能不符合预期的情况你根本不知道是哪里出了问题。1.1 网络加速的三个本质手段TensorRT是NVIDIA出的深度学习推理优化器注意这个词优化器。它不是推理框架不做训练它干的事情是把你已经训练好的模型拿来“重新加工”一遍让它在你手头那张GPU上跑得更快、占更少显存。它的加速手段核心是三板斧第一是层融合。训练框架里一大堆算子都是按逻辑写的但GPU执行算子是有固定开销的一个卷积可能算了也就几微秒但启动一次kernel可能要几十微秒。TensorRT会把可以合并的层合并起来比如ConvBatchNormReLU经常被融合成一个算子把kernel启动次数从三次压到一次。层数越多、结构越规整的模型收益就越明显。第二是精度校准。训练的时候我们用FP32因为要保梯度。但推理的时候其实没那么多要求。TensorRT可以把权重和激活值降到FP16甚至INT8代价是精度损失很小但速度可能翻倍甚至翻三倍。这块就是大家常说的量化也是TensorRT最吸引人的地方。第三是内存复用和内核自动调优。TensorRT会分析整个计算图的生命周期把能共享的显存合并起来避免每个层单独申请释放带来的开销和碎片。同时它内部针对每种算子都预置了大量kernel实现在build engine的时候做一个autotuning挑一个在当前GPU上跑得最快的。我自己的感受是模型越接近“纯卷积网络”TensorRT优化效果越明显。而那种充满了reshape、gather、动态shape、自定义算子的小模型收益没那么大还容易报错。1.2 它和ONNX Runtime、PyTorch的关系这个是我被问过最多的问题。很多人搞不清我模型是PyTorch训练的凭什么让TensorRT来推理中间隔着什么东西训练框架是“全地形车”它要考虑梯度、自动微分、各种动态逻辑什么都能干但什么都不极致。张量在PyTorch里可能已经被一个比较高效的原生推理接口加速过比如PyTorch 2.0的compile但它仍然是一个通用方案。TensorRT是一个专用方案。它不关心你训练时怎么写的只关心你推理时要执行的计算图长什么样、算子是什么、数据流是什么形状。那怎么把PyTorch模型给到TensorRT中间的桥一般就是ONNX。PyTorch模型先导出为一个ONNX文件TensorRT直接读这个ONNX文件来构建engine。当然你也可以走TensorRT的Python API一层层搭网络但那种方式基本没人用复杂模型根本搭不动。所以在实际项目流里ONNX就是那个中间格式。还有一种情况是你有CUDA的cudaGraph或者直接写CUDA kernel。那是终极玩家干的事普通人用TensorRT就够了。理解了这个关系之后后面的安装和测试就顺理成章了你得先把环境装好TensorRT才能去读ONNX你得先搞清楚你的GPU算力才知道该装哪个版本。1.3 哪些场景真正适合上TensorRT说句实话不是所有推理场景都适合TensorRT。我用下来觉得适合的场景有这么几个特征模型结构固定输入shape基本不变。比如固定分辨率的目标检测、固定帧数的视频分析。动态shape比如目标数量不固定的NMS输出TensorRT虽然支持但性能会打折。高吞吐、低延迟要求的线上服务。TP99要压到几毫秒以内的那种。GPU资源紧张想在同样的卡上塞更多路并发。INT8量化需求想在嵌入式设备比如Jetson上跑大一点的模型。反过来如果你的模型还在频繁迭代、每两天改一次结构或者你的部署目标是CPU又或者模型里有大量TensorRT不支持的算子比如某些特殊的NMS、非标准RoI Align实现那TensorRT的性价比就没那么高。这种情况下你需要考虑ONNX Runtime、OpenVINO或者先跟算法团队把模型结构改得更规整一点。2. 安装前的关键准备工作版本匹配是第一道坎TensorRT安装本身不难难的是版本匹配。我见过太多人装完跑不起来最后发现是CUDA版本和TensorRT对不上或者GPU算力太老新版本TensorRT直接不支持。2.1 GPU算力与CUDA版本自查在动手之前你先确认三件事第一你的GPU型号和算力。打开终端跑一下nvidia-smi能看到显卡型号和当前驱动版本。算力的话去NVIDIA官方的CUDA GPU列表查一下比如RTX 3090是8.6A100是8.0Jetson Orin是8.7老一点的V100是7.0T4是7.5。算力为什么重要因为TensorRT对GPU算力有要求它生成的engine是跟特定GPU绑定的。你在一张卡上build出来的engine换到另一张卡上未必能跑。就算能跑性能也大概率不对因为autotuning的kernel选型依赖具体硬件。第二你的CUDA版本。nvidia-smi显示的是驱动支持的CUDA最高版本不代表你系统里装了对应的CUDA toolkit。你还需要敲nvcc -V来看你当前系统里实际的CUDA版本。TensorRT在不同的版本有不同的CUDA依赖要求。第三你的cuDNN版本。TensorRT虽然很多算子是自己实现的但卷积类算子很多地方还会调用cuDNN所以cuDNN版本也要匹配。我的建议是直接用一条命令check全部环境nvidia-smi nvcc -V dpkg -l | grep cudnn python3 -c import torch; print(torch.__version__, torch.version.cuda)如果你是用conda环境跑算法那系统级的CUDA toolkit其实可能不重要因为PyTorch自带了CUDA runtime。但TensorRT不是它安装之后要链接到系统CUDA的库所以还是要对齐。2.2 版本对应关系怎么查TensorRT在10.x之后版本命名变了老版本是8.x.x现在最新的已经到了10.x。版本对应关系简单来说官方文档里有专门的版本兼容性表格。比如TensorRT 10.x版本最低要CUDA 12.0、cuDNN 9.xTensorRT 8.x对应CUDA 11.x和cuDNN 8.x。有一个非常常见的坑你装完TensorRT之后import tensorrt成功了但builder跑起来报cudnn版本不对这种就是系统cuDNN版本太老或者太新。我的经验是如果只是学习测试推荐在一个干净的容器环境里安装。NVIDIA官方出了一个NGC容器里面CUDA、cuDNN、TensorRT全部预装好了且版本匹配直接用就完事完全不用折腾。我把这个放在后面章节详细说。2.3 官方文档去哪找TensorRT的官方文档就是最好的参考网站的Developer Zone里有全套安装指南、API参考和样例代码。英文看不太明白的话其实看安装那页的表格就够了核心信息就版本匹配那一页。我的建议是不要百度随便搜个旧教程。TensorRT版本迭代很快API变动也很大8.x到9.x就改了一些接口10.x又改了一些一个月前别人写的教程拿到现在可能就跑不通。认准官网和当前版本号。3. 安装的三种典型方式与实操记录现在进入正题。TensorRT的安装方式主要有三种tar包安装、Debian包安装、以及NGC容器直接用。我实际在项目里都试过分别说下适用场景和步骤。3.1 方式一tar包安装最通用方案tar包安装的优势是干净、可控、不污染系统包管理器。你只要解压到一个目录把动态库路径加进LD_LIBRARY_PATH把Python模块塞进site-packages或者直接把路径加到PYTHONPATH就行。# 假设你已经下载了TensorRT-10.x.x.x.Linux.x86_64-gnu.cuda-12.0.tar.gz tar -xzvf TensorRT-10.x.x.x.Linux.x86_64-gnu.cuda-12.0.tar.gz cd TensorRT-10.x.x.x # 添加动态库路径 export LD_LIBRARY_PATH$PWD/lib:$LD_LIBRARY_PATH # 安装Python API建议在目标conda环境里执行 pip install python/tensorrt-*-cp3x-none-linux_x86_64.whl # 如果用到onnx-tensorrt还需要这个包 pip install python/tensorrt_onnx-*.whltar包安装虽然要自己配环境变量但好处是全部东西集中在一个文件夹里卸载就是删文件夹。生产服务器上我倾向于这种方式方便定位问题也方便多版本共存比如同时测8.x和10.x。需要注意整个目录不能随便挪动因为内部很多相对路径的引用。如果你要换路径先把LD_LIBRARY_PATH和PYTHONPATH重新指过去然后再测。3.2 方式二Debian包安装Ubuntu系推荐如果你是在Ubuntu的裸机环境上做测试用deb包最省事装完之后库文件会自动放到系统默认路径LD_LIBRARY_PATH都不用配。# 以Ubuntu 20.04 CUDA 12.0为例 dpkg -i nv-tensorrt-local-repo-ubuntu2004-cuda12.0-trt10.x.x.x_1.0-1_amd64.deb # 更新apt源 apt-get update # 安装主包 apt-get install tensorrt # 如果想用Python API装这个 apt-get install python3-libnvinfer # ONNX相关 apt-get install tensorrt-onnx这里有几点想跟大家说一是装deb包之后建议跑一下dpkg -L tensorrt看看具体文件装到哪了心里有个数。它的so文件一般放在/usr/lib/x86_64-linux-gnu/python包放在/usr/lib/python3/dist-packages/。二是如果你系统里本身有PyTorch的conda环境deb包装的Python模块默认是进系统解释器的你在conda环境里import可能会找不到。这种情况下要么修改conda环境的site-packages链接要么直接用tar包方式。我踩过这个坑后面单独说。三是版本冲突。如果之前装过老版本TensorRT建议先彻底卸载再装新的不然后续构建engine的时候会出现各种诡异的符号找不到错误。3.3 方式三NGC容器我强烈推荐测试党用这个如果你想快速验证TensorRT性能又不想折腾环境NGC容器是最聪明的选择。这些容器在NVIDIA GPU Cloud上有专门的镜像比如nvcr.io/nvidia/tensorrt:xx.yy.zz-py3里面CUDA、cuDNN、TensorRT全部预装好版本匹配直接启动就能用。docker pull nvcr.io/nvidia/tensorrt:24.12-py3 docker run --gpus all -it --shm-size16g --rm nvcr.io/nvidia/tensorrt:24.12-py3进去之后输入python3 -c import tensorrt as trt; print(trt.__version__)能正常输出版本号就说明环境OK。容器里还有现成的样例代码在/opt/tensorrt/samplestrtexec命令行工具也已经在PATH里了。实测下来NGC容器最大的价值是提供了一个“标准答案”。当你自己的环境跑不通时先在容器里跑一遍如果容器里能通说明问题出在你的环境配置如果容器里也报错那大概率是代码本身有兼容性问题。这个思路排障效率极高。3.4 安装后的验证脚本不管用哪种方式安装装完都要先跑一个快速验证。TensorRT的Python API验证很简单import tensorrt as trt print(TensorRT version:, trt.__version__) print(CUDA version:, trt.__cudnn_version__) # 创建一个最简单的engine来验证builder可用 logger trt.Logger(trt.Logger.WARNING) builder trt.Builder(logger) network builder.create_network(1 int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) # 构造一个输入层 input_tensor network.add_input(input, trt.float32, (1, 3, 224, 224)) identity network.add_identity(input_tensor) network.mark_output(identity.get_output(0)) config builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 1 20) engine builder.build_serialized_network(network, config) print(Engine build SUCCESS)这段代码做了一件事构建一个简单的Identity网络并序列化engine。如果最后能打印出SUCCESS说明整个TensorRT运行时、builder、显存管理都正常工作了。我用这个脚本在两台新机器上验证过每次都很快速。4. 测试与基准评测跑通第一个推理工程安装完成只是热身真正的重点是测试。我在这一节从两个层面来讲一是命令行工具trtexec的用法二是通过Python API做一个完整的测试流程。4.1 先用trtexec跑通全流程trtexec是一个命令行工具几乎不需要写代码就能完成“ONNX转engine→跑推理→出benchmark报告”的全流程。这个工具我的定位是“侦查兵”先用它快速验证一个模型能不能被TensorRT接受大概性能是多少然后再上代码做具体集成。一个最常用的命令trtexec --onnx./model.onnx \ --saveEngine./model.trt \ --fp16 \ --workspace4096各参数啥意思我拆一下--onnx./model.onnx指定输入模型文件。--saveEngine./model.trt把构建出来的engine保存到本地文件这是你可以直接拿去加载部署的文件。--fp16开启FP16推理。如果想要INT8则需要额外指定--int8同时提供校准数据--calib参数不是所有模型都适合INT8的。--workspace4096构建阶段允许TensorRT使用的显存大小单位MB。这是个很邪门的参数给太小engine可能构建失败给太大不会爆显存但会影响构建时间。构建完成之后trtexec会自动运行一次benchmark输出的报告里有几个关键指标我在这里直接教你看Generated engine和Loaded engine表示构建和加载都成功了。Average latency平均延迟单位毫秒这是你最关心的指标。Throughput吞吐量单位是FPS或者QPS同一时间能处理多少帧或多少次推理。GPU Compute Time实际在GPU上计算的时间这个时间是不包括H2D/D2H数据传输的纯计算时间。如果你的工程里瓶颈在数据拷贝这个指标会很小但总延迟很大那就应该从数据管线优化。我自己的经验第一次跑trtexec不要太惊讶于它的数据因为你还没做任何profile和调优。先把它当作“能不能工作”的判据后续再慢慢调参数。4.2 用Python API完成动态shape推理trtexec适合验证静态shape的模型。但实际部署中很多模型需要动态shape支持——比如检测模型输入分辨率可变或batch size可变。这时就要用Python API来构建engine并且明确设置动态维度。一个关键点TensorRT从8.x开始动态shape是默认的需要在网络定义时用-1占位并在builder config里设置优化profile。import tensorrt as trt import numpy as np logger trt.Logger(trt.Logger.WARNING) builder trt.Builder(logger) network builder.create_network(1 int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) # 动态batch的输入第一个维度设为-1 input_tensor network.add_input(input, trt.float32, (-1, 3, 224, 224)) # 一个卷积层演示一下真正的计算图 conv network.add_convolution(input_tensor, 32, (3, 3), weightstrt.Weights(np.ones((32, 3, 3, 3), dtypenp.float32)), biastrt.Weights(np.zeros(32, dtypenp.float32))) conv.stride (1, 1) conv.padding (1, 1) network.mark_output(conv.get_output(0)) config builder.create_builder_config() config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 1 30) # 动态shape必须设置profile指定最小、常规、最大形状 profile builder.create_optimization_profile() profile.set_shape(input, (1, 3, 224, 224), (8, 3, 224, 224), (16, 3, 224, 224)) config.add_optimization_profile(profile) engine builder.build_serialized_network(network, config) if engine is None: raise RuntimeError(engine build failed) print(dynamic shape engine build OK)跑动态shape时有一个很常见的坑你创建了context设好输入shape后如果输入的实际形状超出了profile里定义的范围TensorRT会直接报错。比如上面最大是16你batch设成32构建时根本不会报错运行时才会炸。再一个坑是显存。动态shape因为要覆盖多个shapeTensorRT内部会申请更大的workspace来应对最坏情况所以显存占用会比静态shape大不少。这个在部署到小显存设备时要预算好。4.3 用ONNX模型做真实基准对比前面都是构造的demo网络真正能说明问题的还是拿一个真实模型来测。我拿一个常见的目标检测模型举例流程是这样的第一步先把PyTorch模型导出为ONNXimport torch model torch.load(yolov5s.pt) # 仅供示例 model.eval() dummy_input torch.randn(1, 3, 640, 640) torch.onnx.export(model, dummy_input, yolov5s.onnx, opset_version12, # TensorRT对一些opset支持有限建议12或更高 input_names[input], output_names[output], dynamic_axes{input: {0: batch}})导出ONNX有几个非常有用的经验opset_version不要太老也不要太新12~16之间通常是最稳的。太老的opset有些算子导不出来太新的TensorRT可能没跟上。导出时不要开optimizeFalse保持默认。遇到Squeeze、Gather、Reshape这种算子导出时多加小心这些是TensorRT图中最常出问题的地方。第二步用trtexec转成engine并做个快速性能测试trtexec --onnx./yolov5s.onnx --saveEngine./yolov5s.trt --fp16 --workspace4096第三步用Python写一个完整的benchmark把PyTorch、ONNX Runtime、TensorRT三种引擎的实际延迟对比出来import time import numpy as np import tensorrt as trt import torch import onnxruntime as ort # 准备输入 x np.random.randn(1, 3, 640, 640).astype(np.float32) x_gpu torch.from_numpy(x).cuda() # --- PyTorch baseline --- model torch.load(yolov5s.pt).cuda().eval() with torch.no_grad(): for _ in range(10): model(x_gpu) # warmup torch.cuda.synchronize() start time.time() for _ in range(100): model(x_gpu) torch.cuda.synchronize() print(PyTorch avg:, (time.time() - start) * 10, ms) # --- OnnxRuntime --- sess ort.InferenceSession(yolov5s.onnx) for _ in range(10): sess.run(None, {input: x}) start time.time() for _ in range(100): sess.run(None, {input: x}) print(ONNX Runtime avg:, (time.time() - start) * 10, ms) # --- TensorRT --- engine load_engine(yolov5s.trt) # 简化表述实际加载需要反序列化 context engine.create_execution_context() # 假设batch1输入shape固定 context.set_input_shape(input, (1, 3, 640, 640)) output np.empty((1, 25200, 85), dtypenp.float32) # 根据模型实际输出 for _ in range(10): context.execute_v2([x_gpu.data_ptr(), torch.from_numpy(output).cuda().data_ptr()]) torch.cuda.synchronize() start time.time() for _ in range(100): context.execute_v2([x_gpu.data_ptr(), torch.from_numpy(output).cuda().data_ptr()]) torch.cuda.synchronize() print(TensorRT avg:, (time.time() - start) * 10, ms)注意我上面这段代码是简化演示实际跑的时候TensorRT加载engine要用runtime.deserialize_cuda_engine输入输出要用context.set_tensor_address。但核心逻辑就是这三件套准备输入→execute→计时。我实测过一组数据不同模型不同卡结果差异很大仅作量级参考某个检测模型输入640x640在单张消费级显卡上PyTorch大概15~20msONNX Runtime大概10~14msTensorRT FP16大概5~8ms。这个差距在服务端会更明显因为你是真实在跑并发请求。如果你的模型层数更深、结构更规整TensorRT的领先幅度还会拉大。4.4 序列化engine的保存与加载engine构建是非常耗时的大模型可能几分钟到几十分钟你不可能每次启动服务都重新构建。正确的做法是构建一次保存为.trt文件之后直接加载。保存的时候注意一点engine是否跨GPU通用答案是否定的。TensorRT生成的engine与构建它的GPU深度绑定换卡后不能保证能加载。所以在生产环境里你必须在目标机器上重新构建engine或者在构建时确保序列化和反序列化用的是一样的驱动和GPU型号。加载ruozdef load_engine(trt_engine_path): runtime trt.Runtime(trt.Logger(trt.Logger.WARNING)) with open(trt_engine_path, rb) as f: engine_data f.read() engine runtime.deserialize_cuda_engine(engine_data) return engine这段代码在服务启动时调用加载速度取决于engine文件大小一般几百MB的engine也就一两秒的事。5. 实际踩过的坑环境、API和性能排障实录这类脑力劳动型技术文章最值钱的部分永远是踩坑记录。我在TensorRT上摔过的跟头不少整理了几个高频问题按排查顺序写下来。5.1 环境类问题排查排查1import tensorrt报错ModuleNotFoundError大概率是Python环境不对。tar包方式装的wheel是给特定Python版本的比如cp311只能给Python 3.11用。你conda环境切换到3.8之后import自然会失败。检查方式是python3 -V和pip install tensorrt的wheel文件名对齐。排查2import tensorrt成功但创建builder时崩溃或报libcudnn版本不对这个就是系统cuDNN和TensorRT版本不匹配。最快的办法是到TensorRT的lib目录用ldd检查动态链接ldd libnvinfer.so | grep cudnn看看它实际链接到了哪个版本的libcudnn。如果系统里同时装了多个版本有时候会链到错误的那一个。解决办法是清理老版本或者显式设置LD_LIBRARY_PATH把正确路径放在最前面。排查3engine在构建机器上能用换一台机器加载不了这是正常的TensorRT的engine和GPU绑定。解决办法是部署时要么在目标机器上重新build engine要么在构建时考虑用kSTRICT_TYPE_CONSTRAINTS等flag让它尽量跨平台兼容但这东西也只是缓解不是根治。5.2 API使用类问题排查排查4dynamic shape 报 shape mismatch最常见的原因就是输入维度超了profile的范围。另一个原因是在执行之前没有调用context.set_input_shape。排查5execute_v2报Error Code 1: Cuda Runtime这个一般不是代码逻辑的问题而是显存不足或者没有正确设置tensor address。如果是显存不足用nvidia-smi看一眼实时显存占用或者把workspace调小。排查6NMS算子不支持很多检测模型里的NMS是自定义实现TensorRT不一定支持。解决办法是ONNX导出的时候把NMS部分剥离掉由上层业务代码自己处理后端逻辑。或者使用TensorRT自带的EfficientNMS插件但这需要你改模型结构属于进阶玩法。5.3 性能不符合预期怎么分析性能不符合预期的排查思路我推荐一个“三层法”第一层确认是不是数据拷贝影响了。TensorRT的execute只统计GPU计算时间如果你的工程是每帧都把数据从CPU拷到GPU再拷回来那这部分开销可能远大于计算时间。优化方向是pinned memory、异步拷贝、或者把整个pipeline搬进GPU。第二层确认是不是动态shape拖了后腿。同一个模型静态shape和动态shape性能差距可能有20%甚至更多。如果业务允许固定batch size和输入尺寸收益立竿见影。第三层确认是不是模型结构本身不适合。比如模型里有很多小算子、大量拼接、动态循环TensorRT优化不动。这种情况要么改模型结构要么并行跑多个engine实例从系统层面压榨吞吐量。5.4 量化时的精度回落用INT8量化是TensorRT收益最大也最危险的操作。精度回落是正常的但回落到什么程度算不可接受取决于业务。我自己的经验是先跑FP16确认精度和速度都能接受再考虑INT8。INT8校准需要精选校准数据。校准集要覆盖你业务里的典型场景不要用训练集也不要用白噪声。我见过有人用一张纯黑图做校准出来的量化模型在真实数据上直接飞了。校准后一定要做全量测试集上的精度对比不能只看几个样例。6. 进阶使用建议从跑通到做好到这里TensorRT的全流程已经带大家走了一遍。我在收尾时想聊两个方向也是我实际项目中遇到较多需求、再往后走一定会碰到的话题。一个是多实例并发。当你需要高吞吐时单engine不一定能跑满GPU。可以考虑多个context并发执行每个context绑定一个CUDA stream这样可以提升GPU利用率和整体吞吐。这个方案做得好同一块卡上并发路数能提升一倍以上。另一个是插件开发。如果模型里有一些TensorRT原生不支持的算子比如自定义激活、特殊归一化你可以用C写插件。这需要你对CUDA有一定基础但写完之后收益很大因为你可以完全控制kernel实现。Python API层面也可以做但性能和内存管理上远不如C插件来得顺手。还有一点关于工作流的想法TensorRT的版本迭代速度很快每次升级都要重新验证一遍精度和性能。我的习惯是每次上线之前固定一个“黄金组合”版本号比如CUDA 12.2 cuDNN 8.9 TensorRT 10.x然后在测试机上跑一份完整的基准测试记录在案。后面如果有人改了环境出了性能问题翻出这份记录对比就能快速定位。最后再分享一个我自己的习惯在项目的目录里放一个trt_version_info.txt把当前用的GPU型号、驱动版本、CUDA版本、TensorRT版本、cuDNN版本、模型文件名全部记录下来。这个东西在协作开发时特别有用任何一个人环境不对看这个文件就能自查省掉很多来回扯皮的时间。TensorRT的学习曲线说陡也陡说平也平关键是把环境这关过了理解了静态shape和动态shape的区别剩下的就是调参和踩坑的经验积累。你只要把这个流程完整走一遍后续再遇到其他模型就只剩熟能生巧的事了。