3. 数据流核心概念:时间数据流与空间数据流的定义与区别

好,咱们进入正题。这一节要聊的,是整个Eyeriss数据流映射里最基础、也最容易搞混的两个概念——时间数据流和空间数据流。

我刚开始接触数据流映射时,也在这上面栽过跟头。当时看,满篇的“temporal”和“spatial”,觉得不就是时间跟空间嘛,有啥难理解的?结果一动手做映射,发现根本不是那么回事。你想想看,一个数据在同一个时钟周期里被多个PE用了,这算时间还是空间?一个数据在不同时钟周期被同一个PE反复用,这又算什么?

嗯,别急。咱们先把定义掰扯清楚。

3.1 时间数据流:数据在时间维度上的复用

时间数据流,说白了就是同一个数据,在不同时间点被同一个处理单元(PE)重复使用。数据在时间轴上“流动”,PE在多个时钟周期里反复读取它。

举个例子,你在做卷积时,一个输入像素要被同一个卷积核反复乘很多次。如果这个像素被加载到PE的寄存器里,然后PE在每个时钟周期都拿它跟不同的权重做乘加——这就是典型的时间数据流。

核心特征:

  • 数据在同一个PE内部被多次使用
  • 使用发生在不同的时钟周期
  • 依赖PE内部的存储(寄存器、局部缓存)来保持数据
  • 减少对全局存储的访问带宽需求

我在做第一版Eyeriss原型时,就吃过时间数据流的亏。当时为了省带宽,我把输入特征图的数据在PE里反复用了32次,结果发现PE内部的寄存器堆根本不够用,数据被挤到局部SRAM里,延迟一下子飙了上去。后来我学乖了——时间复用不是越多越好,得看PE的本地存储能装下多少。

3.2 空间数据流:数据在空间维度上的广播

空间数据流,则是同一个数据,在同一个时间点被多个不同的处理单元(PE)同时使用。数据在空间上“广播”,多个PE在同一时钟周期里拿到相同的数据。

还是拿卷积举例。一个输入像素,如果同时被送到多个PE里,每个PE拿它跟不同的权重做乘加——这就是空间数据流。说白了,就是“一份数据,多份拷贝,同时计算”。

核心特征:

  • 数据在多个PE之间被同时使用
  • 使用发生在同一个时钟周期
  • 依赖数据分发网络(广播总线、多播网络)来传递数据
  • 减少对PE本地存储的容量需求

空间数据流的好处是延迟低——数据一到,所有PE立刻开算。但代价也很明显:你得有足够宽的数据分发总线,否则广播会成为瓶颈。我记得有一次做仿真,空间复用因子设到16,结果总线拥堵导致PE有一半时间在空等。嗯,那感觉就像高速公路上所有车都挤在同一个出口下。

3.3 两者的本质区别

咱们用一张表把区别说清楚:

对比维度 时间数据流 空间数据流
复用对象 同一个PE 多个PE
复用时刻 不同时钟周期 同一时钟周期
存储依赖 PE本地存储(寄存器/缓存) 数据分发网络(总线/多播)
带宽影响 降低全局带宽需求 增加分发网络带宽需求
延迟特征 较高(数据需多次读取) 较低(数据一次到位)
典型场景 权重在PE内复用 输入特征图广播到多个PE

你可能会问:那到底哪个更好?

我的回答是:没有哪个更好,只有哪个更合适。Eyeriss的精髓,恰恰在于把时间数据流和空间数据流混合使用,取长补短。

3.4 混合数据流:Eyeriss的核心思路

Eyeriss不是单纯用时间或空间数据流,而是把两者结合起来。具体来说:

  • 行间(Row-wise):输入特征图的行数据在PE阵列里做空间广播,同一行数据同时送到多个PE
  • 行内(Within-row):同一行内的权重在PE内部做时间复用,一个PE反复使用同一组权重

这样做的好处很明显:

  1. 空间广播减少了PE本地存储的压力——你不用在每个PE里都存一份完整的输入特征图
  2. 时间复用降低了全局带宽——权重在PE内部反复用,不用频繁从全局缓存搬数据
  3. 两者配合,让存储层次和计算单元都能高效运转

我的经验: 在做数据流映射时,我习惯先确定瓶颈在哪。如果带宽是瓶颈,就多用时间复用;如果PE本地存储是瓶颈,就多用空间广播。Eyeriss的混合数据流,本质上就是在带宽和存储之间找一个平衡点。

3.5 一张图看懂时间 vs 空间数据流

下面这张SVG图,把时间数据流和空间数据流的区别画得清清楚楚:

时间数据流 vs 空间数据流 时间数据流 vs 空间数据流 时间数据流 PE 数据输入 t1 t2 t3 t4 D D D D 同一数据D,在不同时间点 被同一个PE重复使用 空间数据流 数据源(广播) PE 0 PE 1 PE 2 D D D 同一时钟周期 同一数据D,在同一时间点 被多个PE同时使用

注意: 千万别把时间数据流和空间数据流割裂开来看。实际芯片里,两者是同时存在的。一个数据可能在同一个时钟周期被多个PE用(空间),然后在接下来的几个周期里又被同一个PE反复用(时间)。你设计数据流映射时,得同时考虑这两个维度。

3.6 避坑指南:我踩过的几个坑

最后,分享几个我实际项目中遇到的坑,希望能帮你少走弯路:

  • 坑一:过度追求时间复用。我曾经在一个项目里,把权重在PE内复用了64次,结果PE的寄存器堆爆了,数据不得不频繁换入换出,延迟反而比不复用还高。记住:时间复用的上限,由PE本地存储容量决定。
  • 坑二:忽视空间广播的功耗。空间数据流虽然延迟低,但广播网络的功耗不容小觑。数据每多广播到一个PE,总线上就要多驱动一个负载。我见过一个设计,空间复用因子从4提到8,功耗直接翻了1.7倍。
  • 坑三:混淆“时间”和“空间”的边界。有些数据流,数据在PE阵列里逐级传递(比如脉动阵列),每个PE在不同时间点拿到数据。这算时间还是空间?严格来说,这是时间数据流,因为数据在不同时间点被不同PE使用。别被“多个PE”这个表象骗了。