4、数据流图优化:图优化策略、常量折叠与算子融合、内存布局优化、数据流深度分析
好,咱们进入第四章。这一章我打算聊点实在的——数据流图优化。
你想想看,FINN 生成的网络,说白了就是一张巨大的数据流图。节点是算子,边是数据流动。但默认生成的图,往往不是最高效的。我见过太多人,模型跑起来性能差,第一反应是去调硬件参数,结果折腾半天没效果。其实问题很可能出在图本身——图没优化好,硬件再强也白搭。
4.1 图优化策略:从“能跑”到“跑得快”
图优化,我的理解就是“给计算图做减法”。
举个例子。你写了个表达式 a = b + 0,这有意义吗?没有。但编译器不会自动帮你删掉,它老老实实算。FINN 也一样,默认生成的图里,这种冗余操作比比皆是。
我个人习惯,拿到一个 FINN 模型后,第一件事就是跑一遍图优化 pass。常用的策略有这么几个:
- 死代码消除:删掉那些输出不被任何地方使用的节点。我在项目中遇到过,有些调试用的节点忘了删,白白浪费资源。
- 公共子表达式消除:如果两个节点算的是同一个东西,合并成一个。比如两个卷积层用了相同的权重,完全可以共享。
- 恒等节点消除:像
Identity、Reshape这种不改变数据的节点,能删就删。
finn.core.transformations 里的 RemoveIdentityOps 和 EliminateDeadNodes。我一般会写个脚本,把常用的优化 pass 串起来,一键执行。
4.2 常量折叠与算子融合:把“计算”变成“查表”
常量折叠,说白了就是“能提前算的,绝不拖到运行时”。
比如 y = 2 * 3 * x,你完全可以在编译时把 2 * 3 算成 6,然后直接生成 y = 6 * x。FINN 里,BatchNorm 的 gamma、beta 这些参数,如果前面接了常量节点,都可以折叠进卷积的权重里。
算子融合就更关键了。我举个例子:
# 原始图:Conv -> BatchNorm -> ReLU
# 融合后:ConvBNReLU(一个算子搞定)
为什么要融合?因为每多一个算子,就多一次数据搬运、多一次中间结果的存储。在 FPGA 上,这直接意味着更多的 BRAM 和 DSP 消耗。融合之后,数据流更紧凑,延迟更低。
我曾经在一个项目中,把 Conv + BN + ReLU 融合后,资源占用直接降了 30%。嗯,这里要注意,不是所有算子都能随便融合。你得看数据依赖关系,看能不能保持数值精度。FINN 提供了 FuseConsecutiveOps 这个 pass,但用之前最好自己检查一下。
4.3 内存布局优化:别让数据“堵车”
FPGA 上的内存,和 CPU 上的完全不一样。CPU 有缓存,有虚拟内存,你不太用操心数据怎么放。但 FPGA 上,你得手动管。
FINN 默认的内存布局,往往是按“通道优先”或者“行优先”来排的。但实际跑起来,你会发现数据访问模式决定了布局方式。
举个例子。一个卷积层,如果输入是 H x W x C,输出也是 H x W x C。但中间的计算,可能需要把数据重排成 C x H x W。为什么?因为 FPGA 的 PE(处理单元)通常按通道并行计算,通道维度的数据需要连续存放。
我建议你重点关注这几个点:
- 数据分块:把大张量切成小块,让每个块能塞进 BRAM。块的大小要跟 PE 的并行度匹配。
- 数据重排:用
Transpose或Reshape节点,把数据排成硬件喜欢的格式。 - 双缓冲:用两个 buffer 交替读写,隐藏数据搬运的延迟。这个在 FINN 的
StreamingDataflowPartition里可以配置。
4.4 数据流深度分析:找到瓶颈,然后干掉它
数据流深度,指的是从输入到输出,数据经过了多少个算子。深度越大,延迟越高,资源消耗也越大。
怎么分析?我一般用 FINN 自带的 DataflowDepthAnalysis 工具。它会生成一张图,标出每个节点的深度。然后你一眼就能看出,哪条路径是“最长的腿”。
举个例子。假设你有一个网络,输入 -> Conv1 -> Conv2 -> Conv3 -> 输出,深度是 3。但如果你在 Conv1 和 Conv2 之间插了一个很大的 Pooling 层,深度可能变成 4。这个 Pooling 层有没有必要?能不能跟 Conv 融合?
我遇到过最夸张的一次,一个网络的深度是 12,但实际只需要 8。中间有 4 层是冗余的 Reshape 和 Transpose。删掉之后,延迟直接降了 40%。
分析的时候,我建议你关注这几个指标:
| 指标 | 含义 | 优化方向 |
|---|---|---|
| 最大深度 | 最长路径的算子数量 | 减少冗余节点,融合算子 |
| 平均深度 | 所有路径的平均长度 | 平衡各路径,避免某条路径过长 |
| 深度方差 | 路径长度的离散程度 | 如果方差大,说明负载不均衡 |
好了,这一章的内容就这些。图优化是个细活,急不得。你慢慢来,多跑几次分析,多试几种组合,总能找到最优解。下一章,咱们聊聊量化感知训练和精度调优,那个更有意思。