第四讲:数据流与脉动阵列——算子映射的实战心法
各位同学,今天我们来聊聊存算一体架构里最核心的实战问题——数据流与脉动阵列。说实话,我当年刚接触这个领域时,觉得脉动阵列就是个高大上的数学概念,直到亲手调过几个算子映射,才明白这东西有多接地气。
4.1 三种数据流类型:你选对了吗?
先问个问题:一个卷积层,权重、输入、输出,哪个先动?
答案不同,映射策略天差地别。我习惯把数据流分成三类,咱们一个个说。
4.1.1 权重固定(Weight Stationary)
说白了,就是权重待在PE里不动,输入数据流过来,输出结果流出去。这种模式我最常用,为什么?
- 权重复用率高:同一个权重可以跟多个输入做乘加,适合卷积核共享的场景
- 存储开销小:权重只加载一次,后续不用反复搬
- 典型场景:卷积层、全连接层
我的经验:在某个边缘AI芯片项目中,我们用权重固定映射,把ResNet-50的卷积层功耗降了40%。但要注意——如果网络层权重频繁变化(比如动态卷积),这种模式反而会成瓶颈。
4.1.2 输入固定(Input Stationary)
输入数据待在PE里,权重流进来。这种模式适合什么情况?
- 输入数据复用:同一个输入特征图,要跟多个卷积核做运算
- 适合深度可分离卷积:逐通道卷积时,输入固定能减少数据搬移
避坑指南:我曾经在部署MobileNet时,一开始用了权重固定,结果发现输入数据反复加载,功耗反而高了。后来换成输入固定,效果立竿见影。所以,别死磕一种模式,得看网络结构。
4.1.3 输出固定(Output Stationary)
输出结果累积在PE里,权重和输入都流进来。这种模式我一般用在需要大量累加的场景。
- 减少写回次数:部分和直接在PE里累加,最后写回一次
- 适合大卷积核:比如7x7卷积,输出固定能避免频繁的中间结果写回
| 数据流类型 | 固定元素 | 流动元素 | 典型场景 |
|---|---|---|---|
| 权重固定 | 权重 | 输入、输出 | 标准卷积、全连接 |
| 输入固定 | 输入 | 权重、输出 | 深度可分离卷积 |
| 输出固定 | 输出 | 权重、输入 | 大卷积核、矩阵乘法 |
4.2 脉动阵列映射原理:数据怎么“流”起来?
脉动阵列,你想想看,就像一条流水线。数据在PE之间“脉动”传递,每个PE只做一件事——乘加。但怎么映射到实际硬件上?
我画个图帮你理解。假设一个4x4的脉动阵列,要计算一个3x3的卷积。
看到没?输入数据从左往右流,权重从上往下流。每个PE收到输入和权重后,做一次乘加,然后把结果往右传。这就是脉动阵列的精髓——数据像心跳一样,有节奏地流动。
注意:脉动阵列不是万能的。我见过有人硬要把不规则算子往脉动阵列上塞,结果利用率不到30%。记住,脉动阵列适合规则、密集的计算模式。
4.3 数据复用策略:别让数据“空跑”
数据复用,说白了就是让同一份数据被多个PE用上,避免反复从存储器搬。我总结了三板斧。
4.3.1 空间复用
同一时刻,多个PE用同一份数据。比如权重固定模式下,一个权重广播给整行PE。我在项目中常用这种,简单粗暴有效。
4.3.2 时间复用
不同时刻,同一个PE用同一份数据。比如输入固定模式下,输入数据在PE里待着,等不同的权重流过来。
4.3.3 混合复用
空间+时间一起上。举个例子,卷积核在空间上广播,输入在时间上复用。这种策略最灵活,但调度也最复杂。
我的小技巧:设计数据复用策略时,先画个数据流图。把每个数据元素的生命周期标出来,看看它被用了多少次。如果复用次数少于2,那基本就是白费功夫。
4.4 实战:如何选择数据流?
好了,理论说完了,咱们来点实际的。假设你要部署一个3x3卷积,输入是32x32,输出通道64。你会选哪种数据流?
我个人的选择逻辑是这样的:
- 先看权重复用度:如果同一个权重被多个输入复用(比如卷积核共享),优先权重固定
- 再看输入复用度:如果同一个输入要跟多个权重做运算(比如深度可分离),优先输入固定
- 最后看累加次数:如果累加次数多(比如大卷积核),优先输出固定
举个例子,3x3卷积,权重复用度是9(每个权重被9个输入复用),输入复用度是9(每个输入被9个权重复用)。这时候,权重固定和输入固定都可以。我一般选权重固定,因为权重加载一次后就不用动了,调度简单。
避坑指南:我曾经在一个项目中,为了追求极致性能,选了混合复用策略。结果调度逻辑复杂到爆,最后性能没提升多少,反而多花了两周调试。所以,别过度设计,简单够用就行。
嗯,数据流与脉动阵列这块,说白了就是找到数据流动的节奏。你想想看,数据在PE之间流动,就像乐队演奏一样,每个PE都是乐手,数据就是乐谱。节奏对了,演奏就流畅;节奏乱了,全是噪音。
希望今天的分享对你有帮助。记住,多动手,多画图,多调参。纸上得来终觉浅,绝知此事要躬行。
想要更多资料,联系可可