NPU架构没那么玄:算力、带宽与数据复用三把钥匙
2026/9/7 9:05:46 网站建设 项目流程

开头先撂个观点:NPU芯片架构这事,看着全是黑话、框图、数据手册,其实真没那么玄。当年我是从嵌入式转过来看AI芯片的,第一次翻开NPU的spec直接被各种总线、缓存层级、指令流水线砸晕,直到后来自己上手算子移植和性能调优才发现,整个架构的骨架就一句话——NPU本质上是一堆MAC乘法器排成阵列,旁边配好存储和搬数据的通道,所有设计都在干一件事:别让这些乘法器闲着。

这句话不是比喻,而是我读过的每一款NPU、跑过的每一轮性能调优背后的共同规律。算力峰值、内存带宽、数据复用率,这三个词抓住,不管它叫NPU、TPU还是ISP智能加速单元,你都能在五分钟内把架构思路摸个八九不离十。这篇文章就沿着这个思路展开,把算力怎么算、带宽怎么卡、数据流怎么调度、落地时怎么排查问题都讲透,适合想做AI加速的嵌入式工程师、刚接触AI PC芯片的开发者,以及所有被“几十TOPS”营销话术搞晕的朋友。

1. 一句话框架:算力、带宽、利用率三个锚点

1.1 算力决定天花板,但天花板不等于实际性能

大多数厂商宣传NPU时最爱讲两个数字:TOPS(每秒万亿次操作)和频率。这个数字怎么来的?背后就是MAC阵列。

MAC是multiply-accumulate,乘加运算。一个MAC单元一个时钟周期做一次乘法和一次加法,这两个操作合起来算两次操作,所以单核算力的计算式是TOPS = MAC个数 × 频率(MHz) × 2 ÷ 10^6。举个例子,一颗NPU有1024个MAC,跑1.4GHz,单核就是1024 × 1.4 × 2 = 2867.2 GOPS,约2.87TOPS,八核堆上去就是23TOPS左右。MTL平台上的NPU大致就是这个量级。

但注意,这只是峰值算力,是理想条件下MAC阵列以百分之百利用率跑满时的数字。真实模型跑下来,MAC利用率能到六七成已经算优化得不错。原因是MAC单元本身不能独立工作,它必须有数据喂进来,有结果搬出去,还得有指令告诉它算什么、怎么算,任何一个环节卡住,算力就往下掉。

一句话理解:TOPS是发动机的排量,排量大的车不一定跑得快,还要看变速箱、路况和司机的水平。架构设计的目标就是让发动机尽量在红线转速附近工作,但现实路况永远有红绿灯。

1.2 带宽是命门,数据喂不饱算力再高也没用

第二个锚点是带宽。MAC阵列再快,如果数据从内存搬不过来的话,算力再高也只能空转。

这里有个经常被忽略的算术题。以INT8计算为例,一个3×3卷积的权重和输入特征图,想要让1024个MAC全部忙起来,每个周期至少需要搬入数百甚至上千字节的数据。假设NPU跑1.4GHz,集群每秒钟就要消耗几十GB到上百GB的数据流量。传统DDR4/LPDDR5的带宽一般也就几十GB/s,峰值高一点的LPDDR5x能到100GB/s以上,但完全不够喂饱一颗中高端NPU。所以几乎所有NPU都会配备大带宽的片上存储(SRAM),甚至云端AI芯片直接用HBM,片外带宽拉到几百GB/s到数TB/s。

这里的核心矛盾,圈内叫“存储墙”:计算能力按照摩尔定律提升,内存带宽却跟不上,导致很多芯片是“算得越快、饿得越狠”。去看看各种AI推理芯片的datasheet,凡是强调“高能效比”的,十有八九都在存储层次和带宽复用上下足了功夫。关于带宽的另一个热词是闪存芯片架构——虽然NPU不直接用闪存做计算内存,但端侧设备里的模型参数放在闪存里,模型加载、动态下载时闪存的顺序/随机读速度会直接影响首次推理延迟,这部分我在后面排查问题时会提到。

1.3 利用率才是真实战场:架构设计都在围绕“数据复用”

第三个锚点,也是大多数新人容易忽略的:MAC利用率。既然带宽搬不过来足够的数据,怎么让有限的带宽喂饱更多的MAC?答案就一个字:复用。

卷积神经网络有个天然优势:同一个输入特征图的数据会被多个卷积核反复使用,同一个权重也会被多个输入位置共用。NPU架构里的脉动阵列、多级缓存、DMA批量搬运,本质都是在把“每次计算都去内存取数”变成“尽量在片上把一份数据多算几遍”。业界有个概念叫算术强度(arithmetic intensity),单位是FLOP/Byte,代表每个字节的数据被读取后能用多少次。算法强度越高,对带宽的要求越低,能效比就越好。

所以评价一个NPU架构好不好,不能只看TOPS,还要看它那个“乘加单元旁边堆了多少缓存、数据流怎么复用”。很多架构看起来算力惊人,但输入输出设计不合理,实际跑起来利用率感人,这种“纸面算力”和“实测算力”的差距,才是论坛讨论里最常翻车的地方。

2. NPU架构核心细节:从MAC阵列到存储层次逐层拆解

2.1 MAC阵列与算力:TOPS是怎么算出来的

聊NPU,绕不开MAC阵列。一个MAC单元做的事情很简单:output = input × weight + bias,乘法和加法在一个时钟周期完成。为了方便布线,MAC单元通常以二维阵列排列:一行代表不同的输出通道,一列代表不同的输入通道,数据从一侧灌入,权重从另一侧广播,部分和像波浪一样在阵列中累加,最后从另一端输出。

这种结构有三种常见形态。第一种是脉动阵列,Google TPU是典型代表,MAC之间直接传递数据,上一步的输出就是下一步的输入,好处是极高数据的复用率、极低的寄存器访问压力,缺点是控制复杂,遇到不同形状的模型容易出现“队形散乱”。第二种是SIMD阵列,每个MAC独立接收指令、独立从寄存器取数,灵活度最高,适合各种形状规则的算子,但数据通路多、功耗容易上去。第三种是百变一点的近存计算或可重构阵列,把MAC和存储单元绑定成块,按算子动态配置连接关系。

这里插一个实用经验:选型或解读参数时,务必看清TOPS是FP16、INT8还是INT4算出来的。同样一颗芯片,INT8算力通常是FP16的两倍,INT4又能再翻一倍。很多玩AI PC的朋友兴奋地跟我说“这个NPU有40TOPS啊”,结果仔细一看,写的是“40TOPS INT4”,换算成能跑实际模型的FP16可能只有10TOPS,差得远。

顺带一提,算力的计算式里有个乘以2的细节,新手容易漏。一个MAC一次乘加在学术上算两个FLOPs(乘和加各算一次),所以无论报表还是论文,谈TOPS时都是按2倍算的,千万别按一个乘加等于一个操作去心算,不然会觉得自己手里的芯片弱了一倍。

2.2 片上存储与存储墙:为什么说瓶颈往往不在计算而在搬数据

NPU的存储层次,和CPU很像但又有明显差异。通常从上到下是:寄存器文件(RF)→ 片上SRAM / 缓存 → 片外内存(DDR/LPDDR/HBM)。区别在于,NPU对数据搬运的确定性要求更高,因为AI推理的访存轨迹是可以预判的,不需要像CPU那样靠分支预测和缓存淘汰来“猜”。

片上SRAM是NPU性能的关键。常见的做法是把一块大的SRAM池分成几个逻辑区:输入特征缓冲区、权重缓冲区、输出缓冲区,每块都是几百KB到几MB。卷积计算开始前,DMA根据指令先把数据从DDR搬到SRAM,MAC阵列读片上SRAM完成若干轮计算,再把结果从SRAM搬回DDR。这样片上带宽极高(几十TB/s),而片外带宽只需要覆盖“每轮复用之间的边界搬运”。

但存储墙问题依然存在。如果模型的中间特征图太大,片上的SRAM放不下,就得把特征图切成block,分块计算。分块切得太碎,每块都要从外面搬一次数据,带宽开销巨大;切得太整,又会超过片上内存容量,甚至造成bank冲突。很多NPU的调度器里,这个“分块策略”就是编译器优化的核心模块,直接决定MAC利用率。我实测过一个目标检测模型,同样的NPU硬件,用厂商推荐的分块参数跑出45%的利用率,手动把tile size调大、复用层次调深后,直接升到68%,推理时间几乎砍了三分之一。

这里插一句闪存芯片架构的影响。在端侧AI场景里,模型文件通常是存到闪存(UFS/eMMC)里,上电后一次性加载到DDR,再映射到NPU内存。如果闪存随机读性能差,模型加载阶段会出现明显卡顿,容易被误判成NPU推理慢。排查时先看是“加载慢”还是“推理慢”,这一步能省下不少时间。

2.3 数据流与控制流:指令级视角看调度

有了MAC阵列和存储,还要解决两个更上层的问题:数据以什么顺序流动?MAC阵列按什么节奏工作?这就涉及到数据流(dataflow)和控制流。

数据流风格大体有三种。权重静止(weight stationary):权重提前固化在MAC阵列旁边的寄存器里,输入特征图流经阵列,适合权重复用率高的全连接层或1x1卷积。输入静止(input stationary):输入特征图驻留,权重轮流灌入,适合输入数据量不大但权重较大的层。输出静止(output stationary):部分和在MAC阵列里累加完再一次性搬出,适合输出通道多、累加次数多的层。

控制流方面,NPU不是像CPU那样逐条取指令、译码执行,更多采用VLIW(超长指令字)或静态调度。编译器提前把数据搬移、MAC计算、输出回写排成一条流水线,用指令一次性告诉NPU“这批计算用哪块缓冲、算几轮、结果放哪”。好处是省掉了复杂的乱序执行硬件,功耗低;坏处是编译器必须非常智能,一旦遇到动态shape(比如NLP里的变长序列),调度就会变得非常麻烦。我试过把动态shape的Transformer模型直接塞进静态编译的NPU流水线,频繁重新编译,延迟高得离谱,最后只好改成padding成固定长度,才跑顺。

这里还有一个高频概念:稀疏化。既然很多神经网络的权重和中间激活值有大量0,那能不能跳过这些0,只计算非零值?这就是稀疏计算。不少NPU架构在MAC阵列前加一级稀疏检测,遇到0值直接跳过,省电又省带宽。但稀疏比例不固定,实际收益就看模型本身稀疏度,不能只看纸面支持。

2.4 AI加速器里的CPU、GPU、NPU分工

聊NPU架构,总是绕不开它和CPU、GPU的关系。CPU像全能管家,什么活都能干,但并行计算能力有限;GPU像一队训练有素的集装箱搬运工,擅长大量数据并行搬运,但单次任务的灵活性弱一点;NPU则是专门为神经网络计算的固定流水线“装配线”,把卷积、矩阵乘这些高频算子优化到极致。

实际部署时不是单靠NPU,而是三种计算单元配合。拿AI PC举例,系统里的NPU处理持续性的低功耗推理任务——比如摄像头背景虚化、语音降噪、本地AI助手——这些任务如果交给GPU,功耗会飙升,续航扛不住;CPU则在后台处理调度、预处理、逻辑分支。注意,并不是所有模型都适合NPU,有些小模型在CPU上跑更快,因为NPU有固定的数据搬移和初始化开销,模型太小的话跑不满启动成本。这个“谁跑哪里”的决策,在叫异构调度,以后会越来越重要。

3. 从数据手册看穿一颗NPU:可复用的实操方法

3.1 从规格书反推架构特征:手把手算TOPS和带宽需求

拿到一颗NPU,我习惯先算三笔账。

第一笔,算算力。找datasheet里的MAC数量和频率,按公式算力 = MAC数 × 频率 × 2心算一遍,再对着官方宣传的TOPS看是否吻合。如果不吻合,可能是标了不同精度,或者用了稀疏加速倍数,仔细找“前提条件”。

第二笔,算带宽需求。假设MAC利用率为100%,每个MAC每周期处理一个输入和一个权重,INT8各占1字节,那么每秒需要的数据量为MAC数 × 频率 × 2字节。比如1024个MAC、1.4GHz的情况下,就是1024 × 1.4e9 × 2 = 2.87TB/s。这个数字一出来你就明白:片外内存带宽不可能供得上,最理想情况下,MAC阵列能连续计算多少个周期,完全取决于片上SRAM缓存了多少数据。

第三笔,算术强度。用模型总FLOPs除以总数据搬运量(包括权重、输入、中间激活),得到一个比值。算术强度高于硬件阈值,算力就能跑满,否则就是带宽受限。我一般在调优前先算这一步,心里有底,避免盲目调参。拿看家的YOLOv8s模型为例,FP16版本的算力需求大约几十GFLOPs,如果NPU的算术强度阈值是100FLOP/Byte,那模型的数据搬移量至少在几百MB量级,对应的片上缓存分块和DMA调度就变得非常重要。

这三笔账算完,这颗NPU的“体质”基本就清楚了:它是偏计算型(堆MAC),还是偏带宽型(堆SRAM和HBM),还是两者兼顾。

3.2 判断MAC利用率:跑模型不能只看峰值

前几年买AI PC,厂商爱宣传“NPU算力XX TOPS”,但真正到用户手里,跑个stable diffusion或本地LLM还是卡。原因很简单,峰值算力和模型能吃到的那部分完全是两码事。

我常用的判断方法有三种。第一种是跑厂商自带的benchmark模型(比如MLPerf的测试样例),拿官方成绩做基线。如果自己的模型跑起来跟官方成绩差一个量级,先别怀疑芯片,大概率是模型适配问题。第二种是用oneDNN、oneDNN、ONNX Runtime或OpenVINO自带的profiling工具,统计NPU每个算子的耗时和利用率。很多工具可以直接显示NPU MAC单元的active stall cycles,这个指标就是看MAC有没有在反复空等数据。第三种,也是最笨但有效的:调完一轮后,把输入分辨率翻倍,看推理时间是否也跟着线性增长。如果只增长50%,说明算力还有余量,瓶颈可能在数据搬移;如果翻倍还多,可能缓存已经严重溢出,在不停往DDR倒腾数据。

分享一个实测案例。在Intel Core Ultra平台用OpenVINO部署一个姿态估计模型,默认配置下NPU利用率只有35%,时间全花在了等待DMA搬数据上。后来我把模型里的卷积层按通道数重组,增加了输入特征图的片上复用轮次,又打开算子融合(把BatchNorm折叠进卷积),利用率直接提升到61%,延迟降到原来的62%。整个过程没动一行CPU代码,全是NPU侧调度和内存分块的事。

3.3 异构平台上的NPU开发,以Intel NPU和OpenVINO为例

现在市面上的端侧NPU开发,绕不开三个主流路线:NVIDIA的TensorRT(针对GPU)、高通的SNPE/QNN(针对手机NPU)、Intel的OpenVINO(针对CPU+GPU+NPU的x86平台)。这里我重点讲Intel NPU,因为这些年在AI PC上做开发,OpenVINO很可能是你最容易碰到NPU的方式。

Intel NPU在Core Ultra(Meteor Lake和后续版本)上集成,算力不算夸张,但功耗极低,适合做持续在线的AI推理任务。开发流程一般分四步:

先装好OpenVINO runtime和NPU驱动,确认设备可用。然后在Python环境里写一个最简推理脚本,用openvino.Core读出CPU、GPU、NPU三个设备。示例代码如下:

import openvino as ov core = ov.Core() print(core.available_devices) # 例如: ['CPU', 'GPU.0', 'NPU'] # 将ONNX模型编译到NPU model = core.read_model("model.onnx") compiled_model = core.compile_model(model, "NPU") infer_request = compiled_model.create_infer_request() infer_request.infer(inputs) print(infer_request.get_output_tensor().data)

这是最简单的路径,但实际落地远没有这么轻松。我踩过最大的坑是模型里存在NPU不支持的算子,比如一些很新的激活函数或动态shape操作。编译时会直接报错,解决方法有两个:把不支持的算子拆分到CPU上跑,或者换一个算子表达方式。OpenVINO给了一个自动切分能力,把图拆成CPU+NPU两份,听起来很美好,但切分点会产生额外的数据拷贝,如果切得太多,性能反而比纯CPU差。我后来总结出一个经验:能重写成NPU支持算子的,最好重写;实在不行的,只把尾部后处理放CPU。

OpenVINO还提供了一个很重要的特性:模型缓存。同一个模型第一次编译到NPU可能要几秒甚至几十秒(因为编译器要做调度),但编译产物会被缓存下来,第二次加载是毫秒级。生产环境一定要开启缓存,不然每个进程启动都要白等一次。示例代码如下:

config = {"CACHE_DIR": "./cache"} compiled_model = core.compile_model(model, "NPU", config)

另外,NPU设备默认跑FP16,如果你的模型是用FP32训练的,精度差异轻微但不可忽略。敏感场景我建议在模型导出时做量化和精度对比,至少跑几十张测试图确认误差在可接受范围内。对这些细节的把握,决定了你是“能用上NPU”还是“把NPU用好”。

4. 常见问题与排查技巧:我在NPU落地中踩过的坑

4.1 存储墙问题排查:数据搬运卡死性能的典型表现

我第一次在NPU上做实时检测时,发现一个怪现象:模型本身不大,官方标称18TOPS算力,跑一个MobileNet应该十几个毫秒的事,实际delay却到了四十多毫秒。用profiler一看,MAC阵列的idle time占了大半。逐层分析后发现,问题不在计算,而是数据搬运:模型里有大量的逐元素操作(比如add、relu、concat),这些操作每次都要完整地把特征图从外部读一遍、算完写一遍。因为特征图尺寸远大于片上SRAM,频繁的DMA搬运把带宽吃满了,MAC阵列反而在空等。

这种问题的排查思路,我整理成了一张速查表:

现象可能原因常用解决手段
延迟随输入分辨率超线性增长片上缓存溢出,频繁数据搬移缩小分块、调整tile size、算子融合
MAC利用率低但内存带宽接近饱和数据复用不足,存在大量逐元素算子算子融合、重排内存布局、融合激活函数
CPU和NPU交替等待,整体卡顿缺乏异步调度使用多请求流水线,CPU在NPU计算时做预处理
模型加载慢,推理速度正常闪存读取或模型解压慢用int8量化减小模型体量、启用连续存储映射
单核跑得快,多核扩展效率差核间通信和共享带宽成为瓶颈减少核间同步、按通道维度切分任务、错开DMA

存储墙问题最大的陷阱,是它常常以“算力不够”的形式出现。所以我的配置里永远先开两列指标:MAC利用率和片外内存带宽。只要带宽顶到90%以上,就不要再盲目调大MAC阵列的并发度了,先优化数据复用。

4.2 多核扩展与同步开销:为什么4核不是4倍性能

很多NPU宣传“8核并行”,实际跑起来却到不了8倍,甚至4倍都难。原因是多核扩展涉及两个硬性成本:核间数据同步和共享带宽竞争。

卷积计算天然可以按输出通道或空间位置切成多份,分给不同核。但如果模型中间存在全局操作——比如全局平均池化、Softmax、全局注意力——所有核的结果必须汇总到一处,这就是同步点。同步点一多,快的核要等慢的核,性能就被“木桶效应”拖住了。另一个问题是核与核之间共享DDR带宽,前面算过,一颗核需要的带宽已经接近甚至超过片外内存的供应能力,多核并行时每个核实际分到的带宽反而更少,不及单核的独立运行带宽。

我的建议是:跨核并行优先按大颗粒度切分(比如按batch或大通道块切),少做细粒度切分。同时,尽量把同步点设计在带宽需求最低的位置,比如在DDR里只交换小尺寸特征图,而不是把整个输出在核间互灌。实测过一个分割模型,粗粒度切成4份后,扩展效率能到3.5倍左右,再往下切成8份,直接掉到2.1倍,得不偿失。

4.3 模型适配与量化技巧:把模型从训练框架挪进NPU

最后一个高频坑在模型适配环节。很多人拿着PyTorch训练好的模型,导出成ONNX就丢给NPU,结果各种报错或性能拉胯。这里有几个实操经验。

量化永远是最先考虑的优化手段。从FP32降到INT8,理论上算力翻倍、带宽减半、模型体积缩小到四分之一。但用了不合适的量化方案,精度会崩。经验是:先尝试per-tensor对称量化,如果精度掉太多再上per-channel和calibration数据集。calibration集不用多,选几百张有代表性的图就行,关键是覆盖模型实际部署时的输入分布。

算子融合是另一个低垂的果实。BatchNorm、ReLU、残差连接这些操作如果单独跑,每次都要多一轮数据搬运。好的NPU编译器(包括OpenVINO)会自动做融合,但自动化总有不完美的时候。手动把卷积+BN+ReLU写成一个算子,有时能再省10%-20%的时间。代价就是代码可读性变差,建议只在性能敏感的引擎里做。

最后,永远保留一个可复现的基准测试脚本。我每次调完一个优化点,都会把延迟、吞吐、MAC利用率、内存带宽四个数字记到表里。这样既能验证改动是否有效,也方便回滚。别嫌麻烦,调优NPU没有一个指标能覆盖全局,组合起来看才靠谱。

最后再聊点实际体会

如果只记住一件事,我希望能记住这句话:NPU的架构就是一场“不让计算阵列饿着”的马拉松。

三年前我刚接触NPU时,也以为它是什么黑科技,后来跑过的模型多了、调过的性能问题也多了,反而越来越觉得线性。算力、带宽、利用率,这三个词对应的其实就是硬件资源、数据通路和算法调度。再怎么花哨的架构,都逃不开这三个锚点。

如果你正要开始做NPU开发,我的建议是先从官方工具链里最小的demo跑通,别一上来就挑战大模型。跑通后打开profile工具,看一遍MAC利用率和内存带宽,你就知道这颗芯片大概“喜欢”什么样的模型结构。接着拿自己的模型做适配,遇到性能不对就先查这一层——到底是计算密度不够,还是数据没喂饱。等这个诊断流程变成肌肉记忆后,你会发现,无论是看架构图还是调性能,都轻松很多。

还有一个小技巧:调NPU之前先把模型用同款数据在CPU上跑一遍,记录一份延迟基线。只要NPU没比CPU快一个量级,就说明配置还有很大优化空间。这个基线,也是你和算法团队拉扯“到底该不该用NPU”时最有力的论据。用数据说话,永远比争论架构高低靠谱。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询