资讯详情

脉动阵列深度拆解:AI芯片算力背后的数据接力架构

📅 2026/10/26 16:33:17 | 华诺云谱 👁 阅读
脉动阵列深度拆解:AI芯片算力背后的数据接力架构
TPU、昇腾这些AI芯片的宣传材料里“脉动阵列”四个字出现的频率高得吓人。很多做算法的人看到架构图上一堆小方格第一反应是“这不就是个二维的乘加器堆吗”。说实话我第一次看脉动阵列的论文时也是这么想的直到自己动手写RTL、跑仿真、对着时序图调数据对齐才发现这个结构真正的巧妙之处在于它把所有复杂问题都变成了“数据在合适的时间出现在合适的位置”这么一件事。这篇内容不打算讲玄乎的架构哲学就把它拆开揉碎说说脉动阵列到底是什么、为什么AI芯片离不开它、以及你自己怎么从零搭一个能跑起来的阵列。想真正理解AI芯片算力来源的人不管是做架构的、搞验证的还是想转行做AI硬件的算法工程师这篇都值得花十分钟看完。1. AI芯片为什么绕不开脉动阵列1.1 先看算力从哪里来矩阵乘法的本质与访存墙深度学习推理和训练的绝大部分计算量都集中在矩阵乘法上。无论是全连接层的权重矩阵乘输入向量还是卷积层通过im2col展开后的矩阵乘本质上都是把一个大规模乘加运算塞进硬件。单次乘加操作MAC本身非常便宜一个8位乘法器加一个累加器在先进工艺下功耗只有皮焦量级面积也小得可怜。真正贵的是数据搬运——从DRAM里读一个数要消耗的能量大约是从SRAM读的二十倍是计算本身的几百倍。这个差距就是所谓的“访存墙”。早在二十多年前芯片设计者就意识到如果你的架构每做一次乘加运算都要从外部存储器取一次数据和一次权重那无论把乘法器做到多快整体性能都会被内存带宽卡死芯片大部分时间和功耗都浪费在搬数据上而不是真正的计算。这就是冯·诺依曼瓶颈在并行计算时代的放大版。AI芯片面临的问题尤其极端神经元数量大、连接密集、数据动辄几十MB甚至上百MB光靠提高主频和增加乘法器数量解决不了根本问题。那怎么办答案只有一条路让数据尽量在片上多复用几次提高计算与访存的比例。脉动阵列就是沿着这个思路做到极致的代表。它不是靠某一个聪明的乘法器而是靠整个阵列的组织方式让权重和输入数据在片内像接力一样反复传递每传递一次就完成一次有意义的乘加。这样的结构下一次从DRAM取回的权重可以被阵列里几百个PE重复使用平均每个数据的计算价值被放大了几百倍。1.2 脉动阵列解决什么问题一次数据接力的旅程脉动阵列的概念最早是卡内基梅隆大学的H.T. Kung在1982年提出的。当时没有AI芯片这个概念他的目标很朴素设计一种适合VLSI实现的并行计算结构让数据规律地、节奏一致地在处理器阵列中流动就像血液在心脏的驱动下脉动一样。之后的几十年这个结构一度被认为是“过时的学术玩具”因为通用CPU和GPU的高速发展掩盖了它的光芒。直到谷歌TPU面世脉动阵列才重新站到前台成为AI芯片最核心的算力引擎。脉动阵列的核心思想可以概括成三个关键词空间并行、时间流水、局部通信。它由大量相同的处理单元PE排成二维网格每个PE内部包含乘法器、加法器和一组寄存器。数据不是通过系统总线广播给所有PE而是每个PE只和上下左右相邻的PE通信。整个阵列在外围数据流的驱动下一拍一拍地工作每一拍每个PE从某个方向接收数据和保存在本地的权重做一次乘加再把数据往下一个PE传递。数据像在工厂传送带上流动的零件每个工位PE只做一道工序所有工位同时工作。这种结构有几个天生的优点一是数据复用极其高效权重只要加载一次就可以参与多轮计算输入数据也在阵列里被反复使用二是通信局部化不依赖昂贵的长线广播布局布线容易时钟频率能做得很高三是高度规则化PE单元完全相同设计验证复杂度低很容易通过复制扩展规模。缺点也很明显——数据必须按规则节奏送入一旦输入矩阵形状不规则、稀疏度高利用率会大幅下降。但深度学习场景里矩阵乘法规则而密集脉动阵列几乎是量身定制的。2. 脉动阵列基本原理拆解2.1 一个PE内部到底有什么想理解整个阵列先得把一个PE吃透。一个最典型的权重固定脉动单元内部只有四个关键部件乘法器、加法器、权重寄存器、输入数据寄存器。工作流程可以用一句话概括每个周期输入数据从上一级PE通过寄存器进来同时权重从本地寄存器读出两者相乘结果加到累加寄存器上然后输入数据原封不动地传给下一级PE。注意这里有个容易忽略的细节——乘法器和加法器其实是按流水线方式工作的数据到达后先乘结果在下一拍才加进累加器。所以单个PE也有两三级流水这也是后面时序对不齐的隐患来源之一。权重寄存器在每个PE里是独立的。这意味着在计算开始前整个阵列需要先把权重“铺”进去这个过程叫权重加载。权重加载通常和计算是重叠的或者利用计算间隙分批进行。对于一层卷积来说权重量并不大完全可以做到一次性全部加载进阵列。比如256×256的阵列权重最多6.5万个几百KB就装下了相比动辄几MB的中间特征图权重加载的开销其实很小。PE的位宽选择是个关键权衡。推理场景普遍用INT8乘法器面积小、功耗低算力高。但累加器的位宽要宽得多——如果一次卷积要累加上千个数每个数是INT8乘积是INT16累加器至少得INT32才能保证不溢出。谷歌TPU v1的PE就是8位输入、32位累加。有些设计会再加饱和逻辑累加结果超过表示范围时直接截断到最大或最小值防止溢出回绕导致结果完全错误。2.2 三种数据流谁动、谁不动、往哪动脉动阵列按“哪份数据是静止的”可以分为三种典型模式学术界叫weight stationary、input stationary和output stationary。名字看着学术意思很直白。权重静止WS模式下权重在计算过程中一动不动输入数据沿阵列的一个方向流动部分和在另一个方向累加。这种模式适合权重复用度高的场景最典型的就是全连接层和卷积层TPU用的就是这种。输入静止IS模式下反过来输入特征图待在PE里不动权重沿阵列流动部分和累加。这种模式适合输入图像数据复用度低的场景有一些边缘设备的加速器采用这种方式但整体不如WS常见。输出静止OS模式则让部分和待在PE里输入数据和权重都流动每个PE负责生成输出矩阵的一个元素所有部分和都在本地累加完最后只需要把结果读出去。这种模式的优势是减少了部分和数据的搬运适合卷积的某些变形结构。下表把三种模式的要点整理在一起方便对照。实际产品中很少用“纯”某一种模式大厂芯片往往会在列方向做权重静止、行方向做数据流动的混合设计再配合编译器做数据复用分析。理解三种模式本质上是在理解一个问题你想让哪份数据停留在片上、哪份数据流动起来这取决于你的瓶颈到底是权重带宽、输入带宽还是输出带宽。数据流模式静止的数据流动的数据典型优势代表应用权重静止WS权重输入特征图、部分和权重复用高适合卷积/全连接谷歌TPU输入静止IS输入特征图权重、部分和输入复用度高适合通道方向累加场景部分边缘加速器输出静止OS部分和输入、权重减少部分和搬运输出写回少部分自定义加速器2.3 数据该什么时候到流水线延迟与输入时序真正劝退很多初学者的不是PE结构而是“数据什么时候送进哪个PE”这件事。脉动阵列不是每个周期把整块输入矩阵广播给所有PE而是按一定的顺序让数据沿着对角线方向错拍输入。为什么是对角线因为数据在PE间流动需要时间。假设你想让第i行第j列的PE在某一拍同时收到来自上面的输入和来自左边的权重那么数据就得提前按距离差对应的周期数错开出发。用一个小的例子来说明。假如有一个2×2的权重静止阵列要计算一个2×2的矩阵乘法。输入矩阵A的每个元素从阵列上方送入每隔一个周期向下移动一行同时每一行又是错拍进入的——第0拍送a00第1拍同时送a01往右移和a10往下移第2拍送a11。这样的节奏下a00和权重w00在第1拍相遇完成第一轮计算a01和w01在第2拍相遇以此类推。所有PE在每个周期都有有用的计算在发生没有任何一个周期在空转这就是完美流水。流水线延迟是个绕不开的指标。阵列从第一拍开始接收数据到第一个有效输出出来需要经过阵列的行数加列数再减一个周期的延迟。假设M×N的阵列第一个结果大约要等MN-1拍才能出来。这听起来很亏但一旦流水线填满之后每个周期都有完整的结果输出。所以阵列规模越大填满一条流水线的“启动成本”相对摊销得就越低吞吐率才是脉动阵列追求的核心指标。理解和掌握这个错拍送数的规律基本就掌握了脉动阵列调度的密钥。3. 自己动手搭一个脉动阵列实操过程3.1 明确目标和性能指标先算账再动手我建议任何想亲手实现脉动阵列的人先从目标反推规格。不要一上来就想着做一个256×256的大阵列那是流片级的工作量。先用一个中等规模的项目练手目标定在“实现一个8×8的权重静止脉动阵列完成16×16矩阵乘法主频50MHz用FPGA跑通”。这个小目标已经能覆盖所有核心难点又不至于被工程细节淹没。定了目标之后算一笔账。8×8的阵列每个周期完成64次MAC50MHz主频下峰值算力是64×2×50M 6.4 GOPS。一个16×16×16的矩阵乘法总共需要16×16×16 4096次MAC理想情况下需要64拍填满流水线、再加若干拍收尾。对照峰值算力这个规模的设计在RTL仿真里几秒钟就能跑完非常适合做正确性验证。矩阵规模与阵列规模不匹配是首先要面对的问题。16×16的矩阵要用8×8的阵列算必须做分块把16×16的矩阵分割成4个8×8子块分别计算再累加。分块逻辑不复杂但会把调度复杂度翻倍。这也是为什么真实芯片设计里编译器要参与到数据排布和调度中来单纯靠硬件控制器处理所有分块策略会把自己绕晕。3.2 从单PE到阵列再到控制器三步走动手写代码的时候我建议严格按三个阶段推进每个阶段都有明确的验证通过标准不要直接写一整个阵列再debug那是灾难。第一步先写单个PE的行为模型。用SystemVerilog或者你喜欢的高层语言都行重点是把乘加、数据传递、权重寄存这些行为写清楚。单PE的测试很简单给定一个输入和权重观察输出是不是乘积。第二步把PE连成一行验证数据接力。一行4个PE串起来从一端喂数据观察数据是不是每个周期移动一格。这一步看起来简单但能抓出很多寄存器时序问题。我自己的经验是一行PE跑通之后二维阵列的“排线”工作其实已经完成一半了。第三步把多行拼成二维阵列加入权重加载逻辑、累加结果读出逻辑以及最关键的控制器。控制器是整个设计的灵魂。它负责三件事按正确的时序把输入数据送入阵列、在计算开始前加载权重、在结果计算完成后把数据从阵列中搬出去。控制器的实现不复杂本质是一个有限状态机加几个计数器难的是想清楚状态转换的边界条件。我的建议是先在纸上画出时序图标清楚每一拍哪个PE在算什么、数据在哪个位置再写代码。时序图画对了代码基本一次通过时序图没画代码必出bug。3.3 关键参数选型位宽怎么选、零填充怎么处理位宽是每个设计者都要做的选择题。先定数据位宽再定乘积位宽最后定累加器位宽。我做的设计里输入和权重都是INT8乘法器输出是INT16INT8相乘最大不会超过16位累加器给了INT32。32位累加有什么用它允许你在不丢精度的情况下最多累加2^31/32767 ≈ 6.5万个乘积对绝大多数卷积层来说绰绰有余。你的层如果通道数特别多记得检查累加上限必要时把累加器扩到40位。边界数据和填充padding处理是另一个容易被忽略的坑。卷积计算中特征图边缘需要补零。你的输入扫描逻辑要把填充的零也作为正常数据送进阵列否则边缘像素的计算结果就是错的。很多初写脉动阵列的人在这里翻车内部像素全对边缘像素全错找了好久才发现是padding数据没对齐。还有一个细节叫“数据有效标志”。输入数据流里可能夹杂无效周期比如权重加载期间不能送输入数据。给每个输入数据配一个1位的valid信号跟着数据一起在阵列里流动这样每个PE都能知道当前周期进来的数据是不是有效。这个标志位的开销很小但能让调试体验提升好几个档次——你只需要盯着valid在哪个PE断掉了就知道数据流在哪一拍出了问题。3.4 性能估算与流水线时序表写完了设计下一步是估算性能、画出实际的时序表验证自己的预期。以一个8×8权重静止阵列计算8×8×8的矩阵乘法为例整个流程可以分成三个阶段。阶段一权重加载64个权重需要送入对应PE假设一个周期送一个需要64拍但这个阶段可以和上一层的计算尾部重叠通常不算额外开销。阶段二数据流入输入矩阵按错拍方式流入流水线开始填充。第一个输入在拍0进入左上角PE经过7行7列的对角线传播第一个完整结果大约在第14拍出现在右下角PE的输出端。阶段三稳定输出从第14拍开始之后每个周期都会有一个输出结果从阵列边缘读出直到所有64个输出都出来。画一张简化的时序表会看得更清楚。拍0到拍7是流水线填充期阵列里只有部分PE在工作利用率从1/64逐渐提高到满拍8到拍13是部分PE开始产出部分和的阶段拍14以后进入满载状态。如果以总周期数63拍来算88-1之后还有稳定输出期有效计算MAC数4096等效利用率约81%。剩下约19%的周期损耗在流水线填充和排空上。阵列越大、计算批次越长这个损耗比例就越低这也是脉动阵列被设计成大阵列的根本原因——摊销启动成本。参考下面的流水线阶段表你可以快速评估自己的设计在哪个阶段浪费了周期阶段周期范围阵列状态说明权重加载0-63拍权重写入PE可与上次计算尾段重叠流水线填充64-71拍部分PE工作利用率从低到满递增满载计算72-78拍全部PE工作每个周期产生8个部分和流水线排空79-85拍部分PE工作尾部数据输出无法避免4. 常见问题与调试验证实录4.1 数据错位与流水线气泡最常踩的坑脉动阵列调试中几乎所有人都至少遇到过一回“计算错乱”的问题。症状是输出的矩阵大部分是对的但某些位置的值完全不对甚至出现负数和零交错。我踩过最典型的一个坑是输入数据的发送节奏多等了一拍。代码里觉得“稳妥起见”在每行输入之间额外加了一个空周期结果整个阵列的对角线对齐全乱了每个PE比预期晚一个周期收到数据部分和与权重的匹配关系全部错位。调试这类问题唯一的正确手段是画波形图在仿真器里把每个PE的输入数据和权重数据拉出来逐周期对照。不要相信打印信息打印信息会把时序信息糊掉。我习惯在每个PE里加一个调试用的计数器记录它第一次收到有效数据是在第几个周期和理论预期对照很快就能定位是哪一行数据流断掉了。另一个常见问题是流水线气泡。气泡指的是本来该有数据流动的周期却因为上游数据没准备好而空置了一拍。气泡会直接拉低吞吐率。气泡产生的原因经常是上游的SRAM读取出现了bank冲突导致数据供应不连续。解决方法是把输入缓冲区做成双缓冲或者用FIFO做弹性缓冲。双缓冲的代价是面积但换来的好处是完全不依赖上游时序阵列不会因为上游偶发阻塞而空转。4.2 数值精度与溢出INT8的快乐与烦恼INT8推理加速是现在AI芯片的主流玩法但它带来的数值问题也足够让人头疼。最简单的溢出场景累加器只有16位两个INT8求和的累加值超过32767符号位翻转变成负数。这个bug很隐蔽因为大部分数据正常只有遇到大数值样本才爆。我的建议是设计阶段就给累加器留足余量不要事后补救。饱和截断策略也要提前想清楚。累加溢出后是让值直接回绕wrap-around还是饱和到最大正值或最小负值回绕会让结果严重失真饱和至少不会让增长方向出错。深度学习模型通常对单点的饱和误差有一定容忍度所以绝大多数芯片选择饱和策略。我见过一些设计为了省逻辑不做饱和结果特定层输出全错精度损失惨重。饱和逻辑的电面积很小但能挡住最危险的溢出回归错误。关于量化校准我建议做一个额外的离线分析用一小批真实数据跑一遍你的脉动阵列模型和参考量化模型统计累加器最大值确认没有接近INT32上限的异常情况。跑真实数据比随机数据更能暴露问题——真实激活值的分布往往极度不平衡某个通道的数值偏大完全可能。4.3 验证策略参考模型对比与随机测试脉动阵列的验证是个系统工程不能指望写一个测试bench跑一遍随机信号就完事。我用的三层验证策略效率很高推荐给你。第一层是单元测试验证单个PE的乘加、数据传递行为覆盖边界输入如全零、全一、最大值、最小值。第二层是阵列级验证用Python或MATLAB写一个参考模型把同一个矩阵乘法用普通三重循环算一遍与RTL仿真输出逐元素对比。注意对比要按累加器的位宽和饱和策略做同样的处理否则数值对不上。第三层是随机化回归测试。用SystemVerilog的随机约束或者直接在Python端生成随机矩阵跑大量随机用例每次都和参考模型比对。随机测试能抓出设计里超出你“想得到的边界”的bug。我调试过程中最值回票价的就是随机测试抓出来的一个bug当输入矩阵包含负数时我的权重加载逻辑在读负数权重时符号位处理错了导致一行结果全部变号。这个bug要是用真实网络权重测试可能几十轮才遇到一次随机测试几分钟就暴露了。4.4 从仿真到上板频率、时序和功耗的现实问题仿真通过只代表逻辑正确能不能上板稳定运行完全又是另一回事。综合后最常遇到的问题是STA时序分析报告的时序违例。脉动阵列的关键路径几乎一定出现在乘加链路里数据从寄存器出来经过乘法器再经过累加加法器组合逻辑延迟太大超过了时钟周期。解决办法有两个方向一是降低时钟频率这最省事但牺牲性能二是把PE内部的乘法器和加法器之间再加一级流水寄存器把单周期组合逻辑拆成两级。第二个方向性能更好但会给时序表的计算增加一拍的延迟所有控制逻辑都要跟着调整。功耗问题在上板阶段也会暴露。脉动阵列是“全速跑”的架构只要数据在流动所有PE都在翻转动态功耗非常可观。我看过一块FPGA跑8×8阵列核心电流比预期高了三成散热片烫手。降低功耗的常用手段包括门控时钟——在权重加载阶段把计算单元的时钟关掉以及数据门控——输入数据无效时防止乘法器输入端翻转。这些优化都不复杂但在设计初期就得留好控制信号事后再加会让控制逻辑变得混乱。关于实测表现我那个8×8阵列在50MHz下跑通了16×16矩阵乘法有效计算时间大约85个周期和理论估算一致。换成更大的16×16阵列后同样跑50MHz效率提升到接近90%验证了前面说的“阵列越大启动开销摊薄越划算”的判断。这个规律在真正设计芯片尺寸时极其重要它直接影响你是做8×8还是32×32。5. 写在最后的一点个人体会脉动阵列真正让我觉得了不起的不是它把多少个乘法器堆在了一起而是它解决了一个极其本质的工程问题在芯片上怎么让数据搬运的成本降到最低。它用的方法甚至不“智能”——就是让所有PE按照同一个节奏、规规矩矩地接力像一支训练有素的仪仗队。这种用结构换效率的思路和现在动不动就堆超大核、靠高主频碾压的做法相比显得特别优雅。我调试脉动阵列那几周印象最深的不是哪个bug而是第一次在波形图里看到数据像海浪一样按对角线扫过阵列的瞬间——那一刻你会突然理解为什么它叫“脉动”。如果你也想入门AI芯片我真心建议亲手写一个8×8阵列跑通矩阵乘你会比看一百篇架构文章都更理解算力是怎么回事。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑