3. 内存基础概念回顾:SRAM vs DRAM、Cache层级、DMA传输、内存对齐

好,咱们正式开始第三讲。在深入BEV模型部署之前,我觉得有必要先把内存这块儿的基础打牢。你想想看,嵌入式部署说白了就是跟内存打交道——模型参数放哪儿、中间结果存哪儿、怎么搬数据最快。这些搞不清楚,后面优化无从谈起。

我个人习惯,每次接手一个新项目,第一件事就是翻芯片手册里的内存架构图。别嫌麻烦,这步省了,后面调试能让你怀疑人生。

3.1 SRAM vs DRAM:速度与容量的博弈

先说说最基础的。SRAM和DRAM,名字就差一个字母,脾气可差远了。

SRAM(静态随机存取存储器)

  • 用触发器存数据,只要不断电,数据就不丢
  • 速度快,跟CPU几乎同频
  • 但贵啊,一个bit要6个晶体管,面积大得吓人
  • 嵌入式芯片里通常只有几百KB到几MB

DRAM(动态随机存取存储器)

  • 靠电容存数据,电容会漏电,得定期刷新
  • 速度慢一些,但容量大,成本低
  • 一个bit只要1个晶体管加1个电容
  • 咱们常说的DDR4、LPDDR5,都是DRAM

核心区别一句话:SRAM是跑车,快但装不了多少东西;DRAM是卡车,能装但跑得慢。

我在项目中遇到过这么个事:有个同事把模型的所有权重都放在SRAM里,结果编译直接报内存不足。后来一查,模型30MB,SRAM才2MB。嗯,这就是没搞清楚SRAM和DRAM的区别。

特性 SRAM DRAM
速度 几纳秒 几十纳秒
容量 KB~MB级 MB~GB级
成本
功耗 较低 较高(需刷新)
典型用途 Cache、片上内存 主存、显存

实战建议:BEV模型部署时,把最频繁访问的层(比如前几层卷积)的权重放在SRAM里,剩下的放DRAM。这叫「热数据驻留」,能省不少带宽。

3.2 Cache层级:为什么你的程序跑得慢?

好,接下来聊Cache。说白了,Cache就是CPU和主存之间的「缓冲带」。

为什么会需要Cache?因为CPU太快了,DRAM太慢了。CPU执行一条指令可能只要0.3纳秒,但从DRAM读数据要几十纳秒。这差距,好比让博尔特等公交车。

典型的嵌入式芯片Cache分三级:

  • L1 Cache:分指令Cache和数据Cache,一般各32KB。速度跟CPU一样快。
  • L2 Cache:统一缓存,256KB~2MB。稍微慢点,但容量大。
  • L3 Cache:有些芯片有,多核共享,几MB到十几MB。

我记得有一次调一个BEV模型的前向推理,发现某个算子执行时间忽高忽低。查了半天,原来是数据在Cache里「颠簸」——每次访问都miss,然后去DRAM搬数据。后来我把数据排布改了一下,让访问模式更连续,性能直接翻倍。

避坑指南:我曾经以为Cache越大越好,后来发现不是这么回事。Cache太大,命中延迟也会增加。而且多核共享的L3 Cache,一个核把数据挤出去,另一个核就得重新加载。这就是所谓的「Cache污染」。

你想想看,BEV模型里那么多特征图,如果每个特征图的访问都不在Cache里,那性能能好才怪。所以后面我们会专门讲「Cache友好型」的数据排布策略。

3.3 DMA传输:别让CPU干苦力

DMA,全称Direct Memory Access,直接内存访问。这玩意儿,说白了就是让硬件自己搬数据,CPU只管发号施令。

没有DMA的时候,CPU得亲自把数据从A搬到B。这就像老板亲自搬砖,效率极低。有了DMA,CPU说「把这块数据从DRAM搬到SRAM」,然后就可以去干别的活了,DMA控制器自己搞定。

在BEV模型部署中,DMA特别重要。因为模型推理时,数据要在DRAM和SRAM之间来回倒腾。如果没有DMA,CPU得花大量时间在数据搬运上,真正算数的时间反而少了。

关键点:DMA传输可以跟CPU计算「流水线并行」。比如CPU在算第N层的时候,DMA已经在搬第N+1层的数据了。这叫「双缓冲」或「乒乓缓冲」。

我建议你在设计推理框架时,一定要把DMA传输和计算重叠起来。具体怎么做?后面章节会详细讲,这里先记住一个原则:不要让CPU等数据,让数据等CPU

3.4 内存对齐:看似小事,实则大事

最后聊内存对齐。这玩意儿,新手往往不重视,但老手都知道它的厉害。

什么叫内存对齐?简单说,就是数据在内存里的起始地址,必须是某个值的整数倍。比如4字节对齐,地址就得是4的倍数;8字节对齐,地址就得是8的倍数。

为什么需要对齐?因为CPU读内存不是按字节读的,而是按「字」读的。32位CPU一次读4字节,64位CPU一次读8字节。如果数据没对齐,CPU可能得读两次才能拿到完整数据,性能直接腰斩。

举个例子:

// 没对齐的情况
struct __attribute__((packed)) {
    char a;    // 1字节
    int b;     // 4字节,但起始地址是1,不是4的倍数
} misaligned;

// 对齐的情况
struct {
    char a;    // 1字节
    char pad[3]; // 填充3字节,让b对齐
    int b;     // 4字节,起始地址是4的倍数
} aligned;

在BEV模型中,特征图的通道数、宽度、高度,这些维度如果不对齐,访问效率会大打折扣。我见过一个案例,把特征图的宽度从31改成32(对齐到4的倍数),推理速度提升了15%。

实战技巧:在分配模型权重和特征图内存时,尽量对齐到Cache Line的大小(通常是64字节)。这样能最大化Cache利用率,减少Cache Miss。

嗯,内存对齐这块,看似是编译器的事,但在嵌入式部署中,我们经常要手动控制。因为编译器不知道你的硬件特性,它只能做通用优化。而咱们做部署的,就是要压榨出每一分性能。

小结

这一章我们聊了四个基础概念:

  • SRAM和DRAM,一个快一个容量大,要合理搭配使用
  • Cache层级,理解它才能写出高性能代码
  • DMA传输,让CPU从数据搬运中解放出来
  • 内存对齐,小细节决定大性能

这些概念,每个都值得你花时间去理解。别觉得基础,我做了这么多年部署,每次遇到性能瓶颈,回头一看,往往就是这些基础问题没处理好。下一章,我们开始讲BEV模型的内存布局,那才是真正考验功底的地方。

一句话总结:内存管理不是锦上添花,而是雪中送炭。基础打牢了,后面的优化才能事半功倍。