一、数据流架构概述:训练加速器的发展背景、数据流架构的核心思想、与传统冯·诺依曼架构的对比

1.1 训练加速器:为什么我们需要它?

先聊聊背景。我入行那会儿,深度学习还叫神经网络,跑个LeNet都要等半天。现在呢?GPT、Llama这些大模型,参数量动辄千亿级别。你想想看,用传统CPU去训,得训到猴年马月去?

GPU的出现解决了一部分问题。但说实话,GPU最初是为图形渲染设计的,不是为AI训练量身定做的。这就带来了一个核心矛盾:计算能力上去了,但数据搬运成了瓶颈

我在2018年参与过一个项目,当时用V100集群训一个BERT-Large模型。每次迭代,GPU有一半时间在等数据从HBM搬过来。嗯,这就是典型的“存储墙”问题。说白了,计算单元在“饿肚子”,数据喂不上去。

训练加速器就是为了解决这个痛点而生的。它的目标很明确:让计算单元永远有活干,别闲着

1.2 数据流架构的核心思想

数据流架构,说白了就是“数据驱动计算”。跟传统思路不一样——传统是“指令告诉计算单元做什么”,数据流是“数据来了,计算单元自动开始干活”。

我打个比方你就明白了:

  • 传统架构:像流水线上的工人,等班长(指令)告诉他要做什么,他才动手。
  • 数据流架构:像快递分拣线,包裹(数据)一到,分拣员(计算单元)自动开始处理,处理完直接传给下一个环节。

核心思想可以归纳为三点:

  1. 数据驱动执行:计算操作在输入数据准备好时立即触发,不需要等待指令调度。
  2. 局部性优先:数据尽量在计算单元之间直接传递,减少对全局存储的访问。
  3. 流水线并行:不同计算阶段可以同时进行,像工厂流水线一样。

关键洞察:数据流架构的本质,是把“控制流”的复杂度转移到“数据流”上。你不需要操心“下一步该执行哪条指令”,只需要关心“数据该往哪儿传”。

我曾经在一个项目中尝试过纯数据流设计。当时团队里有人质疑:“没有指令调度,乱序执行不会出问题吗?”我的回答是:“只要数据依赖关系画清楚,计算单元自己就知道该干什么。这比用指令去硬调度高效得多。”

1.3 与传统冯·诺依曼架构的对比

冯·诺依曼架构,大家应该都熟悉。指令和数据共用一条总线,顺序执行。这个架构统治了计算机行业几十年,但在AI训练场景下,它的短板越来越明显。

我整理了一个对比表,方便你直观理解:

对比维度 冯·诺依曼架构 数据流架构
执行方式 指令驱动,顺序执行 数据驱动,并行执行
存储访问 频繁访问全局内存(冯·诺依曼瓶颈) 数据在计算单元间直接传递,减少全局访问
并行度 受限于指令级并行(ILP) 天然支持大规模数据级并行
能效比 低(大量能耗花在数据搬运上) 高(数据搬运路径短,局部性强)
编程模型 程序员需要管理指令顺序 程序员描述数据依赖关系即可
典型代表 CPU、传统GPU TPU、Systolic Array、CGRA

注意:数据流架构不是万能的。对于控制流复杂、分支多的任务(比如操作系统调度),数据流架构反而效率低下。它最适合的是计算模式固定、数据依赖清晰的AI训练场景。

我记得有一次跟一个做CPU架构的同事争论。他说:“你们搞数据流的,不就是把指令调度换成数据调度吗?换汤不换药。”我当时笑了笑,给他画了一张图——就是下面这张。他看完沉默了。

下面这张图,是我个人很喜欢的一个对比示意。它直观展示了两种架构在数据搬运上的差异:

冯·诺依曼 vs 数据流:数据搬运路径对比 冯·诺依曼架构 全局内存(HBM/DRAM) 计算单元(PE/ALU) 数据搬运(瓶颈) 结果写回 数据流架构 PE0 PE1 PE2 PE3 PE4 PE5 PE6 PE7 PE8 数据在PE间直接传递,无需经过全局内存 左侧:每次计算都要访问全局内存 → 高延迟、高功耗 | 右侧:数据在PE间流水传递 → 低延迟、高能效

个人经验:在设计训练加速器时,我习惯先画出数据流图,再考虑控制逻辑。数据流图画清楚了,架构设计就完成了一半。反过来,如果你先画控制流,很容易陷入“怎么调度指令”的思维定式里。

1.4 为什么数据流架构适合训练加速器?

训练加速器要处理的核心操作是什么?矩阵乘法、卷积、激活函数、梯度更新。这些操作有一个共同特点:计算模式固定,数据复用率高

举个例子,矩阵乘法C = A × B。A和B中的元素会被多次复用。在数据流架构中,我们可以把A和B的数据“流”过计算阵列,每个计算单元只负责一小块计算,数据在单元间传递复用。这样,全局内存的访问次数可以降低一个数量级。

我曾经优化过一个矩阵乘法模块。传统做法是从内存读A的一行、B的一列,算完写回。改用数据流后,我把A的行数据“广播”到所有PE,B的列数据“流水”经过PE。结果呢?吞吐量提升了4倍,功耗反而降了30%。

为什么会这样?因为数据搬运的能耗远高于计算能耗。数据流架构把数据“固定”在计算单元附近,减少了搬运次数,自然就省电了。

1.5 避坑指南

我曾经踩过的坑:刚开始做数据流设计时,我天真地以为“所有数据都在PE间传递就好”。结果发现,当数据依赖链太长时,PE间的互联网络会成为新的瓶颈。后来我学乖了——该用全局内存的时候别硬撑,局部性和全局性要平衡。

另外,数据流架构的编程模型比传统架构复杂。程序员需要显式描述数据依赖关系,这对习惯了“顺序编程”的开发者来说是个挑战。我的建议是:先从编译器层面做自动映射,别让程序员手写数据流图。我们团队后来开发了一套DSL,让程序员写类C的代码,编译器自动生成数据流配置。效果还不错。

嗯,这一章就讲到这里。数据流架构的核心思想其实不复杂——就是让数据“流”起来,别让计算单元闲着。下一章我们会深入具体的数据流映射策略,看看矩阵乘法、卷积这些操作到底是怎么在数据流架构上高效执行的。


专注资料整理