3、NPU硬件架构基础:NPU的组成、计算单元、存储层次、互联结构与指令集特点
NPU(Neural Processing Unit)并非通用处理器的替代品,而是面向神经网络计算范式深度定制的专用加速引擎。理解NPU的硬件架构,是掌握AI编译器后端优化、指令调度与算子映射的前提。本章从NPU的宏观组成出发,逐层拆解计算单元、存储层次、互联结构以及指令集设计,帮助学员建立从硬件微架构到编译器代码生成的完整认知链路。
3.1 NPU的宏观组成与设计哲学
NPU的宏观组成围绕“数据搬运”与“数据计算”两大主题展开。与CPU追求低延迟分支预测、GPU追求大规模线程并行不同,NPU的设计哲学是“以数据流为中心”——尽可能让数据在片上停留更久,减少与外部DRAM的交互次数。一个典型的NPU芯片内部包含四大核心域:计算域(Compute Domain)、存储域(Memory Domain)、互联域(Interconnect Domain)以及控制域(Control Domain)。
计算域由大量乘累加单元(MAC)组成的脉动阵列或SIMD阵列构成,负责执行矩阵乘法和卷积运算。存储域则采用多级层次结构,从寄存器文件到片上SRAM再到共享缓存,每一级都针对特定的数据复用模式设计。互联域负责将计算单元与存储单元高效连接,常见拓扑包括Mesh网络、Ring网络和Crossbar。控制域则包含指令发射逻辑、DMA引擎和同步机制,确保数据在正确的时间出现在正确的位置。
从编译器视角看,理解NPU组成的关键在于:编译器需要将计算图映射到物理计算单元上,同时管理数据在存储层次中的生命周期。例如,卷积操作中的输入特征图(IFMap)和权重(Weight)应尽可能驻留在片上SRAM中,而输出特征图(OFMap)则通过DMA异步写回DRAM。这种“计算-存储”协同设计决定了编译器的调度策略。
3.2 计算单元:从MAC阵列到专用算子引擎
计算单元是NPU执行神经网络运算的核心硬件资源。根据设计目标不同,计算单元可分为三类:脉动阵列(Systolic Array)、SIMD向量单元和专用算子引擎(如Transformer引擎)。
脉动阵列是Google TPU和多数NPU采用的结构。其核心思想是将多个MAC单元排列成二维网格,数据在阵列中“脉动”式流动,每个MAC只与相邻MAC通信,从而大幅降低数据搬运开销。例如,一个32×32的脉动阵列包含1024个MAC,每个时钟周期可完成1024次乘累加操作。编译器需要将卷积运算的输入特征图按“数据复用”原则切分为多个Tile,并决定权重在阵列中的预加载方式。
SIMD向量单元则更灵活,支持逐元素操作(如ReLU、BatchNorm、Element-wise Add)。这些操作通常不涉及数据复用,但需要高吞吐的并行处理。向量单元的位宽通常为128位至512位,可同时处理16~64个FP16数据。编译器需将标量循环向量化,并合理分配向量寄存器。
专用算子引擎是近年来针对Transformer模型新增的硬件模块,例如支持FlashAttention的融合计算单元,或支持Softmax硬件加速的专用电路。这些引擎将原本需要多指令序列完成的复杂算子固化为单条指令,极大提升了执行效率。编译器需识别计算图中的特定模式(如QKV投影+Attention Score计算),并将其映射到专用引擎上。
编译器映射要点:在调度矩阵乘法时,编译器需决定脉动阵列的“数据流方向”。例如,权重固定(Weight Stationary)适合权重复用率高的全连接层;输入固定(Input Stationary)适合输入特征图复用率高的卷积层。选择错误的数据流方向可能导致MAC利用率下降30%以上。
3.3 存储层次:容量、带宽与延迟的权衡
NPU的存储层次设计直接决定了算力能否被有效释放。典型的存储层次从内到外包括:寄存器文件(RF)→ 片上SRAM(Local Memory)→ 共享缓存(L2 Cache)→ 外部DRAM。每一级的容量、带宽和延迟差异巨大,编译器必须显式管理数据在这些层级间的移动。
| 存储层级 | 典型容量 | 访问带宽 | 延迟 | 编译器管理方式 |
|---|---|---|---|---|
| 寄存器文件 (RF) | 256KB ~ 1MB | >10TB/s | 1~2 cycles | 寄存器分配,变量生命周期管理 |
| 片上SRAM | 512KB ~ 8MB | 2~8TB/s | 10~30 cycles | 显式DMA搬运,Tile切分,Double Buffer |
| 共享缓存 (L2) | 8MB ~ 64MB | 0.5~2TB/s | 50~150 cycles | 数据预取,缓存命中优化 |
| 外部DRAM | 8GB ~ 128GB | 64GB/s ~ 1TB/s | 200~500 cycles | DMA批量传输,数据复用最大化 |
编译器在存储管理上的核心挑战是“数据局部性优化”。以卷积层为例,输入特征图的一个Tile(例如32×32×64)需要从DRAM加载到SRAM,然后被脉动阵列多次读取。如果编译器能将Tile大小设置为与SRAM容量匹配,并采用Double Buffer技术(即DMA预取下一块数据的同时计算当前块),则可实现计算与数据传输的重叠,隐藏DRAM访问延迟。
经验提醒:在编写NPU调度策略时,务必计算“计算时间/数据传输时间”的比值(Arithmetic Intensity)。当该比值小于1时,NPU将受限于存储带宽(Memory-Bound),此时应优先优化数据复用和DMA传输效率,而非增加计算指令并行度。
3.4 互联结构:NoC拓扑与数据路由
互联结构决定了计算单元与存储单元之间的通信效率。现代NPU普遍采用片上网络(NoC)替代传统的总线结构,以支持多计算簇与多存储Bank之间的并发通信。常见的NoC拓扑包括:2D Mesh、Ring和Crossbar。
2D Mesh拓扑将计算簇和存储控制器排列成网格,每个节点通过路由器和相邻节点通信。其优点是扩展性好,适合大规模NPU(如64个计算簇以上);缺点是远距离通信延迟较高,需要编译器感知数据放置位置。例如,若计算簇0需要读取存储Bank 7的数据,数据需经过多跳路由,编译器应尽量将数据放置在“近端”Bank。
Ring拓扑将所有节点连接成环形,数据沿环单向或双向传输。其结构简单、布线容易,但带宽受限于环的周长。适合中等规模NPU(16~32个计算簇)。Crossbar则提供全连接能力,任意节点间可低延迟通信,但硬件成本随节点数平方增长,仅适用于小规模(8个节点以内)。
编译器在指令调度时需考虑NoC的拥塞避免。例如,当多个计算簇同时向同一存储Bank发起DMA请求时,NoC路由器会产生仲裁延迟。编译器可通过错峰调度(Staggered Scheduling)或数据复制(Data Replication)来缓解拥塞。
3.5 指令集特点:VLIW、SIMD与专用指令的融合
NPU的指令集设计融合了VLIW(超长指令字)、SIMD(单指令多数据)以及专用硬件指令三类风格。理解指令集特点对于编译器后端代码生成至关重要。
VLIW架构将多条并行操作打包在一条长指令中,由编译器静态调度。例如,一条256位宽的VLIW指令可同时包含:1条MAC阵列操作、1条向量ALU操作、1条DMA搬运操作和1条标量控制操作。编译器需确保这些操作之间无数据依赖,否则会产生流水线停顿。与CPU的动态调度不同,VLIW将调度复杂度转移给编译器,硬件只需按序发射即可。
SIMD指令用于向量单元,支持对多个数据元素执行相同操作。例如,一条向量指令可对16个FP16数据同时执行ReLU。编译器需将标量循环展开并向量化,同时处理数据对齐和边界条件。
专用指令是NPU区别于通用处理器的重要特征。例如,一条“矩阵乘累加”指令(MMA)可直接驱动脉动阵列完成整个Tile的矩阵乘法;一条“DMA传输”指令可指定源地址、目的地址、传输长度和同步标志。这些专用指令大幅减少了指令发射数量,但也要求编译器具备高度的模式识别能力。
// 示例:NPU VLIW指令打包示意(伪代码)
// 一条指令同时驱动计算、搬运和控制
{
MAC_OP: MMA(M0, A_Tile[0:31][0:31], B_Tile[0:31][0:31], Acc[0:31][0:31]);
VEC_OP: VECTOR_RELU(V0, V1, V2); // 向量ReLU
DMA_OP: DMA_LOAD(SRAM_ADDR_1, DRAM_ADDR_X, 64KB, CHANNEL_0);
SCALAR: UPDATE_LOOP_COUNTER(R0, R0, 1);
SYNC: WAIT_DMA_CHANNEL_0(); // 等待DMA完成
}
编译器在生成指令时需注意指令级并行(ILP)的挖掘。例如,DMA搬运与MAC计算之间通常无数据依赖,可打包在同一VLIW指令中实现“计算-搬运”重叠。但若MAC操作需要读取DMA正在搬运的数据,则必须插入同步指令,这会降低流水线效率。
3.6 案例拆解:卷积层的指令调度与存储分配
为综合理解上述架构特性,我们以一个典型的3×3卷积层(输入特征图112×112×64,输出通道128)为例,分析编译器如何完成指令调度与存储分配。
硬件约束:假设NPU配置为:脉动阵列32×32(1024 MAC),片上SRAM 2MB,DMA带宽128GB/s,DRAM带宽64GB/s。输入特征图总大小为112×112×64×2字节(FP16)= 1.6MB,权重大小为3×3×64×128×2 = 147KB,输出特征图大小为112×112×128×2 = 3.2MB。
初始现象:若编译器不做Tile切分,直接将整个输入特征图加载到SRAM,则SRAM容量不足(1.6MB + 147KB + 输出缓冲 > 2MB)。且输出特征图需写回DRAM,产生大量带宽消耗。
分析过程:编译器首先将输入特征图按空间位置切分为多个Tile,每个Tile大小为28×28×64(100KB),权重按输出通道切分为4块,每块36KB。这样,每个Tile的计算过程为:加载输入Tile(100KB)+ 加载权重块(36KB)→ 计算输出Tile(28×28×32×2 = 50KB)→ 写回DRAM。计算时间约为 (28×28×64×32×2) / (1024 MAC × 1GHz) ≈ 3.1μs,数据传输时间约为 (100KB + 36KB + 50KB) / 128GB/s ≈ 1.45μs。计算时间大于传输时间,说明该Tile是计算密集型(Compute-Bound),可充分利用MAC阵列。
实际操作步骤:
- 使用Double Buffer策略:DMA预加载下一个输入Tile到SRAM Bank A,同时计算当前Tile(数据位于Bank B)。
- 权重采用“权重固定”数据流:将权重块预加载到脉动阵列的寄存器中,在计算多个输入Tile时复用。
- 输出特征图采用异步DMA写回,不阻塞计算流水线。
- 循环顺序优化:将输出通道循环置于最内层,以最大化权重复用。
结果对比:优化前,MAC利用率约45%(因频繁等待DMA);优化后,MAC利用率提升至82%。整体推理延迟降低约35%。
风险与代价:Double Buffer需要额外占用SRAM空间(2倍Tile大小),可能导致Tile尺寸减小,增加循环开销。此外,异步DMA写回需要编译器精确管理同步点,否则可能产生数据竞争。
复盘经验:编译器调度NPU时,应优先计算“算术强度”(计算量/数据量),判断瓶颈类型。对于Compute-Bound算子,重点优化MAC利用率和指令级并行;对于Memory-Bound算子,重点优化数据复用和DMA传输效率。
常见误区:初学者常将NPU视为“万能加速器”,认为所有算子都能获得线性加速。实际上,对于逐元素操作(如Add、ReLU)或数据量远大于计算量的算子(如Softmax),NPU可能受限于存储带宽,性能反而不如GPU。编译器应识别此类算子并考虑“算子融合”或“降精度”策略。
3.7 总结与设计权衡
NPU硬件架构的每一部分设计都围绕“数据复用”和“计算密度”展开。计算单元追求高MAC利用率,存储层次追求数据局部性,互联结构追求低延迟高带宽,指令集追求静态可调度性。AI编译器作为软硬件之间的桥梁,必须深刻理解这些硬件特性,才能生成高效代码。
在实际工程中,编译器开发者需关注以下关键指标:MAC利用率(实际计算量/峰值计算量)、存储带宽利用率(实际传输量/峰值带宽)、指令发射效率(有效指令/总指令周期)。通过性能分析工具定位瓶颈,并针对性地调整Tile大小、数据流方向、循环顺序和同步策略,是NPU编译优化的核心方法论。