3、内存瓶颈根源分析:SRAM、DRAM、Flash的层级结构与访问延迟
做边缘AI的朋友,十有八九都被内存卡过脖子。模型跑不起来,不是算力不够,是数据搬不动。我刚开始做嵌入式AI那会儿,总觉得CPU频率够高就行,结果被内存延迟狠狠上了一课。
今天咱们就掰开揉碎,看看SRAM、DRAM、Flash这三兄弟到底怎么分工,又为什么成了瓶颈。
3.1 为什么要有内存层级?
你想想看,如果只用一种存储器件,会怎样?
用SRAM吧,快是真快,但贵得离谱,1MB就要几十块钱。用DRAM吧,容量上去了,但速度掉下来。用Flash吧,断电不丢数据,可写入慢得像蜗牛。
所以芯片设计者搞了个折中方案——内存层级结构。说白了,就是把快的做小放近处,把慢的做大放远处。
核心原则:越靠近CPU的存储器,速度越快、容量越小、成本越高。
我在项目中遇到过这样一个场景:一个目标检测模型,推理一次要300ms,其中200ms都花在从DDR搬数据上。你说气不气人?
3.2 SRAM:速度之王,容量之殇
SRAM,静态随机存取存储器。名字里带“静态”,是因为它不需要像DRAM那样不断刷新。
结构特点:
- 每个存储单元由6个晶体管组成
- 数据以锁存器形式保存
- 访问速度:1~5ns
为什么快?因为它是全硬件逻辑,没有复杂的刷新周期。CPU的L1、L2缓存,用的就是SRAM。
但问题也很明显:
- 6个晶体管存1bit,面积大
- 片上SRAM通常只有几百KB到几MB
- 成本高,不适合做大容量存储
我的经验:在STM32这类MCU上做AI,SRAM就是命根子。我习惯把模型权重放在Flash,把中间特征图放在SRAM。如果SRAM不够,就得做tiling——把大图切成小块处理。
3.3 DRAM:容量担当,延迟刺客
DRAM,动态随机存取存储器。为什么叫“动态”?因为它需要不断刷新,否则电容里的电荷会漏掉。
结构特点:
- 每个存储单元只有1个晶体管+1个电容
- 数据以电荷形式保存
- 访问速度:50~100ns
你看,比SRAM慢了10~20倍。但容量可以做到GB级别,价格也便宜得多。
DRAM的三大痛点:
- 刷新开销:每64ms要刷新一次,占用带宽
- 行冲突:连续访问不同行时,需要预充电,延迟暴增
- 带宽瓶颈:DDR接口虽然快,但协议开销大
避坑指南:我曾经在RK3588上跑YOLOv5,发现推理时间忽高忽低。查了半天,原来是DDR的自动刷新和推理任务撞车了。后来我把刷新周期调成了手动模式,在推理间隙刷新,问题才解决。
3.4 Flash:断电不丢,写入要命
Flash存储器,咱们常用的NAND Flash和NOR Flash。边缘设备上,它主要存模型参数和固件。
关键参数:
| 类型 | 读延迟 | 写延迟 | 擦除延迟 | 寿命 |
|---|---|---|---|---|
| NOR Flash | ~80ns | ~1μs | ~1s | 10万次 |
| NAND Flash | ~25μs | ~200μs | ~2ms | 1万次 |
看到没?读还行,写和擦除慢得离谱。而且Flash有写入次数限制,不能频繁修改。
在AI场景下的应用:
- 模型参数通常烧录在Flash里
- 推理时,参数从Flash读到DRAM或SRAM
- OTA升级时,要小心擦写次数
我建议:如果模型不大(比如<1MB),可以直接在Flash里执行代码,这叫XIP(eXecute In Place)。省掉加载到RAM的时间,延迟能降不少。
3.5 访问延迟对比:数字会说话
咱们用具体数字感受一下差距。假设CPU主频1GHz,一个时钟周期1ns:
| 存储层级 | 典型延迟 | CPU周期数 | 比喻 |
|---|---|---|---|
| L1 Cache (SRAM) | 1~2ns | 1~2 | 从桌上拿笔 |
| L2 Cache (SRAM) | 5~10ns | 5~10 | 从书架拿书 |
| DDR4 DRAM | 50~100ns | 50~100 | 下楼取快递 |
| NAND Flash | 25~100μs | 25000~100000 | 开车去图书馆 |
嗯,这里要注意:Flash的延迟是SRAM的几万倍。所以做AI推理时,尽量别让CPU直接访问Flash。
3.6 实战:如何绕过内存瓶颈
说了这么多理论,来点实际的。我在做边缘AI项目时,总结了几条经验:
1. 数据本地化
尽量让数据待在离计算单元近的地方。比如用NPU时,把权重提前加载到NPU的本地SRAM里。
// 伪代码示例:提前加载权重到SRAM
void load_weights_to_sram() {
for (int i = 0; i < NUM_LAYERS; i++) {
// 从Flash读到DRAM
memcpy(dram_buffer, flash_addr[i], layer_size[i]);
// 再从DRAM搬到NPU SRAM
npu_load_weights(dram_buffer, sram_addr[i], layer_size[i]);
}
}
2. 流水线预取
别等CPU要数据了才去取。提前把下一批数据准备好,隐藏延迟。
3. 内存对齐
DRAM访问有burst模式,一次读8个字节比读1个字节快不了多少。所以数据结构要按16字节对齐。
我曾经踩过的坑:在某个项目里,我用malloc分配缓冲区,结果地址没对齐。DMA传输时,每次都要做字节对齐处理,性能直接腰斩。后来改用aligned_alloc,问题解决。
4. 避免频繁上下文切换
每次任务切换,缓存都会被污染。在实时AI系统中,我习惯把推理任务绑在特定核上,减少切换。
3.7 小结
内存瓶颈,说白了就是速度、容量、成本三者不可兼得。SRAM快但小,DRAM大但慢,Flash持久但更慢。
做边缘AI,你得学会在三个层级间跳舞。把最常用的数据放SRAM,中等常用的放DRAM,不常用的放Flash。再配合预取、对齐、流水线这些技巧,才能把硬件性能榨干。
下一章,咱们聊聊模型量化——怎么把32位浮点模型压成8位甚至4位,让内存占用直接砍半。到时候你会发现,原来瓶颈是可以“绕”过去的。