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 知识体系总览
为了让你更直观地理解,我画了一张图,把本章的核心逻辑串起来:
这张图把近存和存内两条路的特点、映射策略都串起来了。你想想看,做映射优化时,是不是先得搞清楚你的算子属于哪一类?权重固定的,就往存内计算走;数据复用率低的,就往近存计算走。混着用也没问题,但一定要清楚每个算子的“性格”。
好了,这一章就聊到这儿。记住一句话:没有最好的架构,只有最合适的映射。下一章咱们会深入具体的映射算法,到时候再细聊。
想要更多资料,联系可可