3. 硬件架构概览:主流存算一体芯片架构及其对映射的影响

各位同学,咱们今天聊聊硬件架构。说实话,做算子映射优化,不懂硬件架构就是瞎忙活。我见过太多人,算法调得飞起,一上芯片就崩,为什么?说白了,就是没搞懂底层的“脾气”。

存算一体芯片,市面上主流的分两类:近存计算和存内计算。这两兄弟长得像,但性格完全不同。你想想看,一个是在存储器旁边搭个计算单元,一个是直接把计算塞进存储单元里。映射策略能一样吗?

3.1 近存计算架构:搬数据的高手

近存计算,英文叫 Near-Memory Computing。它的核心思路是:把计算单元搬到存储器旁边,缩短数据搬运的距离。

我在项目中遇到过一款近存芯片,它的架构大概是这样的:

+-------------------+     +-------------------+
|   DRAM / SRAM     |     |   DRAM / SRAM     |
|   Bank 0          |     |   Bank 1          |
+--------+----------+     +--------+----------+
         |                         |
         v                         v
+--------+----------+     +--------+----------+
|   Processing      |     |   Processing      |
|   Element (PE)    |     |   Element (PE)    |
+-------------------+     +-------------------+
         |                         |
         +----------+-------------+
                    |
                    v
         +-------------------+
         |   Global Buffer   |
         +-------------------+

嗯,这里要注意,近存计算并没有完全消除数据搬运,只是把搬运距离从“跨芯片”缩短到了“片内”。

核心特点:

  • 计算单元和存储单元物理上分离,但封装在一起
  • 数据仍需通过总线搬运,但延迟大幅降低
  • 适合数据复用率低的算子,比如逐元素操作

映射到这种架构上,我个人的习惯是:优先考虑数据分块。把一个大矩阵切成小块,每个PE处理一块,让数据尽量留在本地。我曾经吃过亏,把整个权重矩阵一次性加载到全局buffer里,结果带宽被打满,性能还不如传统架构。后来改成按行分块,每个PE只处理一行,效果立竿见影。

3.2 存内计算架构:真正的“计算就在原地”

存内计算,In-Memory Computing,这才是真正的狠角色。它直接在存储阵列里做计算,比如用SRAM单元做乘加运算。

我参与过一款基于SRAM的存内计算芯片设计,它的核心结构长这样:

+---------------------------------------------+
|  WL0 ---+----+----+----+----+----+----+----+|
|          |    |    |    |    |    |    |    ||
|  BL0 ---[C]--[C]--[C]--[C]--[C]--[C]--[C]--||
|          |    |    |    |    |    |    |    ||
|  BL1 ---[C]--[C]--[C]--[C]--[C]--[C]--[C]--||
|          |    |    |    |    |    |    |    ||
|  ...     |    |    |    |    |    |    |    ||
|  WLn ---+----+----+----+----+----+----+----+|
+---------------------------------------------+
         |    |    |    |    |    |    |    |
         v    v    v    v    v    v    v    v
      +---------------------------------------+
      |      ADC + Shift-and-Add 电路         |
      +---------------------------------------+

说白了,每个存储单元既是存数据的,也是算数据的。输入信号通过字线(WL)进来,在位线(BL)上直接得到模拟计算结果,再通过ADC转成数字信号。

映射技巧:

存内计算对权重固定、输入变化的算子特别友好。比如卷积层的权重一旦训练好就不变了,直接映射到存储阵列里,每次输入数据进来,一次就能算出所有通道的结果。我建议你把全连接层和卷积层优先映射到存内计算单元上。

3.3 两种架构的对比:选对路子才能跑得快

咱们用一张表来对比一下,这样更直观:

对比维度 近存计算 存内计算
数据搬运 片内搬运,延迟低 几乎无搬运,延迟极低
计算精度 数字计算,精度高 模拟计算,有精度损失
算子适配 适合数据复用率低的算子 适合权重固定的算子
面积效率 中等,PE和存储分开 高,存储即计算
映射复杂度 中等,需考虑数据分块 高,需处理模拟误差

为什么会这样?我解释一下。近存计算本质上还是数字电路,精度有保障,但面积开销大。存内计算用模拟方式做乘加,面积小、速度快,但ADC的精度有限,容易引入噪声。

避坑指南:

我曾经在一个项目里,把Batch Normalization层也映射到了存内计算单元上。结果发现,BN层需要实时计算均值和方差,权重是动态变化的,存内计算根本没法处理。最后只能改回近存计算,白白浪费了两周时间。所以记住:存内计算只适合权重静态的算子。

3.4 架构对映射策略的直接影响

搞清楚了架构,咱们才能谈映射。我总结了几条实战经验:

  • 近存计算:优先做数据本地化。把算子的输入数据尽量分配到同一个PE的本地存储里。比如做矩阵乘法时,把A矩阵的行和B矩阵的列分配到同一个PE,减少跨PE通信。
  • 存内计算:优先做权重固化。把训练好的权重一次性写入存储阵列,后续只更新输入。比如卷积层的权重,映射到存内计算单元后,推理时只需要输入特征图,一次就能算出所有输出通道。
  • 混合架构:现在很多芯片是近存+存内混合的。我建议你把计算密集且权重固定的算子(如卷积、全连接)放到存内计算单元,把控制密集或权重动态的算子(如激活函数、池化、BN)放到近存计算单元。

嗯,这里还要提一句。映射优化不是一锤子买卖。我习惯的做法是:先根据架构特点做一轮粗映射,然后跑仿真看瓶颈,再针对性地调整。比如发现存内计算单元的ADC精度不够,导致分类准确率下降,那就把部分层切回近存计算,用精度换性能。

3.5 知识体系总览

为了让你更直观地理解,我画了一张图,把本章的核心逻辑串起来:

存算一体芯片架构与映射影响 近存计算 (NMC) 存内计算 (IMC) 特点 计算单元靠近存储,数据搬运距离短 数字计算,精度高,面积中等 特点 计算直接在存储阵列内完成 模拟计算,面积小,速度快 映射策略 数据本地化:分块映射,减少跨PE通信 适合:逐元素操作、数据复用率低的算子 映射策略 权重固化:静态权重映射,动态输入计算 适合:卷积、全连接等权重固定的算子 核心原则:根据算子特性选择最匹配的架构

这张图把近存和存内两条路的特点、映射策略都串起来了。你想想看,做映射优化时,是不是先得搞清楚你的算子属于哪一类?权重固定的,就往存内计算走;数据复用率低的,就往近存计算走。混着用也没问题,但一定要清楚每个算子的“性格”。

好了,这一章就聊到这儿。记住一句话:没有最好的架构,只有最合适的映射。下一章咱们会深入具体的映射算法,到时候再细聊。

想要更多资料,联系可可