第三章 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设计的核心。你想想看,如果每个数据只用一次,那带宽需求会爆炸。

三种复用策略:

  1. 权重复用:同一个权重被多个输入复用。比如卷积核在特征图上滑动时,权重不变。
  2. 输入复用:同一个输入被多个权重复用。比如多个卷积核同时处理同一个输入像素。
  3. 部分和复用:中间结果在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?数据流选哪种?为什么?