1. 10 TOPS/W这个数字,到底卡在谁的手里
先说句大实话:很多项目组把能效比挂在嘴上,但真能讲清楚TOPS/W是怎么算出来的人,少得可怜。我接过不少类似的调优需求,一上来就问"我这个模型跑起来怎么才3 TOPS/W,人家怎么到10的",结果打开Profiling一看,要么是测功耗的仪表压根没接对,要么是分母用了峰值功耗、分子用了理论算力,数字自然是虚的。
所以这篇文章想聊的,不是又一份"高能效AI"的愿景稿,而是把CANN能效分析这条线从头捋一遍:10 TOPS/W是怎么测出来的、怎么拆解的、怎么一步步逼近的。适合正在做边缘侧AI部署、NPU上算子适配、或者被功耗墙卡住性能的开发者看。
先给个参照系:当前主流平台跑常见视觉模型,FP16精度的推理能效普遍在2~6 TOPS/W之间,能摸到8以上的已经算很能打。10 TOPS/W放在专用AI芯片上是"优秀但可达"的目标,放在通用GPU上则基本不现实——这不是CANN能力问题,是硬件架构本身的能耗边界问题。换句话说,能不能到10,一半看芯片底子,另一半看软件栈能不能把每一焦耳花在刀刃上。
我习惯把能效分析分成四层来看:测量层、分解层、优化层、验证层。测量层解决"数字是不是真的",分解层解决"能耗到底花在哪",优化层解决"怎么压下来",验证层解决"压完之后长了什么样"。下面的内容基本按照这个逻辑展开,中间穿插一些我自己踩过的坑。
2. 能效测量是个精细活:三个口径能差出三倍
2.1 分子分母都有各自的坑
TOPS/W的分子是每秒万亿次操作,分母是瓦特。这两个看起来都很简单,但每个都埋着雷。
先说分子。芯片标称的TOPS是峰值算力,也就是所有计算单元满负荷跑、且指令流水完全不打折的情况下能达到的理论值。实际部署中模型很难把MAC阵列喂满,矩阵形状不对、数据布局不匹配、搬运跟不上都会让实际算力打折。你拿理论峰值去当分子,相当于用跑车说明书上的极速来统计通勤油耗——数字好看,但没用。
再比如TOPS这个单位本身也含糊。一次乘加算一次操作还是两次操作?不同厂商有不同算法,有的数乘法,有的把加法和乘法分开数,有的甚至把访存也算进去。横向对比平台时如果不看具体定义,会得出完全离谱的结论。
分母也一样。芯片的整卡功耗、核心功耗、算力单元功耗,三者的差值不是几个百分点,而是倍数级别。我见过某方案的宣传材料写着"能效比高达20 TOPS/W",底下小字注明"仅计算核心功耗",可问题是一颗芯片的存储系统、互连、控制逻辑、IO功耗加起来往往比计算核心本身还大。分母只要缩到计算单元那一小块,任何芯片都能"极致能效"。
2.2 我自己用的标准测量口径
这里分享一套实测下来比较靠谱的口径,CANN环境里做能效对比时我基本都用它:
- 功耗采集:直接从板卡供电端采样,测的是"整板功耗",包含内存颗粒、PCIe/接口、板级转换损耗,不做任何扣除。有些调试板卡带独立的功耗采样点,用万用表并联测电压、串联测电流,注意采样率要足够高。
- 温度稳定:芯片跑到温度平衡后再记录功耗,冷芯片和热芯片的漏电功耗能差不少,用冷机数据做对比会低估实际功耗。一般让设备空转5分钟后再压测10分钟,取最后3分钟的平均值。
- 模型工况:固定模型、固定输入尺寸、固定batch,连续跑多轮取稳态数据。
- 能效口径:端到端推理能效,即模型单次前向总有效操作数除以整板平均功耗乘以单次耗时得到的TOPS/W。前处理、后处理都算在耗时里,因为它们是真实存在的能耗开销。
用这套口径测出来的数字确实"难看",但对比可信、说服力强。行业里那些漂亮数字,九成以上不是这个测法。
2.3 一个参考对照表
为了说明口径差异有多大,我整理过一组在某一套AI芯片上跑ResNet风格模型的实测对照,这里列出来供参考:
| 测量口径 | 能效比结果 | 说明 |
|---|---|---|
| 理论峰值算力 ÷ 计算核心TDP | 18.6 TOPS/W | 宣传口径,几乎无实际意义 |
| 理论峰值算力 ÷ 整板典型功耗 | 12.4 TOPS/W | 前提是MAC阵列能跑满 |
| 实测有效TOPS ÷ 整板稳态功耗 | 5.3 TOPS/W | 部署真实模型的可复现数据 |
同样一块芯片,不同的口径能测出三个截然不同的结论。做能效分析第一步,先跟团队对齐"我们说的能效到底指哪种",否则后面所有优化动作都是在跟空气较劲。
3. 能耗账单怎么拆:四个去向,两个大头
3.1 算力芯片的能耗花在哪
有了可信的总功耗数字之后,就要回答那个最核心的问题:这些功率到底消耗在哪里?
拆到宏观层面,芯片能耗有四个去向:计算、访存、搬运、控制。计算是MAC阵列和向量单元做乘加时消耗的能量;访存是数据从DDR搬进片上SRAM、以及片上各级缓存之间的读写;搬运指数据在片上不同单元之间的移动(比如从SRAM搬到MAC阵列的输入缓冲);控制包括取指、译码、调度、同步等逻辑开销。
这四个去向里,很多人默认计算占比最高,这其实是最大的误解。现代AI芯片的计算单元的确是一个很大的功耗来源,但存储系统加数据搬运的合计占比往往超过计算本身。我做过的几次能耗分解结果大致如此:计算占30%到50%,访存占20%到40%,搬运占10%到20%,控制占5%到10%。这个比例会随模型结构浮动,但"访存相关的能耗不可忽视"这一点极少例外。
为什么访存这么贵?因为片外DDR的访问能耗比片上SRAM高一个数量级以上,差的甚至到两个数量级。举个直觉化的对比:读一次DDR消耗的能量,够片上做几十次乘加运算。这意味着哪怕一个算子只多读一遍中间结果,它的真实能耗也会显著上升;如果模型里到处是大的中间张量不停写回DDR再读回来,那能效数据一定不会好看。
3.2 Profiling数据如何对应到能耗
CANN这套工具链里的Profiling能给出算子级的时间占比、MAC利用率、DDR读写量、L2命中率等关键指标。很多人看了时间占比就跑去优化耗时最长的算子,这是另一个常见的错误操作——时间长的算子不一定能耗高,能耗的大头往往藏在"DDR读写量特别大"的算子身上。
我常用一个简单换算来辅助判断:把Profiling里读到的DDR读写字节数,乘以一个经验系数估算其对应的能耗权重,再对照算子时间占比。如果一个算子只占10%的时间但贡献了40%的DDR读写量,那么它才是真正的能耗大头。优化动作的优先级应该按照能耗占比排,而不是时间占比排。
3.3 MAC利用率与能效之间的关系
MAC利用率是Profiling里最常见的指标,代表MAC阵列实际被用起来的比例。它和能效之间呈正相关,但又不是简单的线性关系。利用率很低的时候,大部分能量耗在空转等待上,这时候提升利用率能带来接近线性的能效改善;利用率过了70%以后,再往上扣每一分都要付出很大精力,而能效收益却没有那么明显。
原因在于,高利用率阶段主要瓶颈从"算不过来"变成了"喂不饱",也就是数据搬运跟不上计算速度。此时想把利用率从80%拉到90%,需要大量优化访存路径,但省下的空转能耗有限。换句话说,提升MAC利用率的边际收益是递减的,单纯追高利用率不等于追高能效。
所以我在做能效优化时的优先级是这样的:先压DDR读写总字节数,再优化数据在片上的复用,最后才去抠MAC利用率。这个顺序跟大多数人的直觉相反,但实测下来收效更快。
4. 从图结构开始省电:融合、布局与片上复用
4.1 算子融合为什么省电
算子融合是CANN能效优化里性价比最高的手段,没有之一。它的核心逻辑很简单:把多个连续算子合并成一个算子,让中间数据留在片上SRAM里直接流转,不去DDR里走一趟。
举个例子,一个卷积后面跟ReLU激活,在未融合的场景下,卷积的输出张量会完整写到DDR,ReLU再把它读回来处理一遍,写一次读一次,中间张量越大浪费越多。融合之后,ReLU的计算直接嵌入到卷积输出通道上,数据根本不离开片上存储。
这个省的可不是一点半点。同样是那张10MB左右的中间特征图,写DDR再读DDR的能耗是片上处理的几十倍。一次融合省下的能耗,可能比优化几十个算子的流水调度还管用。
实际工程里优先级最高的融合对象是:卷积+批归一化+ReLU这类"算子在语义上天然串联"的组合,其次是池化+激活、全连接+激活这类elementwise链。现在CANN图优化层会自动处理一部分,但遇到结构不常见、带自定义算子的模型时,还是需要手工改写图结构来触发融合。
4.2 数据布局与格式的能耗影响
数据布局是另一个大项。NCHW、NHWC、以及各种分块多维格式,看起来只是张量维度的排列方式不同,但对MAC阵列喂数效率的影响是决定性的。
不同AI芯片内部对数据通道的排列有偏好。如果数据布局跟硬件MAC阵列的读取模式不匹配,就会出现通道方向无法对齐、片上数据碎片化等问题,表现为MAC利用率起不来、L2命中率偏低、搬运量超预期。而CANN里通过调整布局格式,往往能让同一个算子组的DDR读写量下降20%以上,能效因此获得明显改善。
我自己在适配一个图像检测模型时遇到过这种情况:基线版本的DDR读写在6.8GB/s左右,改成适配芯片通道特征的块布局后直接降到4.1GB/s,能效提升超过30%。布局这件事确实是"看不见摸不着但账单上很清楚"的变量。
4.3 tiling切分:让数据在片上尽量多待一会
tiling指的是把大的特征张量切成若干块,让每个块在片上SRAM的容量范围内完成尽可能多的计算步骤。切得好不好,直接决定数据搬运能耗。
tiling粒度太粗:块太大装不进片上存储,系统退化成反复搬运数据,DDR流量飙升;tiling粒度太细:块太小,片上存储大量空闲,计算单元频繁切换数据块导致流水线停顿。两者都会让能耗变差。
一个经验做法是:分析模型每个op的输入、输出和中间暂存体积,先保证"单块数据在片上跑完所有串联算子"为第一目标,然后再调整块的大小去适配多核并行负载。说白了,tiling的本质是找到那个"片上不溢出、核心不饿死"的平衡点。这一步CANN有自动tiling能力,但自动结果通常是通用方案,针对特定模型手工介入(调整tiling参数,重新编译)往往还能再榨出10%到20%的能效改善。
4.4 软流水与多核并行的能耗账
多核并行和软流水本身不直接省电,但它们能提升时间维度的利用率,从而让同样功耗下完成更多操作,间接改善能效。
软流水的做法是让数据搬运和计算重叠:当前一块数据在MAC阵列里计算时,另一块数据已经在搬运路径上往片上送了。理想状态下,DDR搬运的延迟被计算时间完全掩盖,MAC阵列不会空等数据。我在CANN环境里调多核并行时发现,软流水起不来的最常见原因不是调度器不给力,而是tiling切得太碎——小块数据在片上停留时间太短,搬运的启动开销占比上升,流水根本没机会进入稳定态。
调节奏时有个技巧:观察Profiling里的"计算等待数据"这类气泡,如果气泡集中在算子启动和结束阶段,说明需要增大块粒度;如果气泡均匀分布在计算中等距出现,说明搬运带宽本身不足,这时优先考虑压缩DDR读写量而不是调流水。
5. 用精度换能效要小心:量化的甜区与陷阱
5.1 位宽与能效的关系
量化是冲刺10 TOPS/W最直接的杠杆。原因很简单:从FP16切到INT8,MAC阵列的单次操作能耗下降、指令吞吐还可能翻倍,同时DDR搬运的数据量减半。双管齐下,能效比往往能直接翻倍以上。
但量化不是无脑全切就完事。我见过太多项目,一口气把整个模型压到INT8,结果精度崩了,回头重新调,周期拉得很长。正确的做法是把量化当作一个"精度预算分配"问题来处理,哪些层吃掉精度预算,哪些层能安全压缩,心里要有数。
一般来说,模型的尾部层(输出头、回归头)对精度最敏感,尽量保留FP16;结构的中间层要看数据分布,分布方差大的通道需要per-channel量化来兜住;而前几层卷积通常对量化容忍度较高,可以放心压。
5.2 校准数据的选择与混合精度
后训练量化依赖一组校准数据来确定各层的量化范围。校准集的规模和分布直接决定量化误差。
我踩过的坑是:为了省事只用了32张图片做校准,结果某个深层特征的分布长尾特别长,量化的截断误差被放大,最终精度掉了2个百分点。后来换了覆盖各个亮度区间、含多种目标尺度的200张校准图,同样配置下精度只掉了0.4个点。校准集宁多勿少,而且务必覆盖真实应用场景的数据分布。
另一个经验是混合精度。部分硬件支持在同一模型里混合使用INT8和FP16算子,CANN编译时能按算子的量化敏感度自动决策,也可以手工指定例外算子。我的策略是:先全部量化到INT8,跑一遍精度比对,找出掉点最集中的那几个算子,手工拉回FP16。这样既拿到了大部分能效收益,又把精度损失控制在可接受范围内。
6. 一次完整的10 TOPS/W冲刺复盘
6.1 基线数据:能效卡在5.2
用一套图像检测Demo为例说说完整流程。模型是一个类ResNet骨架加检测头,输入分辨率固定为1920x1080,单batch推理。初始状态跑了FP16精度,整板稳态功耗约7.6W,单次推理延迟13.8ms,实测有效操作量约0.55 TOPS,简单算一下:0.55 TOPS × 1000 ÷ 7.6W ≈ 5.2 TOPS/W。
Profiling里的信息很典型:MAC利用率46.3%,DDR读写量6.8GB/s,算子时间占比里卷积占大头,但按我前面说的能耗权重换算,DDR流量带来的访存能耗才是真正的冠军。问题诊断结论:这是一个"搬运受限但计算有余裕"的场景,优化空间主要在降低数据搬运量和提高计算利用率上。
6.2 三步优化:融合、布局、量化
整个优化过程我拆成三步,每步单独验证效果,方便定位哪项改动带来了什么收益。
第一步是算子融合。模型里卷积后面跟着ReLU、最大值池化后面跟激活层,这些组合全部改成融合算子形态。编译后重新测,DDR读写量从6.8GB/s降到4.6GB/s,能效从5.2提升到6.9。
第二步是数据布局调整。这里把主要算子的数据格式从通用排列切换成芯片侧更友好的分块多维排列,同时配合tiling参数手工微调。重测后DDR读写量进一步降到3.5GB/s,MAC利用率升到58%,能效来到8.4。
第三步是量化。全模型压到INT8后,首次精度测试掉点1.7个百分点,超了项目阈值。定位后发现是两个靠近检测头的小卷积层对量化太敏感,手工拉回FP16后精度只掉0.3个点,能效则继续冲到11.2。
三步做完,最终稳定在11.2 TOPS/W,功耗从7.6W降到6.4W,延迟从13.8ms降到7.2ms。整板口径下的真实数字,算是越过了10 TOPS/W这道线。
| 优化阶段 | 功耗 | 延迟 | MAC利用率 | DDR读写量 | 能效比 |
|---|---|---|---|---|---|
| 基线FP16 | 7.6W | 13.8ms | 46.3% | 6.8GB/s | 5.2 TOPS/W |
| 算子融合 | 7.2W | 11.5ms | 50.1% | 4.6GB/s | 6.9 TOPS/W |
| 布局+tiling | 6.9W | 9.8ms | 58.2% | 3.5GB/s | 8.4 TOPS/W |
| INT8量化 | 6.4W | 7.2ms | 64.5% | 2.1GB/s | 11.2 TOPS/W |
6.3 优化路上踩过的三个坑
这个案例里最值得记住的其实不是成功步骤,而是中间踩过的坑,这里展开说说。
第一个坑是融合算子的数值偏差。做完卷积+BN+ReLU融合之后,输出结果比非融合版本高了一点点,排查了好几个小时才找到原因:BN层的均值和方差是训练时固定的参数,但融合时我把BN参数并进卷积权重后,浮点运算的舍入顺序变了。深层网络里这种微小偏差被逐层累积,最后反映成输出差异。解决方法是先确认BN处于推理模式、参数冻结,再检查融合后权重计算的数值精度,必要时用双精度做一次参数合并。
第二个坑是量化校准集太小。前面提过的那次只用32张图校准导致精度掉1.7个点的教训,后来总结出一个经验法则:校准集的规模一般不要小于100张,且要覆盖应用中可能遇到的极端输入形态。如果条件允许,从真实测试数据里按最差分布的percentile去选校准图,比随机选更有效。
第三个坑是软流水起不来。我一开始把tiling切得很细,想着细粒度方便多核均衡,结果发现MAC阵列的空泡反而变多了。细看Profiling波形后才意识到,太小的块让搬运启动开销成了主导。后来把块粒度调大,让每次搬运的数据量更大、搬运次数更少,软流水才进入正常状态。
6.4 从单模型到多模型的能效普适性思考
这一轮优化跑通后,我把同样的方法应用于另外两个模型:语义分割模型和文本分类模型。语义分割模型收益接近,文本分类模型则没有这么明显——因为它计算密度高、访存需求低,瓶颈本来就在计算本身,融合和布局的收益空间有限,反而是量化带来的能效提升最显著。
这说明能效分析方法论是通用的,但优化优先级必须随模型特征调整。判断模型属于"访存受限"还是"计算受限",可以快速看两个指标:MAC利用率和DDR读写量的比值。计算受限的模型,DDR流量相对较低,优先做精度压缩;访存受限的模型,优先做融合、布局、tiling这些跟数据搬运强相关的优化。
7. 写在最后的实操体会
折腾完多个模型的能效优化之后,我最大的感受是:10 TOPS/W这个目标,七分靠硬件底子,三分靠软件栈把能耗账算清楚、优化动作做对位。CANN能效分析里真正值钱的能力,不是会把某个参数调大调小,而是能准确判断"这个模型当下被什么卡住"——是搬得太多、算得太慢,还是精度预算分配失衡。判断对了,优化动作立竿见影;判断错了,调一个星期可能都在原地打转。
最后分享一个压箱底的小技巧:每次跑完整Profiling后,把DDR读写总字节数单独拉出来看。这个数字和能效之间的相关性,远高于MAC利用率和能效之间的相关性。如果一个版本优化了半天,DDR读写量几乎没有下降,那这轮优化大概率是无效的;反过来,任何能让DDR流量明显下降的改动,即便当时看不出延迟变化,也值得保留。对我而言,这个单指标已经成为判断优化方向的快捷信号,省了不少走弯路的时间。