1. TensorRT概述:什么是TensorRT,为什么需要它,推理优化的核心思想

1.1 从一次线上事故说起

我记得那是三年前的一个深夜。

我们团队部署的一个人脸识别服务,在压测到200 QPS时,GPU利用率才30%,但延迟已经飙到了150ms。客户那边等着上线,老板在群里@我,说「再优化不了就换方案」。

我盯着nvidia-smi看了半天,显存带宽跑满了,但核心利用率上不去。说白了,模型在GPU上跑得「一卡一卡的」,大量时间花在了启动内核、搬运数据这些杂事上。

后来怎么解决的?

上了TensorRT,配合多流并发。同样的硬件,QPS从200干到了1200,延迟反而降到了8ms。

嗯,这就是我为什么一定要讲这门课的原因。

1.2 什么是TensorRT?

TensorRT,是NVIDIA推出的深度学习推理优化引擎。

它不是框架,不是PyTorch,也不是TensorFlow。它是个「编译器」——专门把你的训练好的模型,编译成能在GPU上跑得飞快的推理程序。

一句话总结:TensorRT = 深度学习模型的「性能榨干机」。

它支持主流的模型格式:

  • ONNX(最常用,我一般都用这个入口)
  • PyTorch(通过torch-tensorrt)
  • TensorFlow(通过TF-TRT)
  • 自定义C++模型

1.3 为什么需要TensorRT?

你想想看,我们训练模型时,用的PyTorch或TensorFlow,它们的设计目标是什么?

灵活性。 方便你改网络结构、调参数、做实验。

但推理阶段呢?

模型已经固定了,不需要再反向传播了。这时候,框架里大量的「灵活性」代码就成了累赘。每次前向传播,框架都要动态解析计算图、检查张量形状、调度内核……这些开销,在推理场景下完全是浪费。

我举个例子:

你用PyTorch跑一个ResNet-50,单张图片推理可能要15ms。但用TensorRT优化后,同样的模型,同样的GPU,可能只需要3ms。

5倍的差距,不是模型变了,是「执行方式」变了。

我的经验: 在项目中,我见过最夸张的案例——一个BERT模型,PyTorch推理延迟120ms,TensorRT优化后降到18ms。接近7倍。客户当场就拍板采购了。

1.4 推理优化的核心思想

TensorRT到底做了什么?说白了,就三件事:

1.4.1 图优化与算子融合

深度学习模型本质上是个计算图。节点是算子(Conv、ReLU、BN等),边是张量。

框架执行时,每个算子单独启动一个CUDA内核。你想想看,一个ResNet-50有上百个算子,每个算子都要启动内核、读写显存……这中间的开销有多大?

TensorRT的做法是:把相邻的算子合并成一个。

比如 Conv + BN + ReLU,三个算子合并成一个大的CUDA内核。数据不用来回写显存了,直接在寄存器里传。

// 优化前:三个独立内核
conv_kernel<<<grid, block>>>(input, weight, conv_out);
bn_kernel<<<grid, block>>>(conv_out, gamma, beta, bn_out);
relu_kernel<<<grid, block>>>(bn_out, relu_out);

// 优化后:一个融合内核
fused_conv_bn_relu_kernel<<<grid, block>>>(input, weight, gamma, beta, relu_out);

我曾经在一个项目中,光靠算子融合,就把推理速度提升了40%。而且完全不用改模型结构,纯白嫖的性能。

1.4.2 精度校准与量化

训练时我们一般用FP32,精度高,但计算慢、显存占用大。

推理时,其实不需要那么高的精度。TensorRT支持:

精度模式 显存占用 推理速度 适用场景
FP32 基准 基准 对精度极度敏感
FP16 减半 约2x 大部分场景
INT8 1/4 约4x 视觉模型、分类任务

注意: INT8量化需要校准数据集。我曾经踩过一个坑——直接用训练集做校准,结果推理时遇到分布不同的数据,精度掉了5个点。后来改用验证集+少量线上数据混合校准,才稳住。

1.4.3 内存复用与内核自动调优

TensorRT会分析整个计算图,提前规划好每块显存什么时候用、什么时候释放。避免频繁的malloc/free。

同时,它会自动搜索最优的CUDA内核配置——比如block大小、shared memory分配、循环展开因子等。这些手工调起来非常痛苦,TensorRT帮你做了。

我刚开始接触时,总觉得「自动调优能比我手写的好?」

后来跑了个benchmark,发现TensorRT选的配置,比我折腾了两天的手工调优还快15%。嗯,服了。

1.5 TensorRT的典型工作流程

实际项目中,我一般这么用:

  1. 导出模型:从PyTorch/TF导出ONNX
  2. 构建Engine:用TensorRT读取ONNX,指定精度(FP16/INT8)
  3. 序列化:把优化好的Engine保存为.plan文件
  4. 推理:加载.plan文件,执行推理
// 伪代码示意
// 1. 构建阶段
IBuilder* builder = createInferBuilder(gLogger);
INetworkDefinition* network = builder->createNetwork();
// 解析ONNX
parser->parse(onnx_file, ...);
// 设置精度
config->setFlag(BuilderFlag::kFP16);
// 构建引擎
ICudaEngine* engine = builder->buildEngineWithConfig(*network, *config);

// 2. 推理阶段
IExecutionContext* context = engine->createExecutionContext();
// 绑定输入输出
context->enqueueV2(buffers, stream, nullptr);

避坑指南: 我曾经在构建Engine时忘了设置工作空间大小,结果大模型直接OOM。建议显存够的话,把workspace设到1GB以上。

1.6 这门课你会学到什么?

光知道TensorRT能加速还不够。实际部署时,你会遇到一堆问题:

  • 多路视频流怎么并发推理?
  • 怎么避免显存碎片?
  • 动态batch怎么处理?
  • 多GPU怎么负载均衡?

这门课,就是把这些「实战坑」一个一个填平。

我会从单流推理讲起,逐步深入到多流并发、异步处理、显存池化、多卡协同。每一章都有代码、有踩坑记录、有性能对比数据。

你想想看,如果能把GPU利用率从30%干到90%,同样的硬件成本,吞吐量翻3倍——这不比申请采购新卡香多了?

好,第一章就到这里。下一章,我们直接上手——搭建TensorRT开发环境,跑通第一个推理程序。