一、GPU推理加速概述:为什么需要GPU加速、GPU vs CPU推理对比、主流GPU推理框架介绍
大家好,我是你们这堂课的主讲。今天咱们聊聊GPU推理加速这件事。说实话,我刚开始做深度学习部署那会儿,也踩过不少坑。记得第一次把训练好的模型放到线上,CPU跑一个请求要好几秒,用户直接骂娘了。后来换了GPU,速度直接起飞。嗯,这就是我们今天要聊的核心——为什么非用GPU不可。
1.1 为什么需要GPU加速?
说白了,深度学习推理就是做大量的矩阵运算。你想想看,一个ResNet-50模型,一次前向传播就要做几十亿次乘加操作。CPU虽然很强大,但它设计出来是为了处理各种复杂任务,不是专门做矩阵乘法的。GPU就不一样了,它天生就是为并行计算设计的。
我给大家算笔账:
- 一个典型的BERT模型推理,CPU需要200-500毫秒
- 同样的模型在GPU上,只需要5-15毫秒
- 差了整整一个数量级
为什么会这样?因为GPU有几千个CUDA核心,可以同时处理大量数据。CPU呢?最多也就几十个核心。你想想看,一个工厂里,CPU是几个全能老师傅,GPU是几千个专门拧螺丝的工人。拧螺丝这种重复劳动,当然是工人越多越快。
核心观点:深度学习推理的本质是矩阵运算的并行计算。GPU的并行架构天然适合这种计算模式,加速比通常在10-50倍之间。
1.2 GPU vs CPU推理对比
我在项目中经常被问到:「能不能用CPU凑合一下?」我的回答是:看场景。咱们来做个详细对比。
| 对比维度 | CPU | GPU |
|---|---|---|
| 核心数量 | 4-64个 | 数千个 |
| 单核性能 | 强 | 弱 |
| 并行能力 | 弱 | 强 |
| 内存带宽 | 50-100 GB/s | 900-2000 GB/s |
| 延迟 | 低(微秒级) | 较高(毫秒级) |
| 功耗 | 15-150W | 75-400W |
| 适用场景 | 小模型、低延迟、边缘设备 | 大模型、高吞吐、云端 |
这里有个坑,我曾经踩过:别以为GPU在所有场景下都更快。如果你的模型很小,比如一个简单的逻辑回归,或者batch size只有1,那CPU反而可能更快。为什么?因为GPU有启动开销,数据从内存搬到显存需要时间。小模型这点计算量,还不够塞牙缝的。
避坑指南:我曾经在一个边缘设备项目里,强行用GPU跑一个MobileNet。结果发现,GPU的启动延迟比模型推理时间还长。后来换回CPU,反而更快。所以,选型要看具体场景,别盲目追求GPU。
1.3 主流GPU推理框架介绍
好,既然决定用GPU了,那用什么框架呢?目前市面上主流的就三个:TensorRT、ONNX Runtime、还有直接调CUDA。我一个个说。
1.3.1 TensorRT
TensorRT是NVIDIA的亲儿子。说白了,它就是专门为NVIDIA GPU做推理优化的。我个人的习惯是,只要项目里用的是NVIDIA的卡,优先考虑TensorRT。
它厉害在哪?
- 层融合:把多个计算层合并成一个,减少显存读写
- 精度校准:支持FP16、INT8量化,速度翻倍
- 动态张量:支持可变输入尺寸,不用每次都重新编译
举个例子,我用TensorRT优化过一个BERT模型,FP32精度下延迟从12ms降到了6ms。换成FP16后,直接干到3ms。嗯,这速度提升,客户直接竖大拇指。
// TensorRT推理示例(伪代码)
// 1. 构建引擎
IBuilder* builder = createInferBuilder(gLogger);
INetworkDefinition* network = builder->createNetwork();
// 2. 添加网络层
auto input = network->addInput("input", DataType::kFLOAT, Dims3{3, 224, 224});
auto conv = network->addConvolution(*input->getOutput(0), 64, DimsHW{3, 3}, weight, bias);
// 3. 序列化引擎
builder->buildSerializedNetwork(*network, config);
1.3.2 ONNX Runtime
ONNX Runtime是微软开源的。它的优势在于跨平台、跨硬件。你想想看,如果你的模型要在不同厂商的GPU上跑,或者要同时支持CPU和GPU,那ONNX Runtime就是最佳选择。
我遇到过这样一个项目:客户要求模型能在NVIDIA和AMD的卡上都能跑。TensorRT只支持NVIDIA,CUDA也是NVIDIA的。最后选了ONNX Runtime,配合DirectML后端,完美解决。
适用场景:需要跨平台部署、模型来源多样(PyTorch、TensorFlow、Keras等)、不想被NVIDIA绑定的项目。
1.3.3 CUDA
直接写CUDA代码,这是最底层的方式。说实话,除非你是性能狂魔,或者要做一些框架不支持的特殊操作,否则我不建议直接写CUDA。为什么?开发效率太低了。
我曾经为了优化一个自定义算子,写了三天CUDA代码。调试起来那叫一个痛苦,动不动就显存越界、核函数崩溃。后来发现TensorRT已经支持这个操作了,直接调用API,十分钟搞定。
但CUDA也有它的价值:
- 完全控制硬件资源
- 可以实现框架不支持的自定义操作
- 极致性能优化
// CUDA核函数示例:向量加法
__global__ void vecAdd(float* a, float* b, float* c, int n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
c[i] = a[i] + b[i];
}
}
// 调用核函数
vecAdd<<<(n + 255) / 256, 256>>>(d_a, d_b, d_c, n);
1.4 如何选择?
我给大家一个简单的决策树:
- 只用NVIDIA GPU? → TensorRT,没得选
- 需要跨平台? → ONNX Runtime
- 有特殊算子? → 先用TensorRT/ONNX Runtime,不行再写CUDA
- 追求极致性能? → TensorRT + 手动CUDA优化
注意:别一上来就想着自己写CUDA。我见过太多人,花了两周写了个CUDA算子,性能还不如TensorRT自动优化的版本。先用好现成的工具,再考虑手写优化。
好了,这一章就到这里。下一章我们深入TensorRT,看看它到底是怎么把模型「变快」的。记住一句话:GPU加速不是银弹,选对工具、用对场景,才能真正发挥它的威力。