3、存储层次结构:片上存储(SRAM)设计、全局缓冲区与本地寄存器、数据复用策略、带宽优化

存储,说白了就是加速器的命脉。

我见过太多团队,算力堆得高高的,结果数据喂不进去,整个芯片在那空转。你想想看,一个MAC阵列再强,如果数据搬运跟不上,那就是个摆设。所以这一章,咱们聊聊存储层次怎么搭,才能让数据流像高速公路一样顺畅。

3.1 片上存储(SRAM)设计:你的“数据粮仓”

SRAM是片上最快的存储,但也是最贵的。我个人习惯把SRAM分成三级:L1紧贴计算单元,L2做全局缓冲,L3偶尔用来存一些查表参数。

设计要点:

  • 容量权衡:SRAM面积大,1MB的SRAM可能吃掉整个芯片面积的20%-30%。我建议先算好“最小工作集”——也就是一次卷积需要多少输入和权重,再乘以2作为安全余量。
  • Bank划分:别搞一个大块SRAM,读写冲突会让你头疼。分成4-8个Bank,每个Bank独立读写,带宽直接翻倍。
  • 位宽选择:我做过一个项目,SRAM位宽从64bit提到128bit,带宽翻倍,面积只涨了15%。这笔账划算。

核心原则:SRAM的读写带宽必须大于等于计算单元的需求带宽,否则计算单元就会“饿死”。

3.2 全局缓冲区与本地寄存器:数据搬运的“中转站”

全局缓冲区(Global Buffer)是连接DRAM和计算单元的桥梁。本地寄存器(Local Register)则是每个PE私有的“小仓库”。

全局缓冲区设计:

  • 通常用SRAM实现,容量在256KB到2MB之间。
  • 我建议采用双缓冲(Double Buffering)机制:一个缓冲区在加载数据,另一个在供给计算,流水线不中断。
  • 地址映射要灵活,支持行/列/块三种访问模式。为什么?因为卷积的滑动窗口访问模式千奇百怪。

本地寄存器设计:

  • 每个PE配16-32个寄存器,存当前计算需要的输入和权重。
  • 我曾经踩过一个坑:寄存器太少,导致PE频繁从全局缓冲区取数,功耗飙升。后来把寄存器翻了一倍,性能提升30%。
  • 寄存器文件(Register File)的读写端口要够,至少2读1写,不然数据搬运会卡住。

避坑指南:我曾经设计过一个加速器,全局缓冲区带宽算得刚刚好,结果发现DRAM的刷新周期会偷走带宽。嗯,后来加了10%的余量才搞定。

3.3 数据复用策略:让数据“多跑几趟”

数据复用,说白了就是让同一个数据被多个计算单元共享,减少从DRAM搬数据的次数。DRAM访问一次耗能是SRAM的100倍,你想想看这有多重要。

三种常见复用模式:

复用类型 适用场景 带宽节省 实现代价
输入特征图复用 卷积层(多个滤波器共享输入) 高(5-10倍) 低(增加广播逻辑)
权重复用 全连接层(多个输入共享权重) 中(2-5倍) 中(增加权重缓存)
部分和复用 深度可分离卷积 低(1.5-2倍) 高(增加累加器)

我的实践建议:

  • 先做数据流分析,找出哪些数据被多次使用。
  • 对于卷积层,我习惯用“输入复用”策略:把输入特征图广播到所有PE,每个PE算不同的输出通道。
  • 对于全连接层,用“权重复用”:权重存在全局缓冲区,所有PE共享。

关键指标:数据复用率 = 总数据访问次数 / 实际从DRAM读取的数据量。这个值越高,你的加速器越省电。

3.4 带宽优化:别让数据成为瓶颈

带宽优化,说白了就是让数据在正确的时间出现在正确的地方。我总结了几条实战经验:

优化技巧:

  1. 数据预取(Prefetching):计算单元还在算当前数据时,提前把下一批数据加载到缓冲区。我建议用硬件预取器,比软件控制更稳定。
  2. 压缩传输:对权重做量化(比如从FP32降到INT8),带宽需求直接降到1/4。代价是精度损失,但很多场景可以接受。
  3. 乒乓操作(Ping-Pong):两个缓冲区交替工作,一个读一个写,带宽利用率接近100%。
  4. 突发传输(Burst Transfer):尽量用DRAM的突发模式,一次读一整行数据,而不是一个个读。我见过有人用单次读,带宽利用率只有20%。

注意:带宽优化不是越高越好。过高的带宽意味着更大的SRAM和更复杂的控制逻辑,面积和功耗会失控。找到那个“刚刚好”的点,才是高手。

3.5 存储层次结构总览

下面这张图,是我做加速器架构时必画的。它帮你理清数据怎么从DRAM流到PE:

DRAM (片外存储) 高带宽 全局缓冲区 (SRAM,256KB-2MB) 双缓冲/多Bank 广播/分发 PE阵列 (每个PE含本地寄存器) Reg File Reg File ... 数据流方向:DRAM → 全局缓冲区 → PE阵列 复用策略:输入复用 / 权重复用 / 部分和复用 优化手段:预取 + 压缩 + 乒乓 + 突发传输

这张图里,DRAM是水源,全局缓冲区是水库,PE阵列是用水户。水库的大小和输水管道(带宽)决定了用水户能不能吃饱。我每次做架构评估,都会先画这张图,然后标出每个环节的带宽和容量,瓶颈一目了然。

个人经验:有一次我设计的加速器,全局缓冲区只有512KB,结果跑ResNet-50时,数据复用率只有2倍。后来换成1MB,复用率直接到6倍,性能翻倍。嗯,有时候多花点面积在存储上,比堆算力划算得多。

好了,存储层次结构就聊到这儿。记住:数据流设计好了,你的加速器就成功了一半。


专注资料整理