是什么:NPU(Neural Processing Unit,神经网络处理器)是专门为神经网络计算(以推理为主、部分兼顾训练)设计的领域专用处理器(Domain-Specific Architecture,DSA)。它不是一种固定电路,而是一类架构思想:用大量规则重复的乘累加(MAC)单元 + 紧贴计算的片上存储 + 极简的指令/控制,换取对神经网络负载远超通用处理器的能效。落地上主要有四种形态:手机 SoC 里的 NPU(华为麒麟 970 首次以"NPU"命名、苹果 A11 仿生的 Neural Engine、高通 Hexagon NPU)、自动驾驶域控(NVIDIA Xavier/Orin 内置推理引擎、特斯拉 FSD 芯片、地平线征程系列)、数据中心推理/训练卡(Google TPU、AWS Inferentia、寒武纪 MLU)、机器人与边缘端模组(NVIDIA Jetson、瑞芯微 RK3588 内置 6 TOPS NPU、各类 AI 视觉芯片)。
为什么 2017 年之后 NPU 爆发:四个条件在同一段时间凑齐 —— ①负载变了:2012 年 AlexNet 证明深度学习有效,2016 年 AlphaGo 出圈,2017 年 Transformer 问世,神经网络推理从实验室走向亿级设备,矩阵乘加(MAC)成为最主要的计算形态;②工艺红利变了:摩尔定律放缓、Dennard 缩放(晶体管变小时功耗同比缩小)在 2005 年前后失效,"堆晶体管不再自动省电",必须靠架构层面的专用化换能效 —— 这正是 2018 年 Hennessy 与 Patterson 图灵奖演讲《A New Golden Age for Computer Architecture》的核心论点;③通用芯片的账算不过来:在手机 mW~W 级功耗预算下,CPU/GPU 跑 CNN 的每瓦性能(OPS/W)差专用架构一两个数量级;④生态成熟:量化、编译器、模型压缩等技术让"把模型塞进专用加速器"成为工程上可复制的流水线。
怎么做(建立行业地图):把四种场景按"算力-功耗-延迟-成本"四个约束放进一张表,面试聊到任何一家公司的 NPU 都能对号入座:
| 落地形态 | 代表芯片/平台 | 典型算力量级 | 功耗预算 | 第一约束 | 架构取向 |
|---|---|---|---|---|---|
| 手机 SoC NPU | 麒麟 970/9000、A11~A17 Neural Engine、高通 Hexagon NPU | ~1~几十 TOPS(INT8) | 1~5 W(共享整机) | 能效(OPS/W)、面积成本 | 小阵列 + 权重常驻 + 强量化 |
| 自动驾驶域控 | NVIDIA Orin/Xavier、特斯拉 FSD、地平线征程 5 | 几十~几百 TOPS | 15~100 W | 时延确定性 + 功能安全 | 多核阵列 + 固定流水线融合 + 冗余 |
| 数据中心推理卡 | Google TPU(v1~v5)、AWS Inferentia、寒武纪 MLU | 上百~上千 TOPS | 75~500 W | 吞吐(OPS/$)+ 可伸缩 | 大脉动阵列 + HBM 高带宽 |
| 机器人/边缘模组 | Jetson Orin Nano、RK3588(6 TOPS NPU)、AI 视觉芯片 | 0.5~100 TOPS | 5~60 W(模组) | 功耗/成本/长时运行散热 | CPU+NPU 异构、量化优先、常驻权重 |
面试怎么问:①"为什么 2017 年前后各家芯片厂都开始做 NPU?"(答负载变化 + 摩尔定律失效 + 能效账 + 生态,四点);②"手机厂商为什么坚持自研 NPU 而不只用 GPU?"(能效、成本、与 ISP/相机算法的端到端定制、供应链自主);③"TOPS 越高性能一定越强吗?"(不一定,真实吞吐 = 峰值 × 利用率,利用率被带宽、tiling、算子适配限制)。
易错点:① NPU ≠ 只能推理,训练型加速器(TPU 训练、MLU)同样是 NPU 范畴,只是端侧 NPU 基本只做推理;② 把"NPU 爆发"归因于"AI 火了"只答对四分之一,架构层面必须说出 DSA/能效逻辑;③ TOPS 是 INT8 口径还是 FP16 口径要先问清,不同精度差 2~4 倍。
是什么:三者是同一颗晶体管预算的三种花法。CPU:少数几个大核,每个核把大量晶体管花在控制与预测上(乱序执行、分支预测、多级大缓存),目标是"把一个线程尽快跑完"——延迟优先。GPU:成千上万小核按 SIMT 成组跑同样代码,控制逻辑被大量线程摊薄,缓存较小,目标是"把海量线程的总吞吐拉满"——吞吐优先。NPU:干脆承认"负载是规则的数据流":用空间上排布的 MAC 阵列让数据在相邻单元间流动,控制退化为"装载-计算-存出"的固定节拍,目标是"每焦耳能量做出最多的乘加"——能效优先。
为什么:神经网络计算的三个特点决定了 NPU 的形态 —— ①控制简单:没有复杂分支和指针追逐,分支预测、乱序窗口都用不上;②并行规则:输出元素之间天然独立,大规模空间并行没有依赖冒险;③数据复用高:同一个权重要对成千上万个位置重复使用,只要把它"钉"在计算单元旁边,就能把访存摊薄到接近于零。CPU/GPU 的晶体管大量花在与这三条无关的地方,能效自然吃亏。
怎么做(画图记忆):记住下面这张结构对比图,再配大表 —— 三块芯片内部"哪里晶体管最多"就是它们各自的哲学。
再配一张逐项对比大表,面试前背熟"架构取向"与"关键指标"两行即可:
| 对比项 | CPU | GPU | NPU |
|---|---|---|---|
| 核心组织 | 少量大核(4~64) | 数千~上万小核,SM/SIMT 分组 | MAC 阵列(脉动/空间)+ 固定功能算子 |
| 执行方式 | 乱序执行,逐指令控制流 | SIMT:单指令多线程,时间复用 | 数据流:指令只管装载/触发,计算是空间并行的节拍 |
| 晶体管大头 | 控制逻辑 + 多级大缓存 | ALU 与寄存器,控制被摊薄 | MAC 单元 + 片上 SRAM/累加器 |
| 缓存策略 | 硬件 cache,透明自动 | cache + 共享存储(shared memory) | scratchpad/累加器,软件显式管理 |
| 访存模式 | 不规则、随机访问多 | 规则但海量,靠高带宽 GDDR/HBM | 批量 DMA + 阵列内复用,访存次数被压到最少 |
| 优化目标 | 单线程延迟 | 总吞吐(FLOPS) | 能效(OPS/W)与单位面积吞吐 |
| 典型精度 | FP64/FP32 | FP32/FP16/TF32 | INT8/INT4/二值为主 |
| 灵活性 | 任意代码,最强 | 任意并行代码,强 | 限定的算子集,动态控制流差 |
| 代表 | ARM Cortex-A、x86 | NVIDIA GPU、AMD GPU | TPU、NVDLA、Gemmini、麒麟 NPU |
| 一句话哲学 | "快跑完这一件事" | "同时跑完一万件事" | "用最少焦耳跑完规定好的事" |
面试怎么问:①"同样是并行,GPU 和 NPU 的并行有什么本质区别?"(GPU 是时间复用:同一个 ALU 在万千线程间轮转;NPU 是空间复用:每个数据走位固定在物理上排开的计算单元里);②"1 TOPS 的 NPU 和 1 TOPS 的 GPU 跑 CNN 差别在哪?"(利用率与每次计算的能耗:NPU 的 MAC 直接从寄存器取驻留数据,GPU 每次乘加都要经过寄存器文件/共享存储的读写);③"什么任务 NPU 反而不如 CPU?"(动态控制流、稀疏不规则访存、算子未覆盖时回退 CPU,常见于 beam search、自定义算子)。
易错点:① "NPU = 很多核的 CPU"是错的,NPU 没有"取指-译码-执行"逐指令模型,指令只调度数据搬运与矩阵运算的触发;② "GPU 有 CUDA 做神经网络,NPU 是多余的"忽略了端侧功耗墙 —— 手机/机器人给 AI 的预算常在瓦级以下,GPU 落地不了;③ 把 cache 和 scratchpad 混为一谈:cache 硬件自动管理、行为不确定;scratchpad 由编译器静态分配、延迟确定,这正是 NPU 可预测性的来源之一。
是什么:剥掉"层"的外衣,神经网络推理 90% 以上的计算量是同一种运算:通用矩阵乘 GEMM(GEneral Matrix Multiplication)。全连接层本身就是向量-矩阵乘;卷积层则通过 im2col(image-to-column)把每个卷积窗口展开成矩阵的一列(或一行),卷积就变成了矩阵乘。MAC(multiply-accumulate,乘累加)是这一切的最小硬件单元:一次乘法 + 一次加法,输出部分和(partial sum,psum)。
卷积输出尺寸与计算量的通用公式(务必默写):
为什么:①GEMM 的循环结构规整(NESTED-FOR 三重循环),没有数据依赖分支,天然适合硬件并行 —— M×N 个输出互相独立,可以同时算;K 方向是累加链,可以流水;②把所有层都统一到 GEMM 后,硬件只需要做好"矩阵乘引擎"这一件事,编译器负责把各种算子规约成 GEMM,软硬件各自最简。
怎么做(im2col 手推一遍):以 3×3 卷积、输入 4×4、单通道为例:输出位置共 (4−3+1)² = 4 个;把每个 3×3 窗口拉直成 9 维列向量 → 得到 9×4 的输入矩阵 A;把 3×3 卷积核拉直成 9 维向量 → 权重矩阵 B 为 9×1;一次 9×4 乘 4×1(按 A[9×4]×B[4×1] 的布局即 4 输出 × K=9)等价于原卷积。代价是输入被复制了 K×K 倍 —— 空间换时间,这也是为什么 NPU 必须配大片上 SRAM 来装这些复制品。
| 算子 | 规约成 GEMM 的方式 | GEMM 形状(M×K×N) | 备注 |
|---|---|---|---|
| 全连接 FC | 直接就是矩阵乘 | batch×(in特征)×(out特征) | batch=1 时退化为向量×矩阵 |
| 标准卷积 Conv | im2col 展开窗口 | (输出位置数)×(K·K·Cin)×(Cout) | 内存膨胀 K·K 倍,片上完成才划算 |
| 1×1 卷积 | 无需 im2col | (H·W)×Cin×Cout | 天生 GEMM,逐通道混排的"矩阵乘" |
| 深度可分离卷积 | 逐通道独立累加 | GEMM 退化为一组独立的点积 | 数据复用差,阵列利用率低,是 NPU 的"差生" |
| 池化 / 激活 | 不是 MAC | — | 用加法器树/比较器/查表,单独的数据通路(SDP 类模块) |
面试怎么问:①"卷积为什么能转 GEMM?转的代价是什么?"(窗口规整、参数共享;代价是 im2col 内存膨胀 K² 倍与搬运开销,所以要 tiling 在片上完成);②"一个 7×7 卷积、输入输出 256 通道、输出 56×56,多少 MAC?"(56×56×256×(7×7×256) ≈ 10.1 亿 MAC,当堂心算);③"为什么很多 NPU 对 depthwise 卷积特别慢?"(没有跨通道的 K 维共享,阵列只能一列干活,利用率掉一个数量级)。
易错点:① MAC 数与 FLOPs 差 2 倍(1 MAC = 1 乘 1 加 = 2 FLOPs),报表时要先统一口径;② 忘了 bias 与 padding 的影响(bias 是加法不进 MAC 统计,但 padding 改变输出尺寸);③ "输出位置数 × 输出通道数 × 每窗口 K²Cin"这个三因子乘法,最常见错误是把输出位置算成输入尺寸 H×W。
是什么:脉动阵列(Systolic Array)由 CMU 的孔祥重(H.T. Kung)与 Leiserson 于 1978 年提出。"systole"本意是心脏收缩泵血 —— 数据像血液一样有节律地在相邻计算单元(PE)之间流动:每个 PE 只与上下左右邻居相连,每个周期从上游收一个数、用驻留在本地的权重乘一下、把部分和往下游推一格。没有总线、没有广播,数据一旦从存储读入阵列,就沿着固定方向"脉动"前进,沿途被逐个 PE 复用。Google TPU v1 的 MXU(256×256 INT8 脉动阵列)是它最著名的工业级落地。
为什么(省访存的账):访存是能耗与带宽的双重瓶颈 —— 在 45nm 量级下,从 DDR 读 32bit 的能耗(~640pJ)是从寄存器取数的成百上千倍,更远超一次 INT8 乘加本身。朴素做法算 C=A×B(M×N×K)要把 B 读 N 次以上;脉动阵列把权重(或部分和)驻留在 PE 里,让激活流过阵列:每个权重只需从存储装载一次,就在 K 维累加的过程中被复用 N(或 M)次;同理每个激活数在流过一列 PE 时被逐行复用。访存量从 O(M·N·K) 级被压到 O(K·N + M·K) 级,这就是"一次装载,多次复用"。
怎么做(看懂数据流):下图是权重固定(weight-stationary)风格的 4×4 阵列:权重 w0~w3 先沿列装载并驻留在各 PE(蓝色,不动);激活 a0~a3 每拍从左向右脉动推进一行(橙色);部分和 psum 沿列向下逐拍累加(绿色)。第 t 拍时,PE(i,j) 计算的是 psum ← psum + w(i,j) × a(过路激活) —— 注意没有任何一根导线跨越整个阵列,数据只走一步,这正是走线短、可扩展、频率高的原因。
三种数据流(dataflow)对比:"数据流"回答的问题是 —— 在一次乘累加的三类数据(权重 W、激活 A、部分和 psum)中,让谁驻留(stay)在 PE 里不动,让谁流动。驻留对象的能耗最省,因为它从存储读一次就被复用;流动对象要承担传递能耗。三种经典选择:
| 数据流 | 驻留者 | 流动者 | 代表硬件 | 优势 | 代价 / 适用 |
|---|---|---|---|---|---|
| Weight-Stationary(WS,权重固定) | 权重 | 激活入、psum 出 | Google TPU v1 MXU、Gemmini(可选) | 权重复用最大化,大 batch/大 feature map 效率高 | 换层/换 tile 要重载权重;batch 小且层多时权重装载开销占比大 |
| Output-Stationary(OS,输出固定) | 部分和 | 权重、激活流动 | Gemmini(可选)、多数 OS 阵列 | psum 不用搬动,K 维累加零搬运,长累加链友好 | 权重/激活复用靠重复发送;多通道累加完成后才写回 |
| Row-Stationary(RS,行固定) | 一行权重 / 一行激活 / psum 各驻留于不同 PE | 对角/边缘数据流动 | MIT Eyeriss(168 PE + 多播 NoC) | 三类数据同时最大化片上复用,端侧能耗冠军级 | 映射与编译复杂,NoC 控制复杂,通用性一般 |
面试怎么问:①"TPU 为什么用脉动阵列?"(答三点:一次取数多次复用省带宽省能耗、只连邻居走线短易扩规模、节拍规整设计简单);②"weight-stationary 什么时候吃亏?"(batch 小、层薄、逐层推理时,权重重载频次高,权重装载打满带宽 —— 这也是端侧 NPU 常配"权重常驻 SRAM + 多层融合"的原因);③"row-stationary 好在哪又为什么没成为主流?"(能耗最优但编译器与 NoC 复杂度极高,工程上 WS+大 SRAM 更简单可复制)。
易错点:① 脉动阵列是"邻居间点对点传递",不是总线广播 —— 画图时箭头必须是逐格推进;② 驻留不等于永远不动:WS 阵列在 tile 间仍要重载权重,估算周期必须把装载时间摊进去;③ 脉动阵列不只算矩阵乘,卷积(im2col 或原生窗口映射)同样是它的主业;④ "数据流动 = 数据在多个 PE 里算多遍"是误解,每个数沿一条路径流过,每个 PE 只用经过它的那一份。
是什么:业界 NPU/加速器的算力引擎大体分三派 —— ①脉动/空间阵列:TPU MXU、Berkeley Gemmini、MIT Eyeriss,计算在空间上铺开,数据就近流动;②向量 SIMD/SIMT 扩展:不给神经网络专门造大阵列,而是在处理器里加向量单元(ARM NEON/SVE、RISC-V Vector、高通 Hexagon 的 HVX),靠软件循环驱动向量乘累加;③可重构固定功能流水线:把 CNN 各算子做成流水线级(卷积核 → SDP 激活 → PDP 池化 → CDP 归一化),逐级串联,配置参数决定每级做什么,NVIDIA NVDLA 是开源代表。注意:Eyeriss 虽与脉动阵列同属"空间阵列"家族,但用多播 NoC 供数、RS 数据流,是"空间阵列 ≠ 经典脉动"的最好例子。
为什么(取舍):本质是能效、灵活性、工程复杂度三选二的博弈:固定流水线能效最高(算子直通、无指令开销)但模型一换代就要改 RTL;SIMD 最灵活(就是软件)但每次乘加都要从向量寄存器/缓存取数,能耗与带宽吃亏;脉动阵列在中间 —— 用规整的 GEMM 假设换高能效,保留对不同网络形状的可编程性。
| 维度 | 脉动/空间阵列(TPU / Gemmini) | 向量 SIMD(ARM NEON / Hexagon HVX) | 可重构流水线(NVDLA) |
|---|---|---|---|
| 并行度来源 | 空间:PE 网格同时计算 | 时间:向量寄存器一拍多 lane,循环驱动 | 空间:算子级流水线各段并行 |
| 数据复用 | 极强(驻留 + 邻居传递) | 弱~中(寄存器级,靠缓存) | 强(层内专用 buffer + 融合模式) |
| 灵活性 | 中:限 GEMM 类负载 | 最强:任意可向量化代码 | 弱:算子集固定,新算子难支持 |
| 能效 | 高 | 中(比 GPU 好一截,逊于阵列) | 最高(目标算子内无指令开销) |
| 指令开销 | 一条指令驱动一大块 tile | 每拍都在取指译码 | 几乎无指令,寄存器配置一次(APB 配置,呼应 08-05) |
| 编译器难度 | 高(tiling + 数据流调度) | 低(编译器自动向量化) | 高(层图 → 流水线配置 + 融合决策) |
| 典型落地 | TPU、Gemmini、寒武纪 | 手机 CPU/DSP 上的 NN 加速、预处理 | Xavier/Orin 里的 DLA、监控类视觉芯片 |
| 一句话 | "把 GEMM 做成一座城的管道网" | "给通用 CPU 装上算术宽体机" | "为 CNN 造一条专用流水线" |
面试怎么问:①"低功耗端侧摄像头芯片,你选哪种结构?"(先问算子集合是否封闭:封闭选固定流水线/NVDLA 风格,开放选脉动阵列 + SIMD 兜底,并给出能效估计);②"NVDLA 里也有 MAC 阵列,它为什么算固定流水线而不是脉动阵列?"(其 64×32 MAC 阵列服务于卷积专用数据通路,层间靠 SDP/PDP/CDP 流水串联、寄存器配置,而非通用 GEMM 指令驱动);③"Eyeriss 和 TPU 都是空间阵列,差异在哪?"(NoC 多播 vs 邻居直连、RS vs WS、能耗最优 vs 工程简单)。
易错点:① 把"NVDLA = 没有阵列"说错 —— 它有 2048 个 INT8 MAC(可配 1024 个 16bit),只是组织方式是卷积专用通路;② 认为 SIMD"没做优化" —— NEON/HVX 的寄存器复用同样是一种 stationary,只是粒度小;③ 忽视混合趋势:现代 NPU 常是"脉动阵列 + 固定功能激活/池化单元"的混合体,纯血脉动或纯血流水线都不常见。
是什么:NPU 的存储层级从内到外四层:PE 内寄存器/权重锁存器(容量最小,几十 B~KB 级,零延迟,能耗最低)、片上 SRAM / scratchpad / 累加器(百 KB~几十 MB,TPU v1 配了 24MiB 统一缓冲,数十拍延迟)、NoC/片上互连(把数据送到阵列各处,按跳数计能耗)、DDR/HBM 片外存储(GB 级容量,但延迟百 ns 级、每次访问能耗高两三个数量级)。金字塔的规律铁律一般:越往上(靠近计算)越快、越省能、越贵、越小;越往下容量越大但每字节的延迟和能耗都急剧变差。
为什么:神经网络的算子有极强的数据复用潜力(权重被每个输出位置复用、每个激活被感受野重叠的窗口复用、psum 要累加 K 次),而 DRAM 每字节能耗是寄存器的成百上千倍 —— 所以 NPU 设计的第一性原理是:能不碰 DRAM 就不碰 DRAM,能不碰 SRAM 就不碰 SRAM。金字塔不是"装饰",而是"能耗预算表"。
怎么做(数据复用策略):把"少访存"拆成三种可操作的复用 —— ①层内复用(intra-layer):tiling 分块后,权重块装载一次遍历所有输入块,激活块重叠窗口共享;②层间复用(inter-layer / fusion 融合):上一层输出直接作为下一层输入留在片上,不写回 DDR(NVDLA 的融合模式、常见"卷积+激活+池化"三合一);③权重常驻(weight resident):小模型整网权重住进 SRAM,推理期间零权重搬运(端侧 NPU 的杀手锏)。三种策略对应存储层级的三个"高度",编译器的工作就是把调度尽量抬到金字塔上层。
是什么:算术强度(Arithmetic Intensity, AI)= 总运算量 / 总访存量(单位 OP/Byte),衡量"每搬运一字节能做多少运算"。Roofline 模型一句话:可达性能 = min(峰值算力, 算术强度 × 存储带宽) —— 画在图上就是一条先斜后平的"屋顶线",转折点(脊点)在 AI* = 峰值算力 / 带宽。
为什么:它是 NPU 项目立项时第一个要算的公式:如果目标算子群的 AI 低于脊点,堆 MAC 数毫无意义 —— 性能被带宽锁死;反之才值得加阵列规模。例如 8 TOPS(INT8)芯片配 0.5 TB/s 带宽,脊点 AI* = 8/0.5 = 16 OP/B;而一个 batch=1 的 FC 层(权重为主访存)AI 常只有 2~4,这种层在图上落在带宽受限区,实际只能跑到 1~2 TOPS。
面试怎么问:①"NPU 为什么用 scratchpad 而不用 CPU 式 cache?"(确定性:编译器静态调度 DMA 与计算节拍,不需要 cache 一致性硬件,面积功耗更低、延迟可预测,代价是软件复杂);②"给 4 TOPS 算力和 0.2TB/s,能跑到 4 TOPS 吗?"(先算脊点 AI*=20,再看目标网络逐层 AI,多数层到不了 → 不能,答出"逐层算 AI"即是亮点);③"权重常驻为什么对端侧特别重要?"(端侧带宽最弱、模型小,权重能整网住进 SRAM 时 DRAM 流量只剩激活,能效翻倍级提升)。
易错点:① "TOPS 高 = 快"不看带宽 —— 卖点是 TOPS,命门是带宽,面试一定要把 Roofline 画出来;② AI 的分母是"真实搬运的字节数"(含 padding、中间结果写回),不是理论权重大小,tiling 差的实际 AI 远低于手算值;③ 层间融合有时反而不划算(要大 buffer 兜住激活),融合决策也要用容量算账,不是无脑合。
是什么:NPU 的指令集和 CPU 完全不同 —— 粒度大得多。主流设计把指令压成四类:配置(config):设定数据流模式、量化参数、阵列形状等"一个批次不变一次"的状态;加载(load/mvin):DMA 把权重/激活 tile 从 SRAM/DDR 搬进阵列或累加器;计算(compute/matmul):触发脉动阵列做一块矩阵乘;存储(store/mvout):把累加结果搬出、做量化并写回。Gemmini 的真实指令就是这四类的教科书实现(config_ex / config_mvin / mvin / matmul.preload / matmul.compute / mvout / loop);而 NVDLA 走另一极:没有显式指令,靠 APB/AHB 总线写几百个配置寄存器来"编程"固定功能流水线(正好呼应 08-05 页的总线知识)。
为什么(两种风格):CISC 风格 layer 指令(TPU v1、Gemmini 的 loop 指令):一条 gemmini_loop_conv_ws 把"多层循环 + tiling + 权重重载"全部交给硬件状态机,指令 cache 压力小、总线占用少,但灵活性交给了硬件复杂度;RISC 风格细粒度指令:软件编译器展开循环、逐条发 mvin/matmul,调度完全由编译器掌控,容易做双缓冲、流水、多 tile 并行,但指令吞吐成为设计点。选哪种,本质是"把循环控制器放硬件还是放软件"。
怎么做(写出 C1 层的指令序列):下面用 Gemmini 风格伪码演示"配置→加载→计算→存储"如何跑 LeNet C1(576×25×6 的 GEMM,详见下一节推导):
// ============ LeNet C1: 输入 28×28, 6 个 5×5 卷积核, 输出 6@24×24 ============
// im2col 后 GEMM: A[784×25](激活) × B[25×6](权重) → C[784×6](输出)
// 8×8 PE 阵列 → M 方向切 98 块, K 方向切 4 块(8+8+8+1), N 方向 1 块(6 列用 8)
// —— ① 配置类:一次推理只需执行一次 ——
config_ex( MODE_WS, ACC_32BIT ) // 数据流 = weight-stationary, 累加器 32bit
config_mvin( SCALE, SRAM_BASE ) // 搬运配置: 权重来自片上 SRAM, 定点缩放
// —— ② 加载类:6 个卷积核拉直成 B[25×6],K 方向分 4 个 tile 装载驻留 ——
mvin( W_tile0, B_0 ) // 权重 tile0(8 行)装入, 驻留 PE 阵列
mvin( W_tile1, B_1 ) // ... 共 4 拍 × 6 列
mvin( W_tile2, B_2 )
mvin( W_tile3, B_3 )
// —— ③ 计算类:对 98 个 M 方向块循环(im2col 已在片上完成) ——
for m in 0..97:
matmul.preload( C_addr[m], D_flag ) // 预置本块输出地址(写入累加器)
matmul.compute( A_tile[m], K_0 ) // 激活块流入, 与驻留权重乘累加
matmul.compute( A_tile[m], K_1 ) // K 方向 4 块, 部分和在累加器接续累加
matmul.compute( A_tile[m], K_2 )
matmul.compute( A_tile[m], K_3 )
// —— ④ 存储类:累加器 → 量化 INT8 → 写回 ——
mvout( C1_OUT, ACC_SCALE_RELU ) // 搬出时做 32bit→INT8 量化 + ReLU
// CISC 风格(Gemmini loop 指令)可把 ③④ 整段压成:
// gemmini_loop_conv_ws( A, W, C, 24, 24, 6, 5, ... ) // 循环控制交给硬件
面试怎么问:①"NPU 指令为什么一条顶 CPU 几万条?"(粒度:一条 matmul 指令调度一整个 tile 的空间并行计算,循环与控制被压进硬件或编译器);②"双缓冲(ping-pong)在指令层面怎么体现?"(加载下一块与计算当前块用不同 buffer 并行发指令,靠显式同步/屏障指令或 FIFO 深度约束对齐);③"为什么 NVDLA 不用指令?"(它是协处理器形态,配置即编程:CPU 通过总线写任务描述符与寄存器,代价是任务切换粒度粗、依赖 CPU)。
易错点:① 忘了配置类指令的"批次不变"性质,把量化参数每层重配浪费周期;② 加载/计算没做流水(DMA 等计算完才动)会让阵列一半时间空转;③ CISC loop 指令不是万能 —— 层间动态分支(如早退网络)硬件循环管不了,仍需软件拆开。
是什么:现在把第 08-06 页跑过的 LeNet(输入 28×28,补边到 32×32 口径的经典 C1 输出 28×28,此处按 28×28 输入 + padding=2)逐层映射到 8×8 = 64 个 INT8 MAC 的脉动阵列(就是 08-10/08-11 页要写的 RTL Spec)。方法固定三步:① im2col 变 GEMM → ② tiling 分块适配 8×8(M/N/K 三个方向切块,K 块间部分和累加)→ ③ 排指令序(权重装载 → 激活流入 → 累加器搬出)。
C1 层完整走位(拿面试讲 3 分钟的那段):C1 = 6 个 5×5 核作用于 28×28 输入,padding=2、stride=1(等价于经典 LeNet-5 把 MNIST 贴进 32×32 画布)→ 输出 6@28×28,与原论文逐层链 C1(28)→S2(14)→C3(10)→S4(5)→C5(1) 完全一致。im2col 后:A 矩阵 784×25(784=28×28 个输出位置,25=5×5 窗口),B 矩阵 25×6(6 个核各拉直成一列),输出 C 784×6。映射到 8×8 阵列:M 方向784 切成 98 个 8 行块;N 方向6 列直接放入 8 列阵列(余 2 列空闲,利用率 6/8);K 方向25 切成 4 块(8+8+8+1,最后一块补零到 8)。执行时:4 个权重 tile 依次 mvin 驻留 → 逐个激活块(8×8)从左侧流入 → 部分和在累加器按 K 块累加 → 全部 K 块算完 mvout。理想周期 = 117,600 MAC ÷ 64 MAC/拍 ≈ 1,838 拍。
| 层 | 算子 | 输出尺寸 | 权重数(含 bias) | MAC 数 | GEMM 形状 M×K×N | 映射方式(8×8 阵列) | 理想周期(÷64) |
|---|---|---|---|---|---|---|---|
| C1 | Conv 5×5, s=1, p=2 | 6@28×28 | 156 | 117,600 | 784×25×6 | M 切 98 块,K 切 4 块,WS 权重驻留 | 1,838 |
| S2 | AvgPool 2×2, s=2 | 6@14×14 | 0 | 0(MAC) | — | 加法树/移位,非 MAC,单独数据通路 | ~(小) |
| C3 | Conv 5×5, s=1(6→16 简化全连接) | 16@10×10 | 2,416 | 240,000 | 100×150×16 | K=150 切 19 块,N=16 恰好 2 个列块 | 3,750 |
| S4 | AvgPool 2×2, s=2 | 16@5×5 | 0 | 0(MAC) | — | 同 S2 | ~(小) |
| C5 | Conv 5×5 ≡ FC(16→120) | 120@1×1 | 48,120 | 48,000 | 1×400×120 | M=1:N 切 15 块,权重按 K=400 分 50 块驻留 | 750 |
| F6 | FC 120→84 | 84 | 10,164 | 10,080 | 1×120×84 | M=1,N 切 11 块,K 切 15 块 | 158 |
| OUT | FC 84→10 | 10 | 850 | 840 | 1×84×10 | 利用率最低(仅 10 列工作) | 14 |
| 合计 | — | — | ≈ 61,706 | ≈ 416,520 | — | — | ≈ 6,510 + 池化/装载开销 |
怎么做(周期账怎么算才像内行):三步走 —— ①逐层 MAC 数 ÷ 阵列规模(64)得理想拍数;②乘上利用率折扣:tiling 边角(N=6 占 8 列、M=1 的 FC 层只有 1 行工作)使实际利用率常在 60%~85%,全阵列平均取 ~0.75;③加上权重装载与池化开销(WS 数据流每个 M 块重用驻留权重,装载可被双缓冲隐藏;池化层走单独数据通路约几百拍)。最终量级:约 6K~9K 拍跑完一张 28×28 手写数字 —— 200 MHz 下约 30~45 µs,单帧千帧级吞吐,证明 8×8 阵列对这个模型绰绰有余;瓶颈不在算力而在"逐层搬运与同步"。这个结论直接写进 08-10 页 Spec:8×8 阵列 + 4KB 权重 SRAM + 8KB 激活 SRAM + 32bit 累加器,资源绰绰有余。
面试怎么问:①"让你把一个卷积层映射到脉动阵列,流程?"(im2col → GEMM → 三向 tiling → 数据流选型 → 指令序 + 双缓冲 → 周期与带宽复核,Roofline 检查是否带宽受限);②"你的 8×8 阵列跑 FC 层为什么利用率暴跌?"(M=1 时阵列只有一行工作 → 解法:多 batch 打包、或 FC 改写成 1×1 卷积跨样本批处理);③"权重 61.7KB 放不进 4KB SRAM 怎么办?"(按 K/N tile 分时驻留,把权重装载流水进计算,用双缓冲隐藏 DMA)。
易错点:① 池化不是 MAC,别把它塞进阵列周期;② 周期估算只除 64 不打折 —— 边角块(N 余 2 列、K 补零、M=1 的 FC)会让纸面数字过于乐观;③ im2col 在片上做也占周期与 SRAM(复制 K² 倍),估算时别当免费;④ 忘记输出写回与层间同步的拍数,端到端时间会比粗估长 10%~30%。
是什么:神经网络训练后大量权重接近零,可以"剪枝"(pruning)后置零。非结构化剪枝逐个置零任意位置的权重,压缩率高但对硬件极不规律;结构化剪枝按 channel/filter/块整块剪,张量直接变小、硬件天然受益。两者的硬件友好折中是 NVIDIA Ampere 起的 2:4 结构化稀疏:每 4 个权重最多 2 个非零(50% 稀疏率),硬件以固定格式记录两个非零值的索引,MAC 直接跳过零值 → 理论 2× 吞吐。配套的硬件机制叫跳零(zero-skip / data gating):压缩编码(bitmap 或索引+值)解码后,为零的数据不进入乘法器甚至直接关断其时钟(Eyeriss 的 data gating 即此思路)。
为什么:稀疏化的收益必须在硬件端兑现才成立 —— 软件上把权重置零,GPU 仍会老老实实做乘法(乘 0 也是一拍);只有硬件"跳过",省下的才是真实算力与能耗。而稀疏与量化(08-08 页)是天生搭档:INT8 判断一个数是否为零只要 8 位比较器,FP32 判断还要先解码,所以商用稀疏加速器都工作在低精度定点上。
怎么做(机制分两派):①zero-skip 跳零:权重以 (索引, 非零值) 流水送入,乘法器每拍只接非零对 —— NVDLA 的 sparse weight compression 就属于这类:剪枝后的权重以压缩格式存入 DDR,DMA 解压后只向 MAC 阵列喂非零权重(其 Primer 明确列出"支持稀疏权重压缩"作为可配置特性);②zero-gating 门控:不改变数据流,仅当输入为零时关断该 MAC 的翻转(data gating),省动态功耗但不省周期。两派常混合使用。
面试怎么问:①"非结构化稀疏 90% 为什么在 GPU 上常常不快,甚至更慢?"(不规则的索引与访存 > 省下的乘法,需要专用格式与内核;这引出"结构化换硬件效率"的工程结论);②"2:4 的'2'和'4'分别是什么?"(每 4 个连续权重至多 2 个非零,索引只有 C(4,2)=6 种组合,可用 3bit 编码);③"稀疏化和量化能叠加吗?"(能,先剪枝再量化再蒸馏恢复是常见流水线,注意精度联合校准)。
易错点:① 端到端加速比 ≠ 稀疏率:50% 稀疏率常只换来 1.2~1.6× 实测加速(解码、索引、访存不打折);② 稀疏省的是计算和权重访存,激活访存与 psum 不一定省,Roofline 上算术强度可能不升反降要看格式;③ 剪枝后必须 fine-tune 恢复精度,"剪完直接用"精度会崩。
是什么:学 NPU 不必从零发明 —— 两个开源项目正好对应第 3.2 节的两条路线,且都真实可跑。NVDLA(NVIDIA Deep Learning Accelerator,2017 开源):可配置的固定功能流水线架构 —— 卷积核心(CSC/CMAC/CACC,2048 个 INT8 MAC)+ SDP(激活)+ PDP(池化)+ CDP(归一化)+ BDMA/Rubik(数据搬运重排),模块间可"融合模式"直通,配有完整软件栈(编译器/驱动);GitHub nvdla/hw 约 2.1k star,v1 分支固定为"2048 个 8bit MAC(或 1024 个 16bit)"并只接受 bug 修复。Gemmini(UC Berkeley,Chipyard 生态):用 Chisel 写的脉动阵列生成器 —— 默认 16×16、可在 elaboration 时改阵列规模/scratchpad 容量/bank 数,支持 weight-stationary 与 output-stationary 两种数据流运行时可选;以 RoCC 加速器形态挂在 RISC-V 核旁,用 mvin/matmul/mvout/loop 自定义指令驱动,约 1.4k star。
怎么做(阅读路线):NVDLA 线:官网 Primer(免费入门神文,讲清"为什么 CNN 四类算子适合专用硬件")→ 在线架构文档(模块图+融合模式)→ hw 仓库 vmod/nvdla/ 逐模块读 RTL(NV_NVDLA_CMAC 阵列、CACC 累加器最值得精读)→ cmod/ C 模型对照验证。Gemmini 线:README(架构总览)→ src/main/scala/gemmini/(MeshWithDelays 阵列、DMA、ROB)→ ISA 文档(指令语义)→ software/gemmini-rocc-tests 跑通第一个矩阵乘 → modeling/timeloop 玩性能建模。两条线各花 2~3 周,面试时"我读过两个开源 NPU 的源码"就是硬通货。
| 维度 | NVDLA | Gemmini |
|---|---|---|
| 出品 / 开源时间 | NVIDIA,2017 | UC Berkeley(Chipyard 生态),2020 前后成型 |
| 架构路线 | 可配置固定功能流水线(CNN 专用) | 可配置脉动阵列生成器(GEMM 通用) |
| 核心算力单元 | 卷积核心 2048 个 INT8 MAC(可配 1024×16bit) | 默认 16×16 = 256 MAC,规模可参数化生成 |
| 数据流 | 层内专用通路 + 融合模式(模块间 FIFO 直通) | WS / OS 双数据流,运行时可选 |
| 编程方式 | 寄存器配置(无指令,APB/AHB 写配置,呼应 08-05) | RISC-V 自定义指令(mvin/matmul/mvout/loop) |
| 语言 / 工具链 | Verilog;tmake 构建仿真;配套编译器与驱动 | Chisel;Chipyard/Verilator;配 C 库与 ONNX-Runtime |
| 仓库入口 | github.com/nvdla/hw(vmod/nvdla 放 RTL,cmod 放 C 模型,verif 放 trace 回放) | github.com/ucb-bar/gemmini(src 放 Scala RTL,software 放测试与库) |
| 稀疏支持 | 稀疏权重压缩(sparse weight compression) | 核心不含稀疏,靠研究分支/扩展探索 |
| 适合学什么 | 工业级模块划分、层融合、寄存器编程接口、工程完整性 | 脉动阵列微架构、指令集设计、参数化生成器思想 |
| 阅读门槛 | 中:Verilog 直接可读,文档全 | 中高:需会 Chisel/Scala 与 Chipyard 工具链 |
面试怎么问 —— "让你设计一个 NPU,你怎么开始?"(本页压轴,给出标准七步框架):
易错点:① 面试答"先写 RTL"直接出局 —— 第一步永远是负载分析与指标分解;② NVDLA v1 分支已定格(只修 bug),学架构思想可以,追新功能没有;③ Gemmini 的学习成本大头在 Chipyard 工具链而非 RTL 本身,先跑通仿真再读源码事半功倍;④ 两个项目是"参照系"不是"模板":抄 NVDLA 的模块划分 + 抄 Gemmini 的生成器思想,组合出你自己的 8×8 小 NPU(08-10 页)才是正确姿势。
本页架构观点与数据以下列一手资料为准:NVDLA / Gemmini 官方仓库与文档(两大参照系原典)、Eyeriss 项目页(数据流经典)、MIT 6.5930 课程(体系结构理论框架)、TPU 官方资料与中文解读(脉动阵列工业落地)。
vmod/nvdla/ 放 RTL、cmod/ 放 C 模型、verif/ 放 trace 回放测试平台,配 bin/tmake 一键仿真 —— 08-10 页写完 RTL 后对照的第一范本。