推理性能指标:吞吐量、延迟、算力利用率、内存带宽瓶颈识别
各位同学,今天我们来聊聊NPU推理性能的四个核心指标。说实话,这玩意儿就像汽车的仪表盘——你不看它,车也能开,但什么时候爆缸了你都不知道。我在调优NPU的这些年,见过太多人一上来就堆算子、改模型,结果性能纹丝不动。为什么?因为你连瓶颈在哪都没搞清楚。
吞吐量(Throughput)——你到底能处理多少活儿?
吞吐量,说白了就是单位时间内能处理多少个请求。比如你的NPU一秒能跑100张图片,那吞吐量就是100 FPS(Frames Per Second)。
计算公式很简单:
Throughput = 总处理样本数 / 总耗时
我个人习惯用batch size来调吞吐量。你想想看,单张图片跑一次推理,和一次塞进去32张图片跑推理,哪个吞吐量高?当然是后者。但这里有个坑——batch size不是越大越好。
关键点:当batch size超过某个阈值,NPU的算力利用率会饱和,再增加batch size只会增加延迟,吞吐量反而上不去。
我在项目中遇到过这样一个案例:某视觉模型,batch size从1调到32,吞吐量提升了5倍;但从32调到64,只提升了10%。这时候就该停了,因为内存带宽已经成了新瓶颈。
延迟(Latency)——用户等得起吗?
延迟,就是单个请求从进来到出去花了多久。注意,这里说的是端到端延迟,包括数据搬运、预处理、推理、后处理。
吞吐量和延迟,其实是跷跷板的两头。你追求高吞吐,就得攒一批请求一起处理,那单个请求的延迟自然就高了。反过来,你想让每个请求秒回,那就得牺牲吞吐量。
我的建议:在线服务(比如人脸识别门禁)优先保延迟,离线批处理(比如视频分析)优先保吞吐量。别搞反了。
我曾经踩过一个坑:为了压延迟,把batch size设成1,结果NPU利用率只有5%,白白浪费算力。后来改成动态batch,根据当前排队请求数自动调整,才平衡了延迟和吞吐。
算力利用率(Utilization)——你的NPU在摸鱼吗?
算力利用率,就是NPU实际干活的时间占比。理想情况下,NPU应该一直满负荷运转。但现实是,它经常在等数据。
利用率低的原因通常有两个:
- 数据搬运瓶颈:数据从DDR搬到NPU片上存储的速度跟不上计算速度
- 算子调度开销:小算子太多,启动和同步的时间比计算还长
我记得有一次调一个NLP模型,NPU利用率只有20%。查了半天,发现是LayerNorm这个小算子被反复调用,每次都要等数据从DDR搬过来。后来我把多个LayerNorm合并成一个,利用率直接飙到70%。
注意:利用率不是越高越好。如果利用率接近100%,说明NPU已经满负荷,这时候再优化算子也没用,得从算法层面下手。
内存带宽瓶颈识别——数据到底卡在哪?
这是最容易被忽视的指标。很多同学一看NPU利用率低,就以为是算子没写好,其实往往是内存带宽不够。
怎么识别?看计算密度(Arithmetic Intensity):
计算密度 = 计算量(FLOPs) / 数据访问量(Bytes)
如果计算密度低,说明模型是带宽敏感型,优化方向应该是减少数据搬运,而不是优化算子。
我常用的诊断方法:
- 跑一次推理,记录NPU的计算时间和数据搬运时间
- 如果数据搬运时间占比超过50%,基本可以断定是带宽瓶颈
- 用Roofline模型画图,看看当前操作落在哪个区域
实战技巧:我曾经优化一个3D卷积模型,发现计算密度只有0.5 FLOPs/Byte。这意味着NPU每算一次,就要搬2个Byte的数据。后来我把多个卷积层做算子融合,让中间结果留在片上,数据搬运量减少了60%,推理速度直接翻倍。
四个指标的关系——一张图看懂
下面这张图是我自己总结的,把四个指标串起来了。你仔细看,就能明白它们之间的制约关系。
你看,吞吐量和延迟是跷跷板,算力利用率和内存带宽是底层制约因素。我每次拿到一个新模型,第一件事就是跑这四个指标,看看哪个是短板。说白了,瓶颈永远只有一个,找到它,干掉它,再找下一个。
实战:如何快速定位瓶颈?
给你一个我常用的三步法:
| 步骤 | 操作 | 判断依据 |
|---|---|---|
| 1 | 跑一次推理,记录总耗时 | 如果延迟远高于预期,进入下一步 |
| 2 | 查看NPU利用率 | 利用率低 → 数据搬运或算子调度问题 利用率高 → 算力或带宽饱和 |
| 3 | 计算计算密度 | 密度低 → 带宽瓶颈 密度高 → 算力瓶颈 |
小技巧:如果你手头没有profiling工具,可以手动插桩——在推理前后加时间戳,分别统计数据搬运时间和计算时间。虽然粗糙,但够用。
嗯,今天就先讲到这里。这四个指标是NPU性能优化的基石,后面的所有技巧都是围绕它们展开的。你先把这张图刻在脑子里,下次调优的时候,就知道该往哪个方向使劲了。