一、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 如何选择?

我给大家一个简单的决策树:

  1. 只用NVIDIA GPU? → TensorRT,没得选
  2. 需要跨平台? → ONNX Runtime
  3. 有特殊算子? → 先用TensorRT/ONNX Runtime,不行再写CUDA
  4. 追求极致性能? → TensorRT + 手动CUDA优化

注意:别一上来就想着自己写CUDA。我见过太多人,花了两周写了个CUDA算子,性能还不如TensorRT自动优化的版本。先用好现成的工具,再考虑手写优化。

好了,这一章就到这里。下一章我们深入TensorRT,看看它到底是怎么把模型「变快」的。记住一句话:GPU加速不是银弹,选对工具、用对场景,才能真正发挥它的威力。