4、并发推理场景:在线服务(实时)、离线批处理(吞吐)、边缘设备(延迟与功耗)
聊到并发推理,我脑子里第一个蹦出来的问题就是:你到底要服务谁?
这可不是一句废话。你想想看,一个给用户实时聊天的AI,跟一个半夜跑数据报表的AI,它们对GPU的要求能一样吗?完全不一样。我个人习惯把并发推理场景拆成三大类来思考——在线服务、离线批处理、边缘设备。这三兄弟,脾气秉性完全不同。
4.1 在线服务:低延迟是命根子
在线服务,说白了就是用户点一下,模型就得立刻出结果。比如智能客服、实时翻译、AI绘画的即时生成。这类场景,延迟就是用户体验。我见过太多项目,模型精度再高,如果响应时间超过500ms,用户直接就跑了。
我在项目中遇到过最头疼的事:一个在线翻译服务,并发量一上来,GPU显存就爆了。为什么?因为每个请求都独立加载模型,显存被重复占用了。后来怎么解决的?显存池化。
具体做法是这样的:
- 预分配一块固定大小的显存池,比如4GB。
- 所有请求进来,都从池子里借显存,用完立刻归还。
- 池子满了?那就排队等,或者直接拒绝新请求(这叫背压)。
嗯,这里要注意:在线服务的批处理大小(batch size)通常很小,甚至就是1。因为你要等凑够一批再算,延迟就上去了。我个人建议,在线服务用动态batch——设置一个超时时间,比如5ms,到了时间不管凑没凑够,直接开算。
4.2 离线批处理:吞吐量就是一切
离线批处理,场景完全反过来。比如每天凌晨跑用户画像、批量生成商品描述、大规模数据标注。这类任务,用户不坐在屏幕前等,所以延迟不是问题,吞吐量才是王道。
你想想看,同样的GPU,一次处理1张图和一次处理32张图,吞吐量能差出十几倍。所以离线场景的核心思路就是:把batch size往大了怼,直到显存撑爆为止。
我曾经做过一个离线推理任务,模型是BERT-large,单卡推理太慢了。后来我做了三件事:
- 显存池预分配超大块,比如一次分配8GB,专门给大batch用。
- 流水线并行:把模型切成几段,不同GPU分别算不同段,数据像流水线一样流过去。
- 异步数据加载:CPU提前把下一批数据准备好,GPU算完一批立刻接下一批,中间不卡顿。
结果呢?吞吐量提升了4倍。但代价是显存占用几乎翻倍。这就是典型的用空间换时间。
离线批处理的显存池策略,跟在线服务完全相反:
| 维度 | 在线服务 | 离线批处理 |
|---|---|---|
| batch size | 小(1~8) | 大(32~256) |
| 显存池分配 | 小粒度、快速回收 | 大块预分配、长期持有 |
| 核心指标 | P99延迟 | 吞吐量(样本/秒) |
| 典型框架 | Triton Inference Server | TensorFlow Serving + 自定义调度 |
4.3 边缘设备:在夹缝中求生存
边缘设备,比如手机、摄像头、IoT盒子。这类场景最让人头疼——既要低延迟,又要低功耗,显存还小得可怜。你想想看,一个Jetson Nano才4GB显存,还要跑实时检测,怎么搞?
我做过一个边缘端的行人检测项目,设备是Jetson Xavier NX。刚开始直接把服务器上的模型搬过去,结果帧率只有5fps,功耗还飙到15W。后来我做了三件事:
- 模型量化:从FP32降到INT8,显存占用直接砍掉75%,速度翻倍。
- 显存池复用:边缘设备没有虚拟内存,显存池必须提前规划好。我预先分配了输入、输出、中间特征图三块固定区域,推理时反复用,绝不动态申请。
- 功耗感知调度:如果检测到电池电量低,自动降低batch size或者跳帧处理。
嗯,这里有个坑。边缘设备的显存池大小,不能按峰值来设。为什么?因为峰值场景可能只占1%的时间,但你为了这1%多占了一倍显存,平时就浪费了。我建议的做法是:按90%分位点来分配显存池,剩下的10%场景,用降级策略处理——比如降低分辨率、跳过某些帧。
4.4 三种场景的显存池设计对比
说了这么多,我画了一张图来总结这三种场景的显存池设计思路。你看完应该就明白了。
你看,三种场景对显存池的要求完全不同。在线服务要快,离线批处理要大,边缘设备要省。没有一种显存池策略能通吃所有场景。
最后说一句:显存池不是万能的。它解决的是显存分配和回收的效率问题,但如果你模型本身太大,或者数据量远超预期,该加卡还是得加卡。我曾经在一个项目里死磕显存池优化,折腾了两周,吞吐只提升了15%。后来加了一张卡,直接翻倍。嗯,有时候硬件才是最快的优化。
想要更多资料,联系可可