训练加速器硬件基础:矩阵乘法单元、片上存储层次与数据通路设计

好,我们直接切入正题。今天聊的是训练加速器最核心的几块硬件:矩阵乘法单元、存储层次,还有数据通路。这三样东西,说白了就是加速器的“心脏”、“血管”和“骨架”。你把这几个搞明白了,后面看任何架构图都会觉得眼熟。

一、矩阵乘法单元:Systolic Array 为什么是主流?

矩阵乘法是深度学习里最频繁的操作,没有之一。全连接层、卷积层,底层拆开全是矩阵乘。所以,怎么高效地算这个玩意儿,就成了加速器设计的头号难题。

我个人习惯,一上来先看计算单元的利用率。传统的CPU或GPU用SIMD去算矩阵乘,其实有瓶颈——数据搬运太频繁了。你想想看,每次计算都要从内存里把数据拉进来,算完再存回去,这个来回折腾的时间,比计算本身还长。

Systolic Array 的思路很巧妙。它让数据像血液一样,在计算单元之间“流动”。每个处理单元(PE)只做一件事:乘加。然后结果传给下一个邻居。这样,数据复用率极高,访存次数大幅减少。

核心要点:Systolic Array 的本质是用“数据流”替代“随机访存”。它牺牲了灵活性,换来了极高的吞吐和能效。

我在项目中遇到过一个问题:Systolic Array 的尺寸怎么定?64x64?128x128?其实没有标准答案。尺寸越大,并行度越高,但代价是利用率可能下降。为什么?因为矩阵的维度不一定总是对齐的。比如你有个 33x33 的矩阵,放到 64x64 的阵列里,有一半的PE是闲置的。

嗯,这里要注意:Systolic Array 的利用率,取决于你的矩阵分块策略。我建议在设计初期,先跑一下典型模型的矩阵维度分布,再决定阵列大小。别盲目追求大。

二、片上存储层次:SRAM 和 Register File 的博弈

存储层次,是加速器里最容易“掉坑”的地方。你算力再高,数据喂不进去,全是白搭。

典型的训练加速器,片上存储分三层:

  • Global Buffer(全局SRAM):容量最大,通常几百KB到几MB。用来存放权重和中间激活值。
  • Local SRAM(局部SRAM):每个PE或每组PE私有的小存储,几十KB。用来缓存当前计算需要的切片。
  • Register File(寄存器堆):最小最快,通常几百字节。直接挂在计算单元旁边,用于存放当前操作数。

这三层之间,数据搬运是有代价的。从Global Buffer搬到Local SRAM,可能要花几十个cycle。从Local SRAM搬到Register File,几个cycle。从Register File到计算单元,一个cycle。

我曾经犯过一个错误:把所有的权重都放在Global Buffer里,以为这样省事。结果发现,每次计算都要从Global Buffer读数据,带宽根本不够,计算单元一直在“等数据”。后来我改成“双缓冲”结构——当前计算单元用Local SRAM里的数据,同时后台把下一块数据从Global Buffer搬到另一个Local SRAM。这样,计算和搬运就重叠起来了。

避坑指南:我曾经在某个项目里,为了省面积,把Local SRAM砍了一半。结果训练吞吐量直接掉了30%。因为数据搬运的等待时间变长了。记住:存储带宽和计算吞吐必须匹配。你可以用公式算一下:计算吞吐(MAC/s) × 数据位宽(字节) = 所需带宽(字节/s)。如果存储带宽低于这个值,计算单元就会“挨饿”。

三、数据通路设计:别让连线成为瓶颈

数据通路,就是数据从存储到计算单元,再到存储的路径。听起来简单,但设计起来很讲究。

我习惯把数据通路分成三类:

  1. 权重分发通路:从Global Buffer把权重广播到所有PE。这个通路需要高带宽,但方向单一。
  2. 激活值输入通路:从Global Buffer把输入特征图送到Systolic Array的左侧或上方。这个通路需要支持“数据复用”——同一个输入可能被多个PE使用。
  3. 部分和累加通路:PE计算完的结果,需要沿着阵列向下或向右累加,最后写回Global Buffer。这个通路需要支持“归约”操作。

你想想看,如果这三条通路共用同一组连线,会发生什么?冲突。权重在广播的时候,激活值也在送,部分和也在传,总线就堵死了。

所以,我建议物理上分离这三条通路。权重用专用的广播总线,激活值用点对点或树形网络,部分和用链式或树形归约网络。这样,三者可以同时进行,互不干扰。

另外,数据通路的位宽也很关键。我见过一个设计,数据通路位宽只有64位,但计算单元一次要处理128位的数据。结果呢?一个计算操作要拆成两个cycle来传输数据,吞吐直接减半。嗯,这个坑我踩过。

警告:数据通路的位宽,必须和计算单元的“数据粒度”对齐。如果你的PE一次处理4个8位整数,那数据通路至少是32位宽。如果还要支持双倍数据率(DDR),那就更复杂了。我建议在设计初期,用一张表把各个通路的带宽需求列出来,然后统一规划。

四、一张图看懂整体数据流

下面我用一张SVG图,把这三部分串起来。你可以看到数据是怎么从片外DRAM,流到片上存储,再流到Systolic Array,最后写回去的。

训练加速器数据流架构图 片外DRAM 权重/激活值/梯度 高带宽总线 Global Buffer SRAM (几MB) 数据分发 Local SRAM 双缓冲 (几十KB) 近邻搬运 Register File 操作数 Systolic Array PE阵列 (如64x64) 乘加运算 部分和 累加器 归约 写回结果 图例 数据搬运路径 计算数据路径 写回路径

这张图里,你可以看到数据从DRAM出发,经过Global Buffer、Local SRAM、Register File,最后进入Systolic Array。计算完的结果,通过累加器归约后,再写回Global Buffer。整个过程,数据流是单向的、分层的。每一层都在做“缓冲”和“搬运”的工作。

我个人觉得,这张图最核心的点在于:数据流的层次化设计,决定了加速器的性能上限。你可以在计算单元上堆很多PE,但如果存储层次设计不合理,数据流就会卡住。就像高速公路,车道再多,出口堵了也没用。

五、总结一下

今天聊的三个点,其实是一个整体:

  • Systolic Array 负责高效计算,核心是数据复用。
  • 片上存储层次 负责数据缓冲,核心是带宽匹配。
  • 数据通路 负责数据搬运,核心是路径分离。

你把这三点想透了,再去看Google的TPU、NVIDIA的Tensor Core,或者任何一款AI芯片,都能很快抓住它的设计思路。嗯,下次你看到某个芯片的die shot,可以试着找找它的Systolic Array在哪,存储层次怎么排,数据通路怎么走。这是个很有意思的练习。

一句话记住今天的内容:训练加速器的硬件基础,就是让数据“流”起来,让计算“忙”起来,让存储“喂”得饱。

专注资料整理