第9章:数据流架构原理

在前面的章节中,我们深入探讨了以TPU为代表的脉动阵列架构,其通过规则的数据流动模式和高度优化的矩阵运算单元实现了卓越的能效比。然而,随着AI模型日益复杂化和多样化,特别是在自动驾驶和具身智能场景中,工作负载呈现出更加动态和异构的特征。本章将介绍另一种重要的NPU架构范式——数据流架构,并以Groq的Tensor Streaming Processor (TSP)为例,分析其如何通过编译时确定性调度和片上大容量存储实现极低延迟和可预测的性能。通过对比数据流架构与脉动阵列的设计理念差异,读者将能够根据具体应用场景选择合适的架构方案。

数据流架构的核心价值在于其能够暴露计算的内在并行性,消除传统冯·诺依曼架构中的控制流瓶颈。在自动驾驶场景中,感知、预测和规划任务往往具有不同的计算特征和实时性要求,数据流架构能够通过细粒度的资源调度和确定性执行保证满足这些异构需求。对于具身智能应用,VLM/VLA模型需要处理多模态输入并产生实时控制输出,数据流架构的灵活性和低延迟特性使其成为理想选择。

本章将从数据流计算的基础理论出发,深入分析其执行机制和优化方法,然后详细介绍Groq TSP的创新设计,最后通过与脉动阵列的全面对比,帮助读者建立选择和设计数据流架构NPU的能力。

9.1 数据流计算模型

数据流计算模型起源于20世纪70年代,其核心思想是将计算表示为有向图,数据在图中的节点间流动并触发计算。与传统的冯·诺依曼架构依赖程序计数器顺序执行不同,数据流架构中的操作仅在其输入数据就绪时执行,天然支持细粒度并行。这一范式的理论基础可以追溯到Petri网和Kahn过程网络,它们为并发计算提供了严格的数学模型。

数据流计算的历史演进反映了计算机体系结构对并行性的不断追求。早期的MIT Tagged-Token Dataflow Architecture (1970s)和Manchester Dataflow Computer (1980s)奠定了理论基础,虽然受限于当时的工艺技术未能商业化成功,但其思想深刻影响了后续的并行计算架构。进入21世纪,随着晶体管密度的持续增长和功耗墙的出现,数据流思想在专用加速器领域重新焕发生机,Google的TPU采用了脉动阵列这一特殊的数据流形式,而Groq的TSP则代表了纯数据流架构的现代实现。

这种计算范式的革命性在于它从根本上改变了程序执行的控制方式。在冯·诺依曼架构中,指令的执行顺序由程序计数器严格控制,即使两条指令之间没有数据依赖关系,它们也必须按照程序顺序执行。这种顺序执行模型可以用状态转移函数表示:

$$S_{t+1} = f(S_t, I_t)$$ 其中$S_t$是时刻$t$的机器状态,$I_t$是当前指令。而在数据流架构中,执行模型可以表示为: $$O_i = g_i(I_{i,1}, I_{i,2}, ..., I_{i,k})$$ 其中操作$i$的输出$O_i$仅依赖于其输入集合$\{I_{i,j}\}$,与全局状态无关。这种无状态计算模型消除了指令间的人为顺序约束,使得所有数据独立的操作可以并发执行。

指令级并行度(ILP)的理论上界由数据依赖图的宽度决定。设图$G=(V,E)$的反链(antichain)集合为$\mathcal{A}$,则最大并行度: $$ILP_{max} = \max_{A \in \mathcal{A}} |A|$$ 其中反链是指图中两两之间不存在路径的节点集合。这个理论上界在数据流架构中可以充分利用,而在传统架构中由于控制流约束通常只能达到其一小部分。

为了更深入理解这个概念,考虑一个实际的神经网络层的并行度分析。对于一个Transformer的Multi-Head Attention层,假设有8个注意力头,每个头的计算包括Q、K、V投影和注意力计算。在传统架构中,这些计算通常顺序执行或受限于指令发射宽度(如4-8条指令/周期)。而在数据流架构中,理论上8个头的24个投影矩阵乘法可以同时执行,实际并行度仅受硬件资源限制。数学上,若每个投影需要$d_{model} \times d_k$次乘累加操作,总并行度可达: $$P_{MHA} = 8 \times 3 \times d_{model} \times d_k = 24 \times 512 \times 64 = 786,432$$ 这种大规模并行在数据流架构中通过合理的资源分配和调度可以接近实现。

数据流计算模型的另一个重要优势是其固有的确定性。由于执行顺序完全由数据依赖关系决定,相同的输入总是产生相同的执行序列和输出结果。这种确定性可以形式化为幂等性质: $$\forall x \in X: f^n(x) = f(x), n \geq 1$$ 这种确定性对于安全关键应用(如自动驾驶)至关重要,因为它简化了系统验证和认证过程。形式化验证的状态空间从指数级$O(2^{|S|})$降低到多项式级$O(|V| \cdot |E|)$,其中$|S|$是状态空间大小,$|V|$和$|E|$分别是节点和边的数量。此外,确定性执行还带来了功耗优化的机会,因为编译器可以精确预测每个单元的活动时间,从而实现激进的时钟门控和功耗管理策略。

确定性的价值在自动驾驶场景中尤为突出。考虑一个紧急制动场景,系统必须在检测到障碍物后的100ms内做出反应。在非确定性系统中,由于缓存未命中、DRAM刷新、操作系统调度等因素,同样的感知-决策-控制流程可能在50-150ms之间变化。这种不确定性要求系统按最坏情况设计,导致过度保守。而数据流架构的确定性执行可以保证: $$T_{response} = T_{sensor} + T_{perception} + T_{planning} + T_{control} = 10 + 40 + 30 + 20 = 100ms \pm 0.1ms$$ 这种亚毫秒级的抖动对于满足ISO 26262等功能安全标准至关重要。

9.1.1 数据流图基础

数据流图(Dataflow Graph, DFG)是数据流架构的核心抽象,由节点(nodes)和边(edges)组成。形式化定义为有向图$G = (V, E, \phi, \psi)$,其中:

  • $V$是节点集合,表示计算操作
  • $E \subseteq V \times V$是有向边集合,表示数据依赖
  • $\phi: V \rightarrow \mathcal{F}$将节点映射到函数空间
  • $\psi: E \rightarrow \mathcal{T}$将边映射到数据类型

这种图表示方法不仅直观地展示了计算的结构,更重要的是它精确地定义了执行语义,使得硬件实现和编译器优化都有明确的目标。

数据流图的表示能力是其被广泛采用的关键原因。从理论角度看,数据流图是图灵完备的,能够表示任意可计算函数。更重要的是,它天然地暴露了计算中的并行机会。考虑一个简单的向量点积运算$c = \sum_{i=1}^{n} a_i \times b_i$,在传统指令流中表现为循环,而在数据流图中展开为n个独立的乘法节点和一个归约树,立即显示出$O(n)$的数据并行和$O(\log n)$的归约并行。这种显式的并行表示使得编译器和硬件都能够更有效地利用可用资源。

节点定义与分类

节点是数据流图的基本计算单元,每个节点$v \in V$封装了一个特定的操作。节点的形式化定义为元组: $$v = (id, op, I, O, \tau, \sigma)$$ 其中:

  • $id$:节点唯一标识符
  • $op \in \mathcal{F}$:节点执行的操作函数
  • $I = \{i_1, i_2, ..., i_m\}$:输入端口集合
  • $O = \{o_1, o_2, ..., o_n\}$:输出端口集合
  • $\tau: I \cup O \rightarrow \mathcal{T}$:端口类型映射
  • $\sigma$:节点状态(对于有状态节点)

根据功能和复杂度,节点可以分为多个层次:

  1. 原子节点(Atomic Nodes):执行不可分割的基本操作 - 算术节点:$f_{arith}: \mathbb{R}^n \rightarrow \mathbb{R}$ - 逻辑节点:$f_{logic}: \mathbb{B}^n \rightarrow \mathbb{B}$ - 存储节点:$f_{mem}: Addr \times Data \rightarrow Data$

  2. 复合节点(Composite Nodes):由多个原子操作组成 - SIMD节点:$f_{simd}: \mathbb{R}^{n \times k} \rightarrow \mathbb{R}^{m \times k}$ - 张量节点:$f_{tensor}: \mathbb{R}^{d_1 \times ... \times d_n} \rightarrow \mathbb{R}^{e_1 \times ... \times e_m}$

  3. 算子节点(Operator Nodes):深度学习专用操作 - 卷积节点:$f_{conv}: \mathbb{R}^{B \times C \times H \times W} \times \mathbb{R}^{K \times C \times R \times S} \rightarrow \mathbb{R}^{B \times K \times H' \times W'}$ - 注意力节点:$f_{attn}: \mathbb{R}^{B \times N \times D} \rightarrow \mathbb{R}^{B \times N \times D}$

  4. 控制节点(Control Nodes):管理执行流 - 条件节点:$f_{cond}: \mathbb{B} \times T \times T \rightarrow T$ - 循环节点:$f_{loop}: (S \rightarrow S \times \mathbb{B}) \times S \rightarrow S$

节点层次的设计反映了硬件实现和编程抽象之间的权衡。在自动驾驶场景中,感知网络主要使用算子节点(如PointPillars的3D卷积),而规划算法可能需要更多控制节点(如A*搜索的条件分支)。具身智能的VLA模型则混合使用各层次节点,视觉编码器使用算子节点,语言模型使用复合节点,动作生成使用控制节点。这种层次化设计允许编译器针对不同节点类型采用专门的优化策略。

每个节点的内部结构包含四个关键组件:

  1. 输入端口(Input Ports):接收操作数,每个端口$i_j$定义为: $$i_j = (type_j, width_j, rate_j, policy_j)$$
  • $type_j \in \{scalar, vector, tensor\}$:数据结构类型
  • $width_j \in \{8, 16, 32, 64\}$:位宽
  • $rate_j \in \mathbb{Q}^+$:消费速率(令牌/周期)
  • $policy_j \in \{blocking, non\text{-}blocking\}$:阻塞策略
  1. 输出端口(Output Ports):产生结果,支持多播 $$o_k = (type_k, width_k, rate_k, fanout_k)$$
  • $fanout_k \in \mathbb{N}$:扇出度,表示连接的下游节点数
  1. 触发条件(Firing Rule):定义激活逻辑 $$fire(v) = \begin{cases} true & \text{if } \forall i_j \in I: available(i_j) \geq required(i_j) \\ false & \text{otherwise} \end{cases}$$

  2. 计算函数(Compute Function):定义操作语义 $$f_v: \prod_{j=1}^{|I|} \mathcal{T}(i_j) \rightarrow \prod_{k=1}^{|O|} \mathcal{T}(o_k)$$ 延迟模型:$d(v) = d_{fixed} + d_{variable}(|data|)$

节点的粒度选择是设计中的关键权衡,可以通过成本模型量化: $$Cost(g) = \alpha \cdot Complexity(g) + \beta \cdot Overhead(g) - \gamma \cdot Parallelism(g)$$ 其中:

  • $Complexity(g) = |V_g| + |E_g|$:图复杂度
  • $Overhead(g) = \sum_{e \in E_g} latency(e) + \sum_{v \in V_g} schedule_cost(v)$:调度开销
  • $Parallelism(g) = \frac{\sum_{v \in V_g} work(v)}{critical_path(g)}$:可获得的并行度

实际设计中,粒度选择还需考虑目标硬件的特性。以200 TOPS的NPU设计为例,假设有1000个MAC单元,每个运行在1GHz。若采用细粒度设计(每个MAC作为一个节点),图规模达到$10^6$节点级别,编译时间可能超过小时。若采用粗粒度设计(整个矩阵乘法作为节点),虽然图规模降到$10^2$级别,但失去了细粒度调度的灵活性。实践中的最优选择通常是中等粒度,如将$32 \times 32$的矩阵块作为基本节点,这样既保持了合理的图规模($10^3-10^4$节点),又能有效利用硬件并行性。

细粒度节点(如单个乘法器):

  • 优势:$Parallelism_{fine} = O(n^2)$对于$n \times n$矩阵乘法
  • 劣势:$Overhead_{fine} = O(n^3)$的调度复杂度

粗粒度节点(如整个卷积层):

  • 优势:$Overhead_{coarse} = O(1)$的调度复杂度
  • 劣势:$Parallelism_{coarse} = O(1)$受限于节点内部并行

现代数据流架构通常采用层次化的节点设计,通过多级粒度优化总成本: $$g_{optimal} = \arg\min_g Cost(g) \text{ s.t. } Resources(g) \leq R_{available}$$ 边的属性与语义

边不仅仅是简单的连线,它承载了丰富的语义信息。每条边$e = (u, v) \in E$定义了一个生产者-消费者关系,其形式化定义为: $$e = (src, dst, type, capacity, latency, policy)$$ 其中:

  • $src, dst \in V$:源节点和目标节点
  • $type \in \mathcal{T}$:传输的数据类型
  • $capacity \in \mathbb{N} \cup \{\infty\}$:缓冲容量
  • $latency \in \mathbb{R}^+$:传输延迟
  • $policy \in \{FIFO, LIFO, priority\}$:调度策略

类型系统设计

数据类型系统的复杂度影响硬件开销和编译效率。静态类型系统的类型检查复杂度为$O(|E|)$,而动态类型系统需要运行时开销: $$Overhead_{dynamic} = \sum_{e \in E} (metadata_size(e) \times frequency(e))$$ 类型推断可以通过Hindley-Milner算法实现,复杂度为$O(n \cdot \alpha(n))$,其中$\alpha$是逆Ackermann函数。

缓冲深度优化

缓冲深度$B_e$的选择需要平衡性能和面积: $$B_{optimal} = \arg\min_B [Area(B) + \lambda \cdot Latency(B)]$$ 其中延迟模型: $$Latency(B) = \begin{cases} 0 & \text{if } B \geq \Delta_{rate} \times T_{burst} \\ \frac{\Delta_{rate} \times T_{burst} - B}{throughput} & \text{otherwise} \end{cases}$$ $\Delta_{rate} = |production_rate - consumption_rate|$是速率差异。

传输延迟分析

端到端延迟包含多个组成部分: $$L_{e2e} = L_{prop} + L_{trans} + L_{queue} + L_{proc}$$ 其中:

  • $L_{prop} = \frac{distance}{c_{signal}}$:物理传播延迟
  • $L_{trans} = \frac{data_size}{bandwidth}$:传输延迟
  • $L_{queue} = \frac{\lambda}{\mu(\mu - \lambda)}$:排队延迟(M/M/1模型)
  • $L_{proc}$:处理延迟

在片上网络中,Manhattan距离$d = |x_1 - x_2| + |y_1 - y_2|$决定了跳数,每跳延迟约1-2个周期。

延迟优化在实时系统中至关重要。以自动驾驶的传感器融合为例,相机数据(30fps,延迟33ms)、LiDAR数据(10Hz,延迟100ms)和Radar数据(20Hz,延迟50ms)需要时间对齐。数据流架构可以通过精确的延迟控制实现同步: $$t_{sync} = \max(t_{camera} + L_{camera}, t_{lidar} + L_{lidar}, t_{radar} + L_{radar})$$ 通过在数据流图中插入可编程延迟节点,编译器可以自动平衡不同路径的延迟,确保数据在融合点同步到达。这种确定性的延迟控制是数据流架构相对于传统架构的重要优势。

触发规则的设计空间

触发规则定义了节点何时可以执行,这是数据流语义的核心。触发条件可以形式化为谓词函数: $$\mathcal{F}: V \times \mathcal{S} \rightarrow \{true, false\}$$ 其中$\mathcal{S}$是系统状态空间。不同的触发规则导致不同的执行行为:

  1. 严格触发(Strict Firing): $$\mathcal{F}_{strict}(v, s) = \bigwedge_{i \in inputs(v)} available(i, s)$$ 硬件复杂度:$O(|inputs|)$的AND门 吞吐量损失:$T_{loss} = \max_i T_i - \bar{T}$,其中$T_i$是输入$i$的到达时间

  2. 松散触发(Lenient Firing): $$\mathcal{F}_{lenient}(v, s) = \bigvee_{S \subseteq inputs(v), |S| \geq k} \bigwedge_{i \in S} available(i, s)$$ 其中$k$是最小输入数。硬件复杂度:$O(\binom{|inputs|}{k})$

  3. 条件触发(Conditional Firing): $$\mathcal{F}_{cond}(v, s) = predicate(data_values(inputs(v))) \land \mathcal{F}_{strict}(v, s)$$ 分支预测准确率影响:$IPC_{effective} = IPC_{ideal} \times (1 - penalty \times miss_rate)$

  4. 周期触发(Periodic Firing): $$\mathcal{F}_{periodic}(v, s) = (clock(s) \mod period(v) = 0)$$ 功耗影响:$P_{periodic} = f_{trigger} \times E_{op}$,即使输入未就绪也消耗能量

触发规则的选择准则

选择最优触发规则需要考虑:

  • 硬件开销:$Area \propto complexity(\mathcal{F})$
  • 性能影响:$Throughput \propto P(\mathcal{F} = true)$
  • 功耗效率:$Energy/op = \frac{E_{total}}{ops_{useful}}$

考虑一个简单的表达式 $z = (a + b) \times c$,其数据流图表示和执行分析:

     a ──┐
          ├─[+]─── temp ──┐
     b ──┘                 ├─[×]─── z
                    c ─────┘

执行时序分析

假设输入到达时间:$t_a = 0, t_b = 1, t_c = 0$,操作延迟:$d_{add} = 2, d_{mul} = 3$

执行调度:

  • $t_{add_start} = \max(t_a, t_b) = 1$
  • $t_{temp} = t_{add_start} + d_{add} = 3$
  • $t_{mul_start} = \max(t_{temp}, t_c) = 3$
  • $t_z = t_{mul_start} + d_{mul} = 6$

关键路径:$CP = \{b \rightarrow add \rightarrow mul \rightarrow z\}$,长度为$1 + 2 + 3 = 6$

资源利用率分析

设系统有1个加法器和1个乘法器:

  • 加法器利用率:$\eta_{add} = \frac{2}{6} = 33.3\%$
  • 乘法器利用率:$\eta_{mul} = \frac{3}{6} = 50\%$
  • 平均利用率:$\bar{\eta} = \frac{2 + 3}{2 \times 6} = 41.7\%$

这种异步执行模式的优势在于:

  1. 无需全局同步,减少了控制开销
  2. 自然的流水线并行,不同数据批次可重叠执行
  3. 局部性优化,temp值可直接转发无需存储

数据依赖与并行性分析

数据流图的一个关键优势是它明确地暴露了所有的数据依赖关系。依赖关系可以通过可达性矩阵$R$表示: $$R_{ij} = \begin{cases} 1 & \text{if } \exists \text{ path from } v_i \text{ to } v_j \\ 0 & \text{otherwise} \end{cases}$$ 传递闭包可通过Floyd-Warshall算法计算,复杂度$O(|V|^3)$。

并行性层次分析

  1. 任务级并行(Task-Level Parallelism, TLP)

独立子图识别算法: $$Components = ConnectedComponents(G)$$ $$TLP = |Components|$$ 在自动驾驶系统中的量化:

  • 相机管道:100 GFLOPS
  • LiDAR管道:50 GFLOPS
  • Radar管道:20 GFLOPS
  • 理论加速比:$S_{TLP} = \min(3, N_{processors})$
  1. 数据级并行(Data-Level Parallelism, DLP)

对于张量操作$T \in \mathbb{R}^{d_1 \times d_2 \times ... \times d_n}$: $$DLP = \prod_{i=1}^{n} d_i$$ 矩阵乘法$C = A \times B$的并行分解: $$C_{ij} = \sum_{k=1}^{K} A_{ik} \times B_{kj}$$ 可并行的乘累加操作数:$DLP_{GEMM} = M \times N$

SIMD效率:$\eta_{SIMD} = \frac{vector_length}{\lceil \frac{data_size}{vector_length} \rceil \times vector_length}$

  1. 流水线并行(Pipeline Parallelism)

对于$L$级流水线,稳态吞吐量: $$Throughput = \frac{1}{\max_{i \in [1,L]} latency_i}$$ 流水线效率: $$\eta_{pipeline} = \frac{\sum_{i=1}^{L} latency_i}{L \times \max_i latency_i}$$ 启动延迟:$T_{startup} = \sum_{i=1}^{L} latency_i$ 填充率:$Fill_rate = \frac{N}{N + L - 1}$,其中$N$是处理的数据批次

并行度的定量分析

并行度分析是评估架构效率的核心。设图 $G = (V, E)$,其中节点 $v \in V$ 的计算延迟为 $d(v)$。

关键路径分析

关键路径通过动态规划计算: $$T_{critical} = \max_{v \in V} EST(v) + d(v)$$ 其中最早开始时间(EST)递归定义: $$EST(v) = \begin{cases} 0 & \text{if } v \text{ is source} \\ \max_{u \in pred(v)} [EST(u) + d(u)] & \text{otherwise} \end{cases}$$ 多维并行度模型

  1. 理想并行度(Ideal Parallelism): $$P_{ideal} = \frac{W}{T_{critical}} = \frac{\sum_{v \in V} d(v)}{T_{critical}}$$ 这是Brent定理的上界,表示无限资源下的最大加速比。

  2. 资源受限并行度(Resource-Constrained Parallelism): $$P_{resource} = \min\left(P_{ideal}, \sum_{r \in R} N_r\right)$$ 其中$N_r$是资源类型$r$的数量。

  3. 带宽受限并行度(Bandwidth-Constrained Parallelism)

使用Little's Law:$P_{bandwidth} = BW \times L$

其中$BW$是带宽,$L$是平均延迟。对于计算密集型: $$P_{bandwidth} = \frac{BW_{memory}}{bytes_per_op \times ops_per_second}$$

  1. 通信受限并行度(Communication-Constrained Parallelism)

基于BSP模型: $$P_{communication} = \frac{W}{W/p + g \cdot h + l}$$ 其中$p$是处理器数,$g$是带宽因子,$h$是通信量,$l$是同步延迟。

有效并行度综合模型 $$P_{effective} = \left(\frac{1}{P_{ideal}} + \frac{1}{P_{resource}} + \frac{1}{P_{bandwidth}} + \frac{1}{P_{communication}}\right)^{-1}$$ 这是调和平均数,反映了最弱环节的限制作用。

Amdahl定律的数据流扩展 $$Speedup = \frac{1}{(1-f) + \frac{f}{P_{effective}} + \alpha \cdot \log P_{effective}}$$ 其中$\alpha \cdot \log P_{effective}$项表示调度和同步开销。

图优化与变换

数据流图的显式表示使得各种优化技术可以系统地应用。常见的图优化包括节点融合、节点分裂、重调度和数据布局优化。

节点融合将多个细粒度节点合并为一个粗粒度节点,减少中间数据的存储和传输开销。例如,将批归一化的多个操作(减均值、除标准差、缩放、偏移)融合为单个节点,可以显著减少内存访问。融合的收益可以量化为: $$Benefit_{fusion} = Cost_{separate} - Cost_{fused} = \sum_{i} (M_i \times BW_i) - M_{fused} \times BW_{fused}$$ 其中$M_i$是各个节点的内存访问量,$BW_i$是对应的带宽成本。

节点分裂则是相反的操作,将复杂节点分解为简单节点以增加并行机会。这在处理大规模矩阵运算时特别有用,可以将其分解为多个可以并行执行的子矩阵运算。分裂的关键是找到最优的分割点,使得负载均衡的同时最小化通信开销。

9.1.2 静态vs动态数据流

数据流架构的一个根本性设计选择是采用静态还是动态的执行模型。这个选择不仅影响硬件复杂度和性能特征,更决定了系统能够高效支持的工作负载类型。理解这两种模型的本质差异和适用场景,对于选择和设计合适的NPU架构至关重要。

静态数据流的理论基础

静态数据流模型源于同步数据流(Synchronous Dataflow, SDF)理论,这是一个在数字信号处理领域广泛应用的计算模型。在SDF中,每个节点在每次执行时消耗和产生固定数量的令牌,这使得整个系统的行为在编译时完全可预测。

静态数据流的核心约束可以形式化表示为单赋值规则:每条边在任意时刻最多包含一个有效数据。数学上,这可以表示为: $$\forall e \in E, \forall t \in T: tokens(e, t) \leq 1$$ 其中 $tokens(e, t)$ 表示时刻 $t$ 边 $e$ 上的令牌数。这个约束看似简单,却带来了深远的影响。

首先,它保证了执行的确定性。由于每个数据只能被产生和消费一次,不存在竞争条件或不确定的执行顺序。这使得系统的行为完全可预测,相同的输入总是产生相同的输出和相同的执行轨迹。对于安全关键应用如自动驾驶,这种确定性是系统认证的基础。

其次,单赋值规则简化了硬件实现。不需要复杂的标签匹配单元来区分不同迭代的数据,不需要关联存储器来缓存多个令牌,也不需要动态调度器来决定执行顺序。所有的调度决策都在编译时完成,运行时硬件只需要按照预定的时间表执行即可。

静态调度的编译过程本质上是求解一个约束满足问题。给定数据流图$G=(V,E)$和资源约束$R$,需要找到一个调度函数$S: V \rightarrow T$,将每个节点映射到执行时刻,满足:

  1. 依赖约束:$\forall (u,v) \in E: S(v) \geq S(u) + d(u)$,其中$d(u)$是节点$u$的执行延迟
  2. 资源约束:$\forall t \in T, \forall r \in R: \sum_{v: S(v)=t} usage(v,r) \leq capacity(r)$
  3. 存储约束:$\forall t \in T: \sum_{e: live(e,t)} size(e) \leq M$,其中$live(e,t)$表示边$e$在时刻$t$是否包含活跃数据

这个问题在一般情况下是NP完全的,但通过启发式算法如列表调度、模拟退火或整数线性规划,可以找到接近最优的解。

静态数据流的优势在实际系统中表现显著。零运行时调度开销意味着所有的硬件资源都用于实际计算,而不是控制和协调。时序的完全可预测性使得功耗管理可以做到极致,编译器可以精确地插入时钟门控和电源门控指令。形式化验证变得可行,因为状态空间是有限和确定的。

然而,静态模型也有其固有局限。最大的挑战是处理动态行为,如数据依赖的控制流、可变长度的循环、递归调用等。虽然可以通过保守的静态分析和最坏情况假设来处理这些情况,但会导致资源利用率低下。例如,对于一个条件执行的分支,静态调度必须为两个分支都预留资源和时间,即使实际执行中只会选择一个分支。

动态数据流的演进与创新

动态数据流模型的发展历程反映了计算机体系结构对灵活性和效率平衡的不断探索。早期的MIT Tagged-Token架构和Manchester Dataflow Machine为这一领域奠定了理论基础,而现代的动态数据流设计则在此基础上进行了诸多创新。

动态数据流的核心创新是引入了标签(tagging)机制来区分不同执行实例的数据。每个令牌不仅携带数据值,还包含一个唯一的标签,标识其所属的迭代、线程或执行上下文。这使得同一条边上可以同时存在多个令牌,打破了静态模型的单赋值限制: $$\forall e \in E, \forall t \in T: tokens(e, t) \in \mathbb{N}, \text{ 每个令牌有唯一标签 } tag \in \mathcal{T}$$ 标签空间$\mathcal{T}$的设计是一个关键的架构决策。简单的设计使用整数标签表示迭代次数,但这限制了并行的维度。更复杂的设计采用多维标签$(i, j, k, context)$,可以表示嵌套循环、多线程和函数调用等复杂的执行模式。标签的位宽直接影响硬件成本和可支持的并行度: $$|\mathcal{T}| = 2^{b_{tag}} \text{, 其中} b_{tag} \text{是标签位宽}$$ 令牌匹配是动态数据流的核心操作,决定了节点何时可以执行。匹配规则可以形式化为: $$fire(n, tag) \iff \forall i \in inputs(n): \exists token_i \in buffer(i) \text{ with } tag(token_i) = tag$$ 这个匹配操作在硬件上通常通过关联存储器(CAM)或哈希表实现。匹配单元的复杂度为$O(n \times m)$,其中$n$是输入端口数,$m$是缓冲的令牌数。为了降低复杂度,现代设计采用了多种优化技术:

  1. 分级匹配:将完全匹配分解为部分匹配,先匹配高位标签(如线程ID),再匹配低位标签(如迭代号)
  2. 预测性匹配:基于历史模式预测下一个可能匹配的标签,减少搜索空间
  3. 稀疏匹配:只对活跃的标签范围进行匹配,避免搜索整个标签空间

动态调度带来的灵活性使得系统可以自适应地处理各种不规则的并行模式。例如,在处理稀疏矩阵乘法时,不同行的非零元素数量可能相差很大,静态调度必须按最坏情况分配时间,而动态调度可以让快速完成的行立即开始下一次迭代,实现自然的负载均衡。

然而,这种灵活性是有代价的。首先是硬件复杂度的显著增加。每个节点需要额外的标签存储、匹配逻辑和缓冲管理单元。一个典型的动态数据流节点的面积可能是静态节点的2-3倍。其次是功耗的增加,主要来自于关联搜索和额外的控制逻辑。最后是性能的不可预测性,由于执行顺序依赖于运行时的数据可用性,很难给出精确的性能保证。

混合模型的实践智慧

认识到纯静态和纯动态模型各自的局限性,现代数据流架构普遍采用混合方案,在不同的抽象层次上应用不同的调度策略。这种分层设计充分利用了两种模型的优势,同时规避了它们的缺点。

一种常见的混合策略是"粗粒度动态+细粒度静态"。在这种模型中,系统在任务或子图级别进行动态调度,而每个任务内部采用静态调度。例如,一个神经网络的不同层可以动态调度,但每一层内部的操作是静态编排的。这种方法的优势在于: $$Overhead_{hybrid} = \alpha \times Overhead_{dynamic} + (1-\alpha) \times Overhead_{static}$$ 其中$\alpha$是动态调度部分的比例,通常很小(<10%),因此总开销接近静态模型。

另一种混合策略是"控制流动态+数据流静态"。控制流决策(如分支、循环边界)在运行时确定,但一旦选择了执行路径,该路径内的数据流是静态调度的。这特别适合处理具有数据依赖控制流的应用,如自适应算法和动态神经网络。

Groq TSP采用了一个极端但创新的选择:完全静态的全局调度。这个决定基于对目标工作负载的深刻理解——推理任务的计算图在部署时是已知的,不需要运行时的灵活性。通过将所有的调度复杂度转移到编译器,TSP实现了极简的硬件设计和确定性的执行,这对于实时AI应用至关重要。

这种设计哲学可以用"编译时间换运行时间"来概括。虽然编译可能需要几分钟甚至更长,但这是一次性的成本,而运行时的收益是持续的。对于推理部署场景,模型一旦训练完成就很少改变,因此长编译时间是可以接受的。

9.1.3 Token-based执行机制

令牌(Token)是数据流架构中数据传递的基本单位,包含数据值和控制信息。

令牌结构

一个完整的令牌包含:

Token = {
    data:     数据负载(标量/向量/张量)
    tag:      迭代标识(用于动态数据流)
    color:    执行上下文(用于多线程)
    valid:    有效位
    credit:   流控信息
}

令牌匹配规则

节点执行需要满足令牌匹配条件:

  1. 严格匹配(Strict Matching): 所有输入必须具有相同标签 $$\forall i, j \in inputs: tag(token_i) = tag(token_j)$$

  2. 松散匹配(Relaxed Matching): 部分输入可以使用通配符 $$\exists S \subseteq inputs: \forall i \in S: tag(token_i) = tag_{current}$$ 激活与消耗

节点的执行过程:

  1. 收集阶段:等待所有必需的输入令牌
  2. 激活阶段:满足触发条件,开始计算
  3. 执行阶段:消耗输入令牌,执行操作
  4. 产生阶段:生成输出令牌发送到下游

令牌生产率和消耗率决定了系统的平衡: $$\rho = \frac{\text{production rate}}{\text{consumption rate}}$$ 当 $\rho > 1$ 时需要缓冲,$\rho < 1$ 时产生空闲。

背压与流控

数据流系统需要处理生产者-消费者速度不匹配:

  1. Credit-based流控: - 下游节点向上游发送credit表示可用缓冲 - 上游根据credit决定是否发送数据 - Credit更新:$credit_{new} = credit_{old} + consumed - produced$

  2. 背压传播: - 当下游阻塞时,背压信号逐级向上传播 - 背压延迟:$T_{backpressure} = \sum_{i=1}^{n} (d_{wire}(i) + d_{logic}(i))$

  3. 缓冲设计: 最小缓冲深度以避免死锁: $$B_{min} = \lceil \frac{T_{round-trip}}{T_{cycle}} \rceil + 1$$

9.1.4 确定性执行优势

确定性执行是某些数据流架构(如Groq TSP)的关键特性,带来诸多系统级优势。

时序可预测性

在确定性数据流中,任意操作的执行时间可以精确预测: $$T_{exec}(op_i) = T_{start} + \sum_{j \in path(src, op_i)} d_j$$ 其中 $path(src, op_i)$ 是从源到操作 $op_i$ 的关键路径。

这种可预测性支持:

  • 精确的性能建模
  • 最优的资源调度
  • 严格的QoS保证

调试与验证简化

确定性执行极大简化了系统验证:

  1. 可重现性:相同输入总是产生相同的执行轨迹
  2. 形式化验证:可以使用模型检查等技术
  3. 等价性检查:硬件与软件模型的bit-accurate比较

验证复杂度从 $O(2^n)$(非确定性)降至 $O(n)$(确定性),其中 $n$ 是状态空间大小。

实时性保证

确定性执行提供硬实时保证:

最坏情况执行时间(WCET): $$WCET = T_{critical} + T_{overhead}$$ 其中 $T_{overhead}$ 包括初始化和终结开销。

对于自动驾驶等安全关键应用,确定性执行确保:

  • 感知延迟 < 100ms
  • 规划延迟 < 50ms
  • 控制延迟 < 10ms

抖动(Jitter)最小化: $$\sigma_{jitter} = \sqrt{\frac{1}{N}\sum_{i=1}^{N}(T_i - \bar{T})^2} \approx 0$$ 功耗优化机会

确定性执行允许激进的功耗优化:

  1. 精确的时钟门控: $$P_{saved} = P_{dynamic} \times (1 - \alpha_{activity})$$ 其中活动因子 $\alpha_{activity}$ 可以静态确定。

  2. 电压频率调节(DVFS): 由于知道精确的执行时间,可以优化: $$E = C \times V^2 \times f \times t$$ 在满足截止时间约束下最小化能耗。

  3. 功耗门控调度: 编译器可以插入功耗门控指令:

t0-t100:   Unit_A active
t101-t200: Unit_A sleep, Unit_B active
t201-t300: Both active

确定性执行是以编译复杂度换取运行时效率的典型权衡,特别适合推理等工作负载相对固定的场景。

9.2 Groq TSP架构特征

Groq Tensor Streaming Processor (TSP) 是数据流架构在AI加速器领域的创新实践,通过软件定义的硬件(Software-Defined Hardware)理念,实现了编译器完全控制的确定性执行。TSP的设计目标是消除冯·诺依曼瓶颈,特别是内存墙问题,同时提供可预测的超低延迟。

9.2.1 无外部DRAM设计理念

TSP最激进的设计决策是完全依赖片上SRAM,避免外部DRAM访问带来的不确定性和能耗开销。

片上存储充分性分析

对于典型的推理工作负载,所需存储容量可以估算为: $$M_{total} = M_{weights} + M_{activations} + M_{workspace}$$ 其中:

  • 权重存储:$M_{weights} = \sum_{l=1}^{L} (W_l \times H_l \times C_{in,l} \times C_{out,l}) \times b_{weight}$
  • 激活存储:$M_{activations} = \max_l (B \times H_l \times W_l \times C_l) \times b_{activation}$
  • 工作空间:$M_{workspace} \approx 0.1 \times (M_{weights} + M_{activations})$

以BERT-Base为例(110M参数):

  • FP16权重:220MB
  • 激活峰值:约50MB(batch=1)
  • 总需求:约300MB

TSP单芯片提供220MB SRAM,通过以下技术满足需求:

  1. 权重压缩:2:4稀疏 + INT8量化,压缩率4×
  2. 激活重计算:选择性存储中间结果
  3. 操作融合:减少临时存储需求

带宽墙问题规避

传统架构的带宽限制: $$BW_{required} = \frac{OPS \times (bytes_{input} + bytes_{weight} + bytes_{output})}{reuse_factor}$$ 对于200 TOPS系统,假设reuse_factor=10: $$BW_{required} = \frac{200 \times 10^{12} \times 6}{10} = 120 \text{ TB/s}$$ HBM3提供约1TB/s,远不能满足需求。

TSP通过片上SRAM提供的聚合带宽: $$BW_{on-chip} = N_{banks} \times W_{port} \times f_{SRAM} = 1024 \times 32B \times 1.25GHz = 40 \text{ TB/s}$$ 配合数据重用优化,完全避免带宽瓶颈。

功耗优势

访问能耗对比(45nm工艺):

  • SRAM读取:约5 pJ/byte
  • DRAM读取:约100 pJ/byte
  • 片外传输:约500 pJ/byte

对于每秒处理1TB数据的系统:

  • 全SRAM方案:5W
  • DRAM方案:100W
  • 总功耗降低20×

9.2.2 编译时调度

TSP的编译器承担了传统硬件调度器的全部职责,在编译时生成确定性的执行计划。

静态资源分配

编译器需要解决的资源分配问题可以形式化为整数线性规划(ILP):

目标函数(最小化执行时间): $$\min \sum_{i=1}^{N} t_i$$ 约束条件:

  1. 资源约束:$\sum_{i \in S_t} r_{i,k} \leq R_k, \forall t, k$
  2. 依赖约束:$t_j \geq t_i + d_i, \forall (i,j) \in E$
  3. 存储约束:$\sum_{i \in Live_t} m_i \leq M_{total}, \forall t$

其中:

  • $S_t$:时刻$t$执行的操作集合
  • $r_{i,k}$:操作$i$对资源$k$的需求
  • $R_k$:资源$k$的总量
  • $Live_t$:时刻$t$的活跃变量集合

冲突消除

编译器通过以下技术消除运行时冲突:

  1. Bank冲突消除: 通过地址交织(interleaving)确保并行访问不冲突: $$bank(addr) = (addr \oplus (addr >> log_2(N_{banks}))) \mod N_{banks}$$

  2. 结构冲突消除: 使用Modulo调度实现软件流水线: $$t_{scheduled}(op) = t_{ASAP}(op) + k \times II$$ 其中$II$是初始间隔(Initiation Interval)

  3. 数据冲突消除: 寄存器重命名避免WAW和WAR冲突: $$reg_{physical} = reg_{logical} + version \times N_{architectural}$$ 优化空间

编译器可以探索的优化维度:

  1. 计算图变换: - 算子融合:减少中间结果存储 - 算子分裂:提高并行度 - 重排序:优化数据局部性

  2. 数据布局优化: - Tensor维度排列:NCHW vs NHWC - Padding策略:对齐vs紧凑 - 分块(tiling)参数:平衡重用与容量

  3. 调度策略: - 延迟优先 vs 吞吐优先 - 能耗优化调度 - 热点分散

编译时间复杂度:$O(N^3)$对于N个操作,但只需离线执行一次。

9.2.3 确定性延迟保证

TSP通过硬件和软件协同设计提供严格的延迟保证,这对实时AI应用至关重要。

延迟可预测性

每个操作的延迟完全确定: $$L_{op} = L_{compute} + L_{memory} + L_{transport}$$

  • 计算延迟:$L_{compute} = \lceil \frac{FLOPs}{throughput} \rceil \times T_{cycle}$
  • 存储延迟:$L_{memory} = N_{access} \times T_{SRAM}$(固定SRAM延迟)
  • 传输延迟:$L_{transport} = distance \times T_{hop}$(Manhattan距离)

端到端延迟: $$L_{e2e} = \max_{path} \sum_{op \in path} L_{op}$$ 变异系数(CV)接近零: $$CV = \frac{\sigma_L}{\mu_L} < 0.01$$ QoS支持

TSP支持多级QoS通过时间分片:

时间片分配:
├── 高优先级(RT):40% slots,延迟 < 10ms
├── 中优先级(BE):40% slots,延迟 < 100ms
└── 低优先级(BG):20% slots,best effort

调度算法保证: $$\forall task_i \in RT: L_i \leq L_{deadline,i}$$ 实时应用适配

自动驾驶延迟要求映射:

  1. 感知管道(100ms预算): - 图像预处理:10ms(确定性) - 目标检测:40ms(确定性) - 3D重建:30ms(确定性) - 后处理:20ms(确定性)

  2. 规划管道(50ms预算): - 行为预测:20ms(确定性) - 轨迹规划:20ms(确定性) - 决策制定:10ms(确定性)

延迟分解: $$L_{total} = L_{fixed} + L_{variable} = L_{fixed} + 0$$(TSP中$L_{variable} = 0$)

9.2.4 TSP核心组件

TSP芯片包含多个精心设计的组件,协同实现高效的数据流执行。

计算单元布局

TSP采用二维阵列组织:

┌─────────────────────────────────┐
│ SuperLane 0  │ SuperLane 1  │...│  
│ ┌─────────┐  │ ┌─────────┐  │   │
│ │ MXM Unit│  │ │ MXM Unit│  │   │  320个MXM单元
│ │ 320 INT8│  │ │ 320 INT8│  │   │  = 20 SuperLanes
│ │ MAC/cyc │  │ │ MAC/cyc │  │   │  × 16 MXM/SuperLane
│ └─────────┘  │ └─────────┘  │   │
│ ┌─────────┐  │ ┌─────────┐  │   │
│ │  VXM     │  │ │  VXM     │  │   │  320个VXM单元
│ │  Vector  │  │ │  Vector  │  │   │  向量运算
│ └─────────┘  │ └─────────┘  │   │
│ ┌─────────┐  │ ┌─────────┐  │   │
│ │  SXM     │  │ │  SXM     │  │   │  320个SXM单元
│ │  Scalar  │  │ │  Scalar  │  │   │  标量/控制
│ └─────────┘  │ └─────────┘  │   │
└─────────────────────────────────┘

计算能力:

  • INT8: 750 TOPS
  • FP16: 188 TFLOPS
  • FP32: 47 TFLOPS

存储系统组织

分布式SRAM组织:

Memory Hierarchy:
├── L0: Register Files (per unit)
   ├── Size: 144KB/unit
   └── BW: 1TB/s/unit
├── L1: Local SRAM (per SuperLane)
   ├── Size: 11MB/SuperLane
   └── BW: 2TB/s/SuperLane
└── L2: Global SRAM (shared)
    ├── Size: 220MB total
    └── BW: 40TB/s aggregate

地址映射函数: $$addr_{physical} = base + (x \times stride_x + y \times stride_y) \mod size$$ 互连网络设计

TSP使用定制的片上网络连接所有组件:

拓扑结构:

  • 2D Mesh用于近邻通信
  • Express lanes用于长距离传输
  • Multicast树用于权重广播

路由策略:

  • XY维序路由避免死锁
  • 虚通道(VC)支持多优先级
  • Wormhole流控减少延迟

网络性能:

  • 单跳延迟:1 cycle
  • 平均延迟:$\bar{L} = \frac{N}{3}$ hops(N×N mesh)
  • 二分带宽:$BW_{bisection} = N \times w \times f$

指令流架构

TSP指令采用超长指令字(VLIW)格式:

Instruction Format (512 bits):
┌────────┬────────┬────────┬────────┬────────┐
│MXM_ops │VXM_ops │SXM_ops │MEM_ops │CTRL_ops│
│128-bit │128-bit │64-bit  │64-bit  │28-bit  │
└────────┴────────┴────────┴────────┴────────┘

指令流特征:

  • 无分支预测(所有分支在编译时解决)
  • 无乱序执行(严格按序)
  • 无缓存(完全软件管理)

这种设计将复杂度从硬件转移到编译器,实现了功耗和面积的最优化。

9.3 与脉动阵列对比

数据流架构和脉动阵列代表了NPU设计的两种不同理念。理解它们的差异有助于根据具体应用场景做出最优的架构选择。

9.3.1 灵活性vs效率权衡

计算模式支持

脉动阵列优化特定计算模式:

  • 主要支持矩阵乘法及其变体
  • 卷积通过im2col转换为矩阵乘法
  • 对不规则计算支持有限

数据流架构支持更广泛的计算:

  • 任意计算图映射
  • 原生支持稀疏操作
  • 灵活的数据重用模式

计算效率对比(以GEMM为例):

脉动阵列利用率: $$\eta_{systolic} = \frac{M \times N \times K}{max(M,N,K) \times Array_{size}^2}$$ 当矩阵维度与阵列大小匹配时,$\eta_{systolic} \rightarrow 1$。

数据流架构利用率: $$\eta_{dataflow} = \frac{Ops_{actual}}{Ops_{peak}} \times \frac{1}{1 + \alpha_{overhead}}$$ 其中$\alpha_{overhead}$包括数据移动和同步开销,典型值0.1-0.3。

资源利用率

脉动阵列的利用率挑战:

  1. 维度不匹配:当$M, N, K < Array_{size}$时利用率下降
  2. 批处理受限:小batch导致利用率低
  3. 稀疏性处理:零值仍占用计算周期

数据流架构的利用率优势:

  1. 动态映射:根据工作负载调整资源分配
  2. 稀疏原生支持:跳过零值计算
  3. 多粒度并行:同时执行不同类型操作

实际利用率数据(200 TOPS设计):

工作负载        脉动阵列   数据流架构
GEMM(大)         95%        85%
GEMM(小)         45%        75%
稀疏GEMM(2:4)    50%        90%
Conv2D           85%        80%
Attention        70%        85%
Element-wise     30%        90%

功耗效率

功耗分解(典型值):

脉动阵列:

  • 计算:40%
  • 片上数据移动:25%
  • 控制逻辑:10%
  • 时钟树:15%
  • 泄漏:10%

数据流架构:

  • 计算:35%
  • 片上数据移动:30%
  • 控制逻辑:15%
  • 时钟树:10%
  • 泄漏:10%

能效比较: $$\frac{TOPS/W_{dataflow}}{TOPS/W_{systolic}} = \frac{\eta_{dataflow} \times P_{systolic}}{\eta_{systolic} \times P_{dataflow}}$$ 对于稀疏工作负载,数据流架构能效提升1.5-2×。

9.3.2 编程模型差异

控制流vs数据流

脉动阵列编程模型:

// 伪代码脉动阵列编程
for t in range(M+N-1):  // 时间步
    for i in range(M):
        for j in range(N):
            if (i+j == t):  // 对角线执行
                C[i][j] += A[i][*] @ B[*][j]

特点:

  • 显式的时间步控制
  • 规则的数据访问模式
  • 编译器优化空间有限

数据流编程模型:

// 伪代码:数据流编程
Graph g;
Node matmul = g.add_op(MATMUL, {A, B});
Node add = g.add_op(ADD, {matmul, bias});
Node relu = g.add_op(RELU, {add});
compile_and_execute(g);

特点:

  • 声明式编程
  • 隐式并行
  • 编译器完全控制执行

编译复杂度

脉动阵列编译主要任务:

  1. Tiling选择:$O(log(M) \times log(N) \times log(K))$
  2. 数据布局:预定义模式,$O(1)$
  3. 调度生成:模板化,$O(M \times N)$

总复杂度:$O(M \times N)$,相对简单。

数据流编译任务:

  1. 图优化:$O(V^2)$,V是节点数
  2. 资源分配:NP-hard,使用启发式$O(V^3)$
  3. 路由生成:$O(E \times P)$,E是边数,P是处理器数

总复杂度:$O(V^3)$,显著更高。

编译时间对比(BERT-Base):

  • 脉动阵列:< 1秒
  • 数据流(快速模式):10-30秒
  • 数据流(优化模式):1-5分钟

优化机会

脉动阵列优化维度:

  1. 数据重用优化 - Temporal重用:固定模式 - Spatial重用:受阵列大小限制
  2. 精度优化 - 混合精度:粗粒度 - 量化:统一应用
  3. 稀疏性利用 - 结构化稀疏:2:4模式 - 非结构化:支持有限

数据流架构优化维度:

  1. 图级优化 - 算子融合:任意模式 - 算子分解:自适应粒度 - 重计算vs存储权衡
  2. 数据流优化 - 自定义重用模式 - 动态批处理 - 流水线深度调整
  3. 资源分配优化 - 异构资源调度 - 功耗感知映射 - 热点避免

优化效果量化: $$Speedup = \frac{T_{baseline}}{T_{optimized}} = \frac{1}{(1-f) + \frac{f}{s}}$$ 其中f是可优化部分比例,s是优化加速比。数据流架构的f更大。

9.3.3 适用场景分析

工作负载特征

适合脉动阵列的工作负载:

  1. 密集矩阵运算 - 大规模GEMM:M,N,K > 1024 - 标准卷积:3×3, 5×5 - 批量推理:batch > 32

  2. 规则计算模式 - Transformer的线性层 - CNN的卷积层 - RNN的门控计算

性能预测模型: $$T_{systolic} = \lceil \frac{M}{M_a} \rceil \times \lceil \frac{N}{N_a} \rceil \times \lceil \frac{K}{K_a} \rceil \times (M_a + N_a + K_a - 2)$$ 适合数据流架构的工作负载:

  1. 不规则计算 - 稀疏网络:剪枝率 > 50% - 动态网络:早退出、条件执行 - 混合精度:层级不同精度

  2. 复杂数据流 - Multi-head Attention - Graph Neural Networks - Neural Architecture Search

性能预测模型: $$T_{dataflow} = T_{critical-path} + T_{scheduling-overhead}$$ 部署环境要求

数据中心部署:

评分标准            脉动阵列  数据流
吞吐量(batch>128)    10       8
能效(TOPS/W)         9        8
成本($/TOPS)         8        6
可扩展性             9        7
总分                 36       29

边缘端部署:

评分标准            脉动阵列  数据流
延迟(batch=1)        7        10
功耗(<10W)           8        9
灵活性               6        10
确定性               7        10
总分                 28       39

车载部署(自动驾驶):

评分标准            脉动阵列  数据流
实时性保证           7        10
功能安全             8        10
热设计              8        9
成本敏感度          7        6
总分                 30       35

成本考虑

开发成本:

  • 脉动阵列:RTL简单,验证快速,NRE成本低
  • 数据流架构:RTL复杂,验证周期长,NRE成本高 $$Cost_{total} = Cost_{NRE} + Cost_{unit} \times Volume$$ 当Volume > 100K时,单位成本主导:

  • 脉动阵列:芯片面积小,良率高

  • 数据流架构:芯片面积大,良率较低

运营成本(TCO): $$TCO = Cost_{hw} + Cost_{power} \times Years + Cost_{cooling} + Cost_{maintenance}$$

数据流架构在功耗相关成本上有优势,特别是边缘部署场景。

架构选择决策树

是否需要极低延迟?
├─是→ 是否工作负载固定?
│     ├─是→ 数据流架构(TSP)
│     └─否→ 需要进一步分析
└─否→ 是否批处理为主?
      ├─是→ 矩阵运算占比?
      │     ├─>80% → 脉动阵列(TPU)
      │     └─<80% → 混合架构
      └─否→ 数据流架构优先

实际产品选择还需考虑生态系统、软件栈成熟度、供应商支持等因素。两种架构各有优势,关键是匹配应用需求。

本章小结

本章深入探讨了数据流架构的基本原理及其在NPU设计中的应用,以Groq TSP为例分析了其独特的设计理念。关键要点包括:

  1. 数据流计算模型:基于图的执行模型天然支持并行,通过令牌机制协调计算,静态数据流提供确定性执行的优势。

  2. TSP架构创新: - 无外部DRAM设计消除了内存墙瓶颈 - 编译时完全调度实现零运行时开销 - 确定性延迟保证满足实时应用需求 - 软件定义硬件理念简化硬件复杂度

  3. 架构对比分析: - 脉动阵列在规则密集计算上效率最优 - 数据流架构在不规则稀疏计算上更灵活 - 编程模型的差异导致不同的优化空间 - 应用场景决定最优架构选择

关键公式回顾:

  • 理想并行度:$P_{ideal} = \frac{\sum_{v \in V} d(v)}{T_{critical}}$
  • 令牌匹配:$fire(n) \iff \forall i \in inputs(n): \exists token_i \text{ with } tag(token_i) = tag_{current}$
  • 带宽需求:$BW_{required} = \frac{OPS \times bytes_{per_op}}{reuse_factor}$
  • 架构利用率:$\eta = \frac{Ops_{actual}}{Ops_{peak}} \times \frac{1}{1 + \alpha_{overhead}}$

练习题

基础题(理解概念)

习题9.1 数据流图表示 给定表达式:$y = (a \times b + c) \times (d - e)$,画出对应的数据流图,标注所有节点和边,并计算理想并行度。假设每个操作延迟为1个周期。

提示

考虑哪些操作可以并行执行,关键路径包含哪些节点。

答案

数据流图:

a ──┐
    ├─[×]─── t1 ──┐
b ──┘              ├─[+]─── t2 ──┐
            c ─────┘              ├─[×]─── y
                    d ──┐         │
                        ├─[-]─── t3
                    e ──┘

关键路径:a/b → × → + → × → y,长度为3个周期 总操作数:4个操作 理想并行度:$P_{ideal} = \frac{4}{3} = 1.33$

习题9.2 静态vs动态数据流 比较以下循环在静态和动态数据流中的执行差异:

for i = 0 to N-1:
    if (A[i] > threshold):
        B[i] = compute_heavy(A[i])
    else:
        B[i] = A[i]

分析两种模型的优缺点。

提示

静态数据流需要展开所有可能路径,动态数据流可以运行时决策。

答案

静态数据流:

  • 必须为两个分支都分配资源
  • 执行时间固定:$T = N \times \max(T_{heavy}, T_{copy})$
  • 资源利用率低当分支不平衡时
  • 优点:时序可预测,无调度开销

动态数据流:

  • 根据实际条件动态调度
  • 执行时间变化:$T = \sum_{i=0}^{N-1} T_i$,其中$T_i$依赖于$A[i]$
  • 资源利用率高
  • 缺点:需要运行时调度,时序不可预测

当threshold导致分支严重不平衡时,动态数据流效率显著更高。

习题9.3 TSP存储容量计算 对于ResNet-50推理(25.6M参数),计算在TSP上部署需要的片上存储。假设:

  • 权重使用INT8量化
  • 激活使用FP16
  • Batch size = 1
  • 最大特征图:56×56×256
提示

考虑权重存储、激活峰值存储和工作空间。

答案

权重存储:

  • 参数数量:25.6M
  • INT8量化:25.6MB

激活存储(峰值):

  • 最大特征图:56×56×256 = 802,816
  • FP16:802,816 × 2 = 1.6MB
  • 考虑双缓冲:3.2MB

工作空间:

  • 约10%额外:(25.6 + 3.2) × 0.1 = 2.88MB

总需求:25.6 + 3.2 + 2.88 = 31.68MB

TSP单芯片220MB SRAM充分满足需求,还可以存储多个模型或增大batch。

挑战题(深入分析)

习题9.4 编译器调度优化 给定一个简化的数据流图,包含6个操作,依赖关系和资源需求如下:

| 操作 | 依赖 | 计算单元需求 | 执行时间 |

操作 依赖 计算单元需求 执行时间
A - MXM×2 4 cycles
B - VXM×1 2 cycles
C A MXM×1 3 cycles
D A,B VXM×2 2 cycles
E C MXM×1 2 cycles
F D,E VXM×1 1 cycle

系统资源:MXM×2, VXM×2

设计最优调度方案,最小化总执行时间。

提示

使用列表调度算法,考虑资源约束和依赖关系。

答案

最优调度:

| 时刻 | MXM利用 | VXM利用 | 执行操作 |

时刻 MXM利用 VXM利用 执行操作
0-1 A(2/2) B(1/2) A,B并行
2-3 A(2/2) - A继续
4-5 C(1/2) D(2/2) C,D并行
6-6 C(1/2) - C继续
7-7 E(1/2) - E开始
8-8 E(1/2) - E继续
9-9 - F(1/2) F执行

总执行时间:10 cycles

关键路径:A(4) → D(2) → F(1) = 7 cycles(由于资源冲突延长到10) 资源利用率:MXM = 9/20 = 45%, VXM = 5/20 = 25%

习题9.5 背压机制设计 设计一个credit-based流控系统,满足以下要求:

  • 生产者峰值速率:1000 tokens/cycle
  • 消费者平均速率:800 tokens/cycle
  • 网络往返延迟:20 cycles
  • 避免死锁和饥饿

计算最小缓冲深度和credit初始值。

提示

考虑最坏情况下的背压延迟和速率不匹配。

答案

最小缓冲深度计算:

  1. 往返期间最大产生量: $Tokens_{RTT} = 1000 \times 20 = 20,000$

  2. 速率不匹配缓冲: $\Delta_{rate} = (1000 - 800) = 200$ tokens/cycle 长期运行需要额外缓冲来吸收突发

  3. 最小缓冲: $B_{min} = Tokens_{RTT} + \Delta_{rate} \times T_{burst}$ 假设突发时长$T_{burst} = 100$ cycles $B_{min} = 20,000 + 200 \times 100 = 40,000$ tokens

  4. Credit初始值: $Credits_{init} = B_{min} = 40,000$

  5. Credit更新策略: - 发送token时:credit-- - 收到ACK时:credit += consumed_tokens - 当credit = 0时,停止发送(背压)

这个设计确保:

  • 无死锁:总有足够缓冲处理在途数据
  • 无饥饿:消费者总能获得数据
  • 高效率:允许突发传输

习题9.6 能效优化分析 比较两种200 TOPS NPU设计方案的能效:

方案A(类TPU):

  • 256×256脉动阵列
  • 外部HBM2E (3.6TB/s)
  • 片上SRAM 32MB
  • 工艺:7nm
  • 频率:1GHz

方案B(类TSP):

  • 分布式计算单元
  • 无外部DRAM
  • 片上SRAM 256MB
  • 工艺:7nm
  • 频率:1.25GHz

对于BERT-Base推理(batch=1),估算两者的能耗差异。

提示

考虑计算能耗、片上/片外数据移动能耗、静态功耗。

答案

BERT-Base工作负载分析:

  • 计算量:~440 GFLOPs
  • 权重:110M参数 × 2B = 220MB
  • 激活:~50MB峰值

方案A能耗:

  1. 计算:$E_{compute} = 440G \times 10pJ = 4.4J$
  2. HBM访问(权重+激活): - 读取量:~270MB × 重用因子(假设10) = 27MB - $E_{HBM} = 27M \times 100pJ = 2.7J$
  3. 片上移动:$E_{on-chip} = 440G \times 2pJ = 0.88J$
  4. 静态功耗:$P_{static} = 5W$,执行时间$T = 440G/200T = 2.2ms$ - $E_{static} = 5W \times 2.2ms = 11mJ$

总能耗A:$E_A = 4.4 + 2.7 + 0.88 + 0.011 = 7.99J$

方案B能耗:

  1. 计算:$E_{compute} = 440G \times 10pJ = 4.4J$
  2. 无HBM访问:$E_{DRAM} = 0$
  3. 片上移动(更多但仍在片上):$E_{on-chip} = 440G \times 3pJ = 1.32J$
  4. 静态功耗:$P_{static} = 3W$(无HBM PHY),$T = 440G/200T = 2.2ms$ - $E_{static} = 3W \times 2.2ms = 6.6mJ$

总能耗B:$E_B = 4.4 + 0 + 1.32 + 0.0066 = 5.73J$

能效提升:$\frac{E_A}{E_B} = \frac{7.99}{5.73} = 1.39×$

方案B通过消除外部DRAM访问,能效提升约40%。

习题9.7 实时性保证分析 某自动驾驶系统要求:

  • 相机输入:30 FPS (33.3ms/帧)
  • 感知延迟:< 100ms
  • 规划延迟:< 50ms
  • 总延迟:< 150ms

使用数据流架构设计满足要求的调度方案。考虑:

  • 感知网络:100 GFLOPs
  • 规划网络:50 GFLOPs
  • NPU性能:200 TOPS
提示

设计流水线调度,考虑任务优先级和资源分配。

答案

调度方案设计:

  1. 执行时间计算: - 感知:$T_{perception} = 100G / 200T = 0.5ms$(计算) - 规划:$T_{planning} = 50G / 200T = 0.25ms$(计算) - 加上数据传输和预/后处理开销(假设各10ms)

  2. 流水线设计:

时间线(ms):
0    33.3   66.6   100   133.3  166.6
|-----|-----|-----|-----|-----|
Frame1: Capture → Perception → Planning → Control
        [33.3]    [10.5]      [10.25]    
Frame2:          Capture → Perception → Planning
                 [33.3]    [10.5]      [10.25]
  1. 资源分配: - 感知优先级:高(时间片70%) - 规划优先级:中(时间片20%) - 其他任务:低(时间片10%)

  2. 确定性保证: - 静态调度表:每33.3ms周期固定 - 最坏情况延迟:

    • 感知:33.3(采集) + 10.5(处理) = 43.8ms < 100ms ✓
    • 规划:10.25ms < 50ms ✓
    • 端到端:33.3 + 10.5 + 10.25 = 54.05ms < 150ms ✓
  3. 容错设计: - 双缓冲避免覆盖 - 优先级继承防止优先级反转 - 看门狗定时器检测超时

该方案满足所有实时性要求,且留有充足余量。

习题9.8 架构选择决策 为以下三个场景选择最适合的NPU架构(脉动阵列/数据流/混合),并说明理由:

场景1:云端大语言模型推理服务

  • 模型:GPT-3 (175B参数)
  • QPS要求:1000
  • 延迟要求:< 200ms (P99)

场景2:机器人实时控制

  • 模型:RT-2 (视觉-语言-动作)
  • 控制频率:100Hz
  • 延迟要求:< 10ms (确定性)

场景3:手机端多模型并发

  • 模型:拍照增强、语音识别、键盘预测
  • 功耗预算:< 2W
  • 内存限制:< 4GB
提示

考虑每个场景的关键约束和不同架构的优势。

答案

场景1分析(云端LLM):

  • 选择:脉动阵列
  • 理由:
  • 大batch推理(QPS=1000需要批处理)
  • 矩阵运算占主导(>90%是GEMM)
  • 延迟要求相对宽松(200ms)
  • 需要HBM存储175B参数
  • TPU v4实际案例证明有效

场景2分析(机器人控制):

  • 选择:数据流架构
  • 理由:
  • 严格实时性要求(10ms确定性)
  • Batch=1的低延迟推理
  • 多模态融合的复杂数据流
  • 需要确定性执行保证安全
  • TSP类架构提供最佳延迟保证

场景3分析(手机端):

  • 选择:混合架构
  • 理由:
  • 多个异构模型并发
  • 严格功耗限制需要细粒度控制
  • 不同模型有不同特征(CV/NLP/预测)
  • 需要灵活的资源调度
  • 小的脉动阵列+可编程数据流单元组合

架构决策矩阵: | 因素 | 脉动阵列 | 数据流 | 混合 |

因素 脉动阵列 数据流 混合
大batch ★★★ ★★
低延迟 ★★ ★★★ ★★
确定性 ★★ ★★★ ★★
灵活性 ★★★ ★★★
能效 ★★★ ★★ ★★
成本 ★★★ ★★

常见陷阱与错误

1. 数据流图设计错误

陷阱:创建包含环的数据流图而未正确处理

错误示例:
A → B → C
↑       ↓
└───D───┘

问题:可能导致死锁或无限等待

解决方案

  • 使用初始令牌打破循环依赖
  • 添加缓冲深度避免死锁
  • 考虑使用动态数据流处理循环

2. 静态调度的局限性误判

陷阱:认为静态调度总是最优

if (rare_condition):  // 概率 < 1%
    heavy_computation()  // 1000 cycles
else:
    light_computation()  // 10 cycles

问题:静态调度必须按最坏情况分配资源

解决方案

  • 识别动态行为显著的模块
  • 考虑混合静态-动态方案
  • 使用profile-guided优化

3. 存储容量估算错误

陷阱:仅考虑模型参数大小

错误计算:
Memory = Model_weights

问题:忽略了激活、梯度、工作空间

正确计算

Memory = Weights + Activations + Workspace + Double_buffering

4. 背压处理不当

陷阱:简单的阻塞可能传播

Producer → Buffer → Consumer
   ↓         ↓         ↓
 Block    Full     Slow

问题:局部阻塞导致全局停滞

解决方案

  • 实现分级缓冲
  • 使用虚通道隔离不同流
  • 添加旁路机制

5. 编译时间低估

陷阱:假设编译是一次性成本

问题

  • 模型频繁更新需要重编译
  • 编译时间可能长达小时级别
  • 影响开发迭代速度

缓解策略

  • 增量编译支持
  • 编译缓存机制
  • 快速原型模式

6. 确定性的过度承诺

陷阱:声称完全确定性但有例外

"确定性延迟"但忽略了:

- 错误处理路径
- 资源竞争情况
- 温度节流

最佳实践

  • 明确定义确定性边界
  • 提供最坏情况分析
  • 实施异常处理机制

最佳实践检查清单

架构选择阶段

  • [ ] 工作负载分析
  • 量化计算/访存比
  • 识别并行模式
  • 评估动态行为

  • [ ] 约束条件明确

  • 延迟要求(平均/P99/最坏)
  • 吞吐量目标
  • 功耗预算
  • 成本限制

  • [ ] 架构匹配度评估

  • 创建决策矩阵
  • 考虑未来扩展性
  • 评估生态系统成熟度

设计实现阶段

  • [ ] 数据流图优化
  • 最小化关键路径
  • 平衡计算负载
  • 优化数据局部性

  • [ ] 资源分配策略

  • 避免资源碎片
  • 实现负载均衡
  • 预留调试资源

  • [ ] 存储系统设计

  • 多级缓冲规划
  • Bank冲突避免
  • 数据预取优化

编译器开发阶段

  • [ ] 调度算法选择
  • 评估算法复杂度
  • 实现增量优化
  • 支持多目标优化

  • [ ] 调试支持

  • 保留调试信息
  • 支持性能分析
  • 实现确定性重放

  • [ ] 优化等级

  • 快速编译模式
  • 标准优化模式
  • 极致性能模式

验证测试阶段

  • [ ] 功能验证
  • 单元测试覆盖
  • 集成测试完整
  • 边界条件处理

  • [ ] 性能验证

  • 基准测试suite
  • 实际工作负载
  • 扩展性测试

  • [ ] 可靠性测试

  • 长时间运行稳定性
  • 错误注入测试
  • 恢复机制验证

部署维护阶段

  • [ ] 监控指标
  • 利用率统计
  • 延迟分布
  • 错误率跟踪

  • [ ] 优化迭代

  • 收集真实负载
  • 识别瓶颈
  • 持续优化

  • [ ] 文档维护

  • 架构设计文档
  • 编程指南
  • 最佳实践更新

遵循这些最佳实践可以显著提高数据流架构NPU的设计质量和项目成功率。关键是在灵活性和效率之间找到适合具体应用的平衡点。