4. 数据复用性与局部性

各位同学,今天我们来聊聊芯片设计中一个绕不开的话题——数据复用性与局部性。说实话,我见过太多团队在架构设计阶段忽略了这一点,结果后期性能怎么调都上不去。我自己就踩过这个坑,所以今天想把这些经验分享给你们。

4.1 时间局部性与空间局部性

先说说两个最基础的概念:时间局部性和空间局部性。你想想看,程序运行时是不是经常反复访问同一块数据?这就是时间局部性。而空间局部性呢,就是访问了某个地址后,大概率会访问它旁边的地址。

举个例子,我在做图像处理芯片时,一个像素点被反复读取做滤波运算。这就是典型的时间局部性。而读取一行像素时,相邻像素也会被加载到缓存中,这就是空间局部性在起作用。

核心要点:

  • 时间局部性:同一数据在短时间内被多次访问
  • 空间局部性:相邻地址的数据被连续访问
  • 两者共同决定了缓存命中率

4.2 输入特征图复用

在卷积神经网络中,输入特征图的复用是个大头。我刚开始做AI芯片时,总觉得这很简单,不就是把数据存好嘛。结果第一次流片回来,性能惨不忍睹。

为什么会这样?因为卷积运算时,同一个输入特征图会被多个卷积核反复使用。如果每次都用从DRAM读,那延迟和功耗都受不了。

我个人习惯的做法是:

  • 把输入特征图按行分块,每块大小刚好能放进SRAM
  • 让多个卷积核共享同一块输入数据
  • 用双缓冲机制,一边计算一边加载下一块

避坑指南:我曾经在某个项目中,把输入特征图分块分得太小了,结果频繁切换块导致缓存抖动。后来调整为每块包含4行数据,性能提升了30%。

4.3 权重复用

权重复用,说白了就是让同一个权重被多个输入数据共享。在卷积层中,一个卷积核会在整个特征图上滑动,权重被反复使用。

我记得有个项目,团队把所有权重都放在片外存储,每次计算都要从DRAM读权重。结果功耗高得吓人,芯片烫得能煎鸡蛋。

正确的做法是:

  1. 把当前层的所有权重预加载到片上SRAM
  2. 在计算过程中,权重只读不写
  3. 等这一层算完了,再加载下一层的权重

这里有个关键点:权重的存储格式。我建议用NHWC格式,这样在读取时能充分利用空间局部性。

4.4 输出部分和复用

输出部分和复用,这个概念可能有些同学不太熟悉。简单说,就是在做卷积时,多个输入通道会累加到同一个输出像素上。如果每次累加都写回DRAM,那效率太低了。

我的做法是:在片上维护一个输出缓冲区,把部分和暂存在里面。等所有输入通道都算完了,再一次性写回DRAM。

注意事项:输出缓冲区的大小要精心设计。太小了,部分和写回太频繁;太大了,片上资源不够用。我一般按输出特征图的一行大小来设计。

4.5 三种复用策略的对比

复用类型 复用对象 典型场景 优化目标
输入特征图复用 输入数据 多卷积核并行 减少DRAM读取次数
权重复用 权重参数 卷积核滑动 降低带宽需求
输出部分和复用 中间计算结果 多通道累加 减少写回次数

4.6 知识体系结构图

数据复用性与局部性知识体系 数据复用性与局部性 时间/空间局部性 输入特征图复用 权重复用 输出部分和复用 同一数据反复访问 相邻地址连续访问 输入数据分块 多卷积核共享 权重预加载到SRAM NHWC存储格式 片上输出缓冲区 多通道部分和累加 核心目标:最大化片上数据复用,减少DRAM访问

4.7 实战经验总结

说了这么多,我给大家总结几条实战经验:

  • 先分析数据流:在设计缓存架构前,先画出数据流图,标出哪些数据被反复使用
  • 合理分配片上资源:输入缓冲区、权重缓冲区、输出缓冲区要平衡分配
  • 注意数据对齐:不对齐的数据访问会浪费带宽,我吃过这个亏
  • 用仿真验证:别光靠理论,跑仿真看看缓存命中率

最后说一句:数据复用性设计得好,芯片性能能翻倍。设计得不好,再牛的算力也白搭。嗯,今天就到这里,大家回去好好消化一下。