2. NPU硬件架构基础
大家好,我是你们的老朋友。今天咱们聊聊NPU的硬件架构。说实话,很多做算子映射的朋友,一上来就盯着指令集手册看,结果越看越懵。我个人习惯是,先搞清楚芯片的“骨架”——也就是计算单元、存储、数据通路这些底层结构。你想想看,连硬件长什么样都不知道,怎么写出高效的算子呢?
一、NPU计算单元:算力的心脏
NPU的核心,说白了就是一堆计算单元堆在一起。跟CPU那种通用核不一样,NPU的计算单元是专门为矩阵运算和卷积设计的。
1. 乘累加阵列(MAC Array)
这是NPU最核心的部分。一个MAC单元做一次乘法和一次加法,成千上万个MAC排成阵列,就能并行处理大量数据。我在项目中遇到过,有些芯片的MAC阵列是16x16,有些是32x32,甚至更大的。阵列越大,单次计算吞吐量越高,但功耗和面积也上去了。
关键参数:MAC阵列大小直接决定了算力峰值。比如一个512 MAC的NPU,跑在1GHz,理论算力就是512 GFLOPS(假设是FP16)。
2. 向量处理单元(Vector Unit)
除了矩阵乘,NPU还得处理激活函数、归一化、池化这些操作。这些活儿通常交给向量处理单元。它有点像CPU的SIMD,但更专一。我记得有一次做算子融合,发现向量单元和MAC阵列之间数据搬运太频繁,性能直接腰斩。嗯,这里要注意,尽量让数据在计算单元内部流转,别老往外搬。
3. 标量处理单元(Scalar Unit)
控制流、地址计算、循环计数这些杂活,就交给标量单元。它虽然算力不强,但不可或缺。你想想看,没有它,谁给MAC阵列喂数据?
二、存储层次:数据流动的命脉
NPU的存储层次,跟CPU的Cache层级很像,但更强调带宽和延迟的平衡。我经常跟团队说,算子优化的本质,就是管理好数据在存储层次间的流动。
| 存储层级 | 容量 | 带宽 | 延迟 | 典型用途 |
|---|---|---|---|---|
| 寄存器文件(RF) | 几十KB | 极高 | 1 cycle | 临时变量、累加结果 |
| 本地内存(Local Memory) | 几百KB~几MB | 高 | 几 cycle | 输入特征图、权重 |
| 共享内存(Shared Memory) | 几MB~几十MB | 中等 | 几十 cycle | 多核数据交换 |
| 全局内存(Global Memory) | 几GB | 低 | 几百 cycle | 模型参数、中间结果 |
实战技巧:我曾经踩过一个坑——把整个权重矩阵都塞进本地内存,结果容量不够,频繁溢出到全局内存,性能惨不忍睹。后来改用分块(tiling)策略,每次只加载一小块权重到本地内存,计算完再换下一块。这才把性能拉回来。
三、数据通路:数据怎么跑起来
数据通路,就是数据从存储到计算单元,再写回存储的路径。这里有个关键概念——数据流(Dataflow)。不同的NPU架构,数据流方式差别很大。
1. 输入固定流(Input Stationary)
输入特征图数据固定在MAC阵列里,权重数据流进来。这种方式适合卷积层,因为输入特征图复用率高。我建议在做卷积算子映射时,优先考虑这种数据流。
2. 权重固定流(Weight Stationary)
权重数据固定在MAC阵列里,输入特征图流进来。适合全连接层,因为权重复用率高。你想想看,全连接层的权重矩阵那么大,搬来搬去多浪费带宽。
3. 输出固定流(Output Stationary)
部分和结果固定在MAC阵列里,输入和权重都流进来。这种方式适合深度可分离卷积,能减少中间结果的搬运。
注意:数据流的选择不是拍脑袋定的。我见过有人不管三七二十一,所有算子都用输入固定流,结果在深度可分离卷积上性能反而下降。一定要根据算子的计算特征来选。
四、指令集概览:怎么指挥硬件干活
NPU的指令集,跟CPU的指令集完全是两码事。CPU指令集强调通用性,NPU指令集强调并行性和数据流控制。
1. 计算指令
包括矩阵乘指令、向量运算指令、标量运算指令。比如一条矩阵乘指令,可能就完成了整个MAC阵列的一次计算。我刚开始接触时觉得这太简单了,后来才发现,指令的配置参数(比如数据格式、累加模式)才是真正的坑。
// 伪代码示例:NPU矩阵乘指令
MATMUL R0, R1, R2, SIZE=32, DTYPE=FP16
// R0: 输出地址
// R1: 输入A地址
// R2: 输入B地址
// SIZE: 矩阵维度32x32
// DTYPE: 数据类型FP16
2. 数据搬运指令
负责在存储层次间搬数据。比如从全局内存搬到本地内存,或者从本地内存搬到寄存器。我曾经犯过一个低级错误——数据搬运指令的地址对齐没做好,结果硬件报错,排查了半天才发现是地址没对齐到128字节边界。
3. 控制指令
包括循环控制、分支跳转、同步屏障等。NPU的控制指令比CPU少得多,因为大部分计算是数据并行的,不需要复杂的控制流。
4. 同步指令
多核NPU里,核与核之间需要同步。比如一个核算完一部分结果,另一个核才能开始用。同步指令用不好,就会出现数据竞争,结果算出来是错的。
核心原则:指令集是硬件能力的抽象。你写算子时,脑子里要时刻想着:这条指令背后,硬件是怎么跑的?数据从哪里来,到哪里去?计算单元在干什么?
五、知识体系总览
下面这张图,是我自己总结的NPU硬件架构知识体系。你可以把它当成一张地图,随时回来看看。
这张图把四个核心模块串起来了。你写算子时,可以对照着看看:当前算子的计算瓶颈在哪?存储够不够用?数据流选对了没?指令用得对不对?
好了,这一章的内容就到这儿。记住,硬件架构是算子优化的根基。把今天讲的这些搞明白,后面做算子映射时,你就能少走很多弯路。