第三章 AI加速器架构:神经网络处理单元(NPU)的微架构设计,MAC阵列与数据流
好,咱们进入正题。这一章聊的是NPU的核心——MAC阵列和数据流。说白了,这就是AI加速器的心脏。你想想看,一个自动驾驶芯片里,神经网络的计算量动不动就是几百TOPS,没有高效的MAC阵列,根本跑不动。
3.1 MAC阵列的基本结构
MAC,全称是Multiply-Accumulate,乘累加操作。一个MAC单元做一次乘法和一次加法。NPU里成千上万个MAC单元排成阵列,这就是MAC阵列。
我个人习惯把MAC阵列分成三类:
- 一维阵列(1D MAC Array):适合向量-矩阵乘法,结构简单,但数据复用率低。
- 二维阵列(2D MAC Array):最常用,支持矩阵-矩阵乘法,数据复用率高。
- 三维阵列(3D MAC Array):用于3D卷积或张量处理,面积大,功耗高,自动驾驶里用得少。
嗯,这里要注意:自动驾驶场景下,2D MAC阵列是主流。为什么?因为卷积神经网络(CNN)的卷积核通常是2D的,2D阵列能完美匹配。
关键参数:MAC阵列的利用率。不是所有MAC单元每时每刻都在干活。数据流设计不好,利用率可能不到50%。
3.2 数据流设计:三种主流方案
数据流,就是数据怎么在MAC阵列里流动。我见过太多项目,MAC阵列设计得很大,但数据流没想好,结果性能惨不忍睹。
三种主流数据流:
| 数据流类型 | 核心思想 | 适用场景 | 优缺点 |
|---|---|---|---|
| 权重固定(Weight Stationary) | 权重数据留在MAC单元内,输入特征图流动 | 权重复用率高,适合大卷积核 | 权重存储开销大,但计算效率高 |
| 输入固定(Input Stationary) | 输入特征图留在MAC单元内,权重流动 | 输入数据复用率高,适合小卷积核 | 输入存储开销大,但灵活性好 |
| 输出固定(Output Stationary) | 部分和(partial sum)留在MAC单元内,权重和输入都流动 | 减少中间结果搬运,适合深度网络 | 控制逻辑复杂,但能效比高 |
我在项目中遇到过一个问题:一开始选了权重固定数据流,觉得权重复用率高。结果发现自动驾驶场景里,网络层数深、卷积核小,权重复用率其实不高。后来改成输出固定,性能提升了30%。
我的建议:别死磕一种数据流。现代NPU大多支持混合数据流,根据网络层动态切换。比如,前几层用输入固定,中间层用输出固定,最后几层用权重固定。
3.3 MAC阵列的微架构细节
好,咱们深入一点。MAC阵列的微架构,说白了就是每个MAC单元怎么连、数据怎么走。
常见的微架构有:
- 脉动阵列(Systolic Array):数据像流水一样,一个时钟周期移动一次。Google TPU用的就是这种。优点是控制简单,缺点是数据流固定,灵活性差。
- 广播阵列(Broadcast Array):一个数据同时广播给所有MAC单元。优点是灵活性高,缺点是布线压力大,频率上不去。
- 混合阵列(Hybrid Array):结合脉动和广播的优点。我比较推荐这种,尤其是自动驾驶这种多变的场景。
举个例子,一个8x8的脉动阵列,数据从左边流入,从上边流入,结果从下边流出。代码实现起来其实不复杂:
// 伪代码:脉动阵列的MAC操作
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
// 每个MAC单元接收来自左边和上边的数据
mac[i][j].input = input_from_left[i][j];
mac[i][j].weight = weight_from_top[i][j];
// 执行乘累加
mac[i][j].psum = mac[i][j].input * mac[i][j].weight + mac[i][j].prev_psum;
// 将结果传递给右边和下边的MAC单元
output_to_right[i][j] = mac[i][j].input;
output_to_bottom[i][j] = mac[i][j].weight;
}
}
嗯,这里要注意:实际硬件里,MAC单元之间还有流水线寄存器,不然时序会崩。我曾经因为忘了加寄存器,导致芯片跑不到目标频率,流片回来只能降频用,教训深刻。
3.4 数据复用的策略
数据复用,是NPU设计的核心。你想想看,如果每个数据只用一次,那带宽需求会爆炸。
三种复用策略:
- 权重复用:同一个权重被多个输入复用。比如卷积核在特征图上滑动时,权重不变。
- 输入复用:同一个输入被多个权重复用。比如多个卷积核同时处理同一个输入像素。
- 部分和复用:中间结果在MAC阵列内部累加,不用频繁写回内存。
我个人习惯,在自动驾驶NPU里,优先保证部分和复用。为什么?因为自动驾驶网络层数深,中间结果量巨大。如果每层都写回DDR,功耗和延迟都受不了。
避坑指南:我曾经在一个项目里,为了追求高复用率,把MAC阵列设计得特别大。结果发现,数据搬运的能耗比计算能耗还高。记住:复用不是目的,降低总能耗才是。有时候小阵列+高频率,反而更高效。
3.5 实际设计中的权衡
好,咱们聊聊实际设计中的权衡。MAC阵列设计,说白了就是面积、功耗、性能的三角博弈。
| 设计参数 | 面积影响 | 功耗影响 | 性能影响 |
|---|---|---|---|
| MAC阵列大小 | 线性增加 | 线性增加 | 吞吐量提升,但利用率可能下降 |
| 数据位宽 | 平方增加 | 线性增加 | 精度提升,但计算速度下降 |
| 流水线深度 | 轻微增加 | 轻微增加 | 频率提升,但延迟增加 |
| 数据流类型 | 影响控制逻辑 | 影响数据搬运 | 影响利用率 |
举个例子,你设计一个128x128的MAC阵列,面积大概占整个NPU的60%以上。如果自动驾驶芯片的功耗预算只有10W,那MAC阵列的功耗不能超过6W。这时候你可能得考虑降低频率,或者用更小的阵列。
我记得有一次,客户要求芯片在5W功耗下跑30TOPS。算了一下,MAC阵列的能效必须达到6TOPS/W。当时我们用了7nm工艺,配合输出固定数据流,才勉强达标。所以说,设计MAC阵列,不能只看性能,还得看功耗墙。
3.6 小结
这一章咱们聊了MAC阵列的三种结构、三种数据流、微架构细节、数据复用策略,还有实际设计中的权衡。核心就一句话:MAC阵列是NPU的心脏,数据流是血管,两者配合好,芯片才能跑得又快又省电。
下一章,咱们聊聊NPU的存储层次设计。为什么自动驾驶NPU需要巨大的片上SRAM?怎么设计数据搬运的路径?到时候再细聊。
课后思考:如果你要设计一个面向L4级自动驾驶的NPU,MAC阵列大小选64x64还是128x128?数据流选哪种?为什么?