训练加速器硬件基础:矩阵乘法单元、片上存储层次与数据通路设计
好,我们直接切入正题。今天聊的是训练加速器最核心的几块硬件:矩阵乘法单元、存储层次,还有数据通路。这三样东西,说白了就是加速器的“心脏”、“血管”和“骨架”。你把这几个搞明白了,后面看任何架构图都会觉得眼熟。
一、矩阵乘法单元: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)。如果存储带宽低于这个值,计算单元就会“挨饿”。
三、数据通路设计:别让连线成为瓶颈
数据通路,就是数据从存储到计算单元,再到存储的路径。听起来简单,但设计起来很讲究。
我习惯把数据通路分成三类:
- 权重分发通路:从Global Buffer把权重广播到所有PE。这个通路需要高带宽,但方向单一。
- 激活值输入通路:从Global Buffer把输入特征图送到Systolic Array的左侧或上方。这个通路需要支持“数据复用”——同一个输入可能被多个PE使用。
- 部分和累加通路:PE计算完的结果,需要沿着阵列向下或向右累加,最后写回Global Buffer。这个通路需要支持“归约”操作。
你想想看,如果这三条通路共用同一组连线,会发生什么?冲突。权重在广播的时候,激活值也在送,部分和也在传,总线就堵死了。
所以,我建议物理上分离这三条通路。权重用专用的广播总线,激活值用点对点或树形网络,部分和用链式或树形归约网络。这样,三者可以同时进行,互不干扰。
另外,数据通路的位宽也很关键。我见过一个设计,数据通路位宽只有64位,但计算单元一次要处理128位的数据。结果呢?一个计算操作要拆成两个cycle来传输数据,吞吐直接减半。嗯,这个坑我踩过。
警告:数据通路的位宽,必须和计算单元的“数据粒度”对齐。如果你的PE一次处理4个8位整数,那数据通路至少是32位宽。如果还要支持双倍数据率(DDR),那就更复杂了。我建议在设计初期,用一张表把各个通路的带宽需求列出来,然后统一规划。
四、一张图看懂整体数据流
下面我用一张SVG图,把这三部分串起来。你可以看到数据是怎么从片外DRAM,流到片上存储,再流到Systolic Array,最后写回去的。
这张图里,你可以看到数据从DRAM出发,经过Global Buffer、Local SRAM、Register File,最后进入Systolic Array。计算完的结果,通过累加器归约后,再写回Global Buffer。整个过程,数据流是单向的、分层的。每一层都在做“缓冲”和“搬运”的工作。
我个人觉得,这张图最核心的点在于:数据流的层次化设计,决定了加速器的性能上限。你可以在计算单元上堆很多PE,但如果存储层次设计不合理,数据流就会卡住。就像高速公路,车道再多,出口堵了也没用。
五、总结一下
今天聊的三个点,其实是一个整体:
- Systolic Array 负责高效计算,核心是数据复用。
- 片上存储层次 负责数据缓冲,核心是带宽匹配。
- 数据通路 负责数据搬运,核心是路径分离。
你把这三点想透了,再去看Google的TPU、NVIDIA的Tensor Core,或者任何一款AI芯片,都能很快抓住它的设计思路。嗯,下次你看到某个芯片的die shot,可以试着找找它的Systolic Array在哪,存储层次怎么排,数据通路怎么走。这是个很有意思的练习。
一句话记住今天的内容:训练加速器的硬件基础,就是让数据“流”起来,让计算“忙”起来,让存储“喂”得饱。