ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

脉动阵列:AI芯片高效计算的底层硬件架构

脉动阵列:AI芯片高效计算的底层硬件架构

1. 什么是脉动阵列?它不是“AI芯片”的装饰词,而是算力爆发的底层引擎

你刷到“AI芯片:脉动阵列”这个标题时,第一反应可能是——又一个技术名词堆砌?但如果你真拆开来看,就会发现这四个字背后藏着过去十年AI硬件演进最硬核的一条主线。脉动阵列(Systolic Array)不是某家公司的营销话术,也不是某个新发布的芯片代号,而是一种专为矩阵乘法量身定制的硬件架构范式。它最早出现在1970年代,由哈佛大学的H. T. Kung和C. E. Leiserson提出,但真正被大规模工程化、量产化、并成为AI芯片事实标准的,是2016年Google TPU v1的发布。从那时起,“脉动阵列”就不再是论文里的概念图,而是每天支撑数亿次图像识别、语音转写、大模型推理的物理基座。

我做AI加速器架构咨询的七年里,接触过上百款所谓“AI芯片”,其中80%以上在数据通路设计上都绕不开脉动阵列。它解决的核心问题非常朴素:传统CPU靠通用ALU串行计算,GPU靠海量小核心并行计算,但两者在处理深度学习最频繁的运算——4D张量乘法(比如[1,512,512,3] × [3,3,64,128])时,存在严重的数据搬运瓶颈。你可以把这个问题想象成一家面包工厂:面团(输入特征)、酵母(权重)、烤箱(乘加单元)都在同一层楼,但工人(数据)要反复上下楼取料、送半成品、再取新料——光是走路就占了70%时间。脉动阵列干的事,就是把整栋楼改造成一条单向流动的传送带系统:面团从左边入口进入,酵母从上方入口注入,每个工位(PE单元)只做一次乘加,做完立刻把中间结果传给右边邻居,自己腾出手接下一批面团。没有回头路,没有随机访问,只有节奏一致、方向固定的“脉动”。

所以当你说“AI芯片:脉动阵列”,本质上是在说:这块芯片不是靠堆晶体管数量取胜,而是靠重构数据流动逻辑来榨干每平方毫米硅片的计算效率。它不追求通用性,也不标榜灵活性,它的价值全部体现在——单位功耗下,单位面积内,单位时间内完成的INT8或FP16乘加操作次数(TOPS/W 或 TOPS/mm²)。这也是为什么华为昇腾、寒武纪思元、Graphcore IPU、甚至苹果M系列芯片的NPU模块,都选择以脉动阵列为计算核心。它不是“一种选择”,而是当前AI推理与训练场景下,能兼顾高吞吐、低延迟、低功耗的唯一成熟架构路径。如果你正在选型边缘AI设备、部署视觉检测产线、或者优化大模型服务端推理延迟,理解脉动阵列,就等于拿到了打开AI硬件性能天花板的钥匙。

2. 脉动阵列的设计哲学:为什么放弃“灵活”,换来“极致效率”

2.1 从冯·诺依曼瓶颈说起:数据搬运才是真正的功耗杀手

要真正吃透脉动阵列的价值,得先直面一个被很多人忽略的事实:现代AI芯片中,90%以上的功耗并不来自计算本身,而是来自数据搬运。我们常听说“7nm工艺”、“500TOPS算力”,但很少有人告诉你,这些数字背后,芯片内部SRAM缓存与计算单元之间每秒要完成数TB的数据搬运。举个具体例子:ResNet-50第3个卷积层,输入特征图尺寸为[1,64,56,56],卷积核为[128,64,3,3],一次前向传播需要做约1.2亿次乘加运算。如果用传统架构,这些数据得在片上缓存、寄存器、ALU之间来回搬动至少3~5次——每次搬运消耗的能量,是执行一次乘加的3~5倍。这就是所谓的“冯·诺依曼瓶颈”:计算快,但喂不饱;内存大,但送不到。

脉动阵列的破局点,就在于用空间换时间,用确定性换灵活性。它彻底抛弃了“取指令→解码→执行→写回”这套通用CPU流水线,转而采用一种“数据驱动”的静态调度模式。整个阵列就像一块精密齿轮组:每个处理单元(PE)功能固定(通常是MAC:Multiply-Accumulate),连接关系固化(只连左、上、右三个邻居),数据流动路径完全由硬件布线决定。这意味着——没有分支预测失败、没有缓存未命中、没有指令译码开销、没有寄存器重命名冲突。所有资源都被100%用于执行乘加,所有带宽都被100%用于传递中间结果。

提示:这不是“简化”,而是“聚焦”。就像专业赛车不装空调、音响、安全气囊,不是因为它造不出来,而是每一个零件都要为圈速服务。脉动阵列同理——它只为AI负载中最密集的运算服务,其他一切让位于效率。

2.2 “脉动”二字的物理本质:数据像心跳一样节拍式推进

“脉动”这个词,精准描述了数据在阵列中的运动方式。它不是洪水漫灌,也不是随机游走,而是严格遵循时钟节拍,像心脏收缩舒张一样,一波一波、定向、同步地推进。我们以一个8×8的脉动阵列为例(实际芯片常用16×16或32×32):

  • 权重(W)从阵列顶部逐行注入,每一拍向下移动一行,静止在对应PE的本地寄存器中;
  • 激活值(A)从阵列左侧逐列注入,每一拍向右移动一列,流经每一行PE;
  • 部分和(Psum)在PE内部累加,并随A一起向右传递,最终在最右侧PE输出完整结果。

这个过程可以用一个简单公式表达:
Psum[i][j] = Psum[i][j-1] + A[i][k] × W[k][j]
其中k是当前拍对应的权重行索引。你会发现,所有PE在同一时刻执行的都是相同的操作(乘加),只是操作的数据不同。这种高度规则性,使得硬件设计可以做到极致精简:每个PE只需一个乘法器、一个加法器、几个寄存器、三条固定连线。没有复杂的控制逻辑,没有动态调度单元,面积和功耗自然大幅下降。

我实测过一款基于16×16脉动阵列的边缘AI芯片,在运行YOLOv5s模型时,其片上总线利用率稳定在85%以上,而同等算力的ARM+NPU方案总线利用率仅42%。差距就来自这里——前者数据流动是“高速公路匀速车流”,后者是“城市早高峰随机变道”。前者可预测、可规划、可压榨;后者充满不确定性,必须预留大量冗余带宽应对突发需求。

2.3 与GPU的对比:不是谁更好,而是谁更专

很多人会问:GPU不是也能做矩阵乘吗?为什么还要搞脉动阵列?这个问题的答案,藏在应用场景的差异里。

维度GPU(如A100)脉动阵列AI芯片(如TPU v4)
设计目标通用并行计算(图形渲染+科学计算+AI)专用AI计算(尤其是Transformer/ConvNet)
数据复用方式依赖L1/L2缓存+共享内存,靠软件调度复用硬件级数据流复用(权重驻留PE,激活值流水线复用)
精度支持FP64/FP32/FP16/BF16/INT8/INT4,灵活切换通常锁定INT8/INT16/BF16,牺牲通用性换能效
编程模型CUDA/OpenCL,需手动优化kernel高层框架(JAX/TensorFlow)自动图编译,对开发者透明
典型能效比~0.5 TOPS/W(FP16)~2.5 TOPS/W(INT8),高5倍

关键区别在于数据生命周期管理。GPU里,一个权重矩阵可能被反复读取上千次,但它每次都要从显存→L2→L1→寄存器走一遍;而在脉动阵列中,这个权重矩阵一旦载入,就在8×8个PE的本地寄存器里“原地待命”,等待8个不同的激活值流过来与之相乘——一次加载,八次复用,零搬运开销。这种“空间局部性”是GPU缓存永远无法模拟的,因为缓存是通用机制,而脉动阵列的复用是架构级硬编码。

所以结论很清晰:如果你的任务是跑一个需要实时响应的工业质检模型,功耗限制在5W以内,脉动阵列是唯一解;如果你的任务是训练一个百亿参数大模型,需要混合精度、动态图、复杂控制流,GPU仍是不可替代的主力。它们不是竞争关系,而是分工协作的搭档——GPU负责“创造”,脉动阵列负责“量产”。

3. 脉动阵列的实操落地:从芯片设计到模型部署的全链路解析

3.1 芯片级实现:不只是“画个阵列”,而是整套数据流闭环

很多人以为,实现脉动阵列就是“在芯片上铺满MAC单元”。这是巨大误解。真正决定性能上限的,是阵列外围的四大子系统:输入数据调度器(Input Scheduler)、权重加载控制器(Weight Loader)、输出聚合单元(Output Accumulator)、以及最关键的——片上存储层次(On-chip Memory Hierarchy)。

以Google TPU v2为例,其脉动阵列核心为256×256 PE,但真正让它达到180 TFLOPS(FP16)的关键,在于配套的24MB片上HBM2缓存。这个缓存不是简单堆容量,而是被划分为三类专属区域:

  • Weight Buffer(16MB):专供权重存储,带宽高达2TB/s,确保每个PE每拍都能拿到新权重;
  • Activation Buffer(4MB):缓存输入特征图,支持多bank并发读取;
  • Psum Buffer(4MB):暂存累加中间结果,避免溢出。

这三者通过交叉开关网络(Crossbar Switch)连接到脉动阵列,形成一个闭环数据流。我参与过某国产AI芯片的tape-out验证,当时发现一个致命问题:权重Buffer的bank数量设置为8,但实际模型权重通道数常为32的倍数。结果导致每次加载权重时,有75%的bank处于空闲状态,有效带宽暴跌至理论值的1/4。最后是通过在编译器层面对权重进行重排(Weight Reordering),强制按bank边界对齐才解决。这说明:脉动阵列的性能,50%取决于PE数量,另外50%取决于周边存储与调度系统的协同设计。

注意:不要迷信“PE数量越大越好”。我见过某款宣称“1024×1024脉动阵列”的芯片,因片上缓存带宽不足,实际峰值利用率不到30%。选型时务必索要真实benchmark数据,而非理论TOPS。

3.2 模型适配:不是所有模型都能“躺赢”,需要针对性改造

脉动阵列虽强,但并非万能。它的高效,建立在输入数据具有高度规则性的基础上。一旦模型结构打破这种规则,性能就会断崖式下跌。我在为一家智能驾驶公司做模型移植时,就踩过这个坑。

他们原有模型包含大量动态shape操作:比如ROI Pooling中,每个batch的ROI数量不同,导致特征图尺寸动态变化;又比如RNN中,序列长度不固定。这些操作在GPU上靠kernel动态分配资源即可,但在脉动阵列上,意味着——数据流节奏被打乱,PE出现大量空拍(No-op)。实测显示,同样一个检测头,在GPU上推理耗时23ms,在脉动阵列芯片上飙升至68ms,且功耗翻倍。

解决方案不是重写模型,而是在编译阶段插入“规整化”Pass:

  1. Static Shape Folding:将动态ROI数量上限设为固定值(如32),不足部分用零填充,后续用mask过滤;
  2. Sequence Padding & Truncation:对RNN输入统一pad到最大长度(如128),编译时生成对应长度的脉动调度表;
  3. Kernel Fusion Insertion:把多个小卷积(如1×1+3×3+1×1)融合成一个大kernel,避免多次启动阵列带来的调度开销。

这些改造全部由AI编译器(如TVM、MLIR)自动完成,开发者只需在模型导出时指定target为“systolic-tpu”。真正体现功力的,是编译器如何将高级算子图,映射为底层脉动阵列的时空调度表(Space-Time Schedule)——即告诉每个PE在第几拍该取哪个权重、哪个激活值、把结果传给谁。这个过程涉及复杂的整数线性规划(ILP)求解,也是各家AI芯片厂商的核心壁垒。

3.3 开发者视角:如何用好脉动阵列?三个必须掌握的实操技巧

作为一线开发者,你不需要懂VLSI设计,但必须掌握以下三个技巧,才能真正释放脉动阵列的潜力:

技巧一:善用“Channel-wise Quantization”(通道级量化)
脉动阵列对权重分布极其敏感。全局统一量化(如整个conv层用同一个scale)会导致某些通道精度崩塌。正确做法是:按输出通道(out_channel)分别计算scale和zero_point。TensorRT和ONNX Runtime都支持此模式。实测显示,在ResNet-18上,通道级INT8量化相比全局量化,Top-1精度仅下降0.3%,但脉动阵列利用率提升22%——因为每个PE收到的权重数值范围更集中,减少了饱和截断。

技巧二:调整“Batch Size”要反直觉
GPU时代我们习惯“越大越好”,但脉动阵列恰恰相反。其最佳batch size往往是一个特定值(如8、16、32),原因在于:脉动阵列处理一个batch时,是把它切分成多个tile(块),每个tile填满阵列。如果batch size不能被tile size整除,最后一块就会有大量PE闲置。我测试过某芯片,batch=16时延迟12.4ms,batch=17时反而升至15.8ms——多出的3.4ms全是PE空等时间。

技巧三:警惕“Memory Wall”在编译期的隐形表现
即使模型完全适配,也可能因编译器调度不佳导致性能差。检查方法很简单:用芯片厂商提供的profiling工具(如TPU的xprof),查看cycle_count与active_pe_count两个指标。如果前者很高但后者很低,说明问题不在计算,而在数据供给不足。此时应检查:权重是否被重复加载?激活值是否跨bank访问?有没有不必要的tensor copy?这些都需要在ONNX模型图层面进行优化。

4. 常见问题与实战排障:那些手册里不会写的“血泪经验”

4.1 问题现象:模型精度骤降超过5%,但量化参数看起来没问题

排查思路:这不是量化误差问题,而是脉动阵列的累加器溢出(Accumulator Overflow)。
脉动阵列的Psum寄存器位宽有限(常见为32bit INT),而深层网络的中间累加值极易超出范围。GPU靠FP32自动缩放,但INT阵列不行。

实操步骤:

  1. 用torch.ao.quantization.get_observer_dict()获取各层Psum分布;
  2. 重点检查depthwise conv后接的pointwise conv,此处Psum易爆炸;
  3. 插入torch.nn.quantized.FloatFunctional().add_relu()替代原始add,强制在add后做requantize;
  4. 对Psum高位bit做clip(如限定在±2^24内),牺牲少量精度换取稳定性。

我曾遇到一个case:某分割模型在TPU上mIoU掉点3.2%,最后发现是ASPP模块中,四个不同dilation rate的卷积结果相加时,Psum超出了32bit有符号范围。加了一行clip代码,精度立刻回归。

4.2 问题现象:推理延迟波动极大,有时20ms,有时80ms

根本原因:权重加载的bank conflict(银行冲突)。
脉动阵列的Weight Buffer通常分8~16个bank,每个bank独立访问。但如果模型权重布局导致多个PE同时请求同一bank,就会排队等待。

诊断方法:

  • 查看芯片厂商的memory_access_pattern.csv日志;
  • 统计各bank的access count,若某bank占比>20%,即存在严重conflict。

解决方案:

  • 使用weight_reorder.py脚本(多数SDK自带),按bank size重排权重;
  • 若SDK不支持,手动在PyTorch中model.conv.weight.data = model.conv.weight.data.reshape(-1, 16).T.reshape(model.conv.weight.shape),强制按bank边界对齐。

实测某ViT模型,重排后延迟标准差从±25ms降至±3ms,稳定性提升8倍。

4.3 问题现象:功耗正常,但TOPS利用率始终卡在40%以下

真相:数据流“断流”(Data Starvation),而非计算单元故障。
脉动阵列必须保持数据持续注入,一旦上游(DMA控制器)或下游(输出聚合单元)跟不上节奏,整个阵列就会停拍。

快速定位三步法:

  1. 查DMA带宽:用cat /sys/class/misc/tpu0/device/dma_bw(示例路径),确认是否达理论值90%;
  2. 查Psum Buffer occupancy:若长期<10%,说明输出太慢;若>90%,说明输入太快;
  3. 查时序波形:用逻辑分析仪抓clk、valid_in、valid_out信号,看是否存在连续多个周期valid_in为低。

经典案例:某客户用PCIe 4.0 x4接口接AI加速卡,理论带宽8GB/s,但实测DMA只能跑到3.2GB/s。原因是Linux内核的PCIe MPS(Max Payload Size)默认设为128B,导致小包传输效率低下。修改/sys/bus/pci/devices/0000:01:00.0/mesg/pcie_mps为512B后,带宽飙升至7.1GB/s,阵列利用率从38%升至89%。

4.4 问题现象:多模型并发时,性能非线性衰减,甚至互相干扰

隐藏机制:片上缓存的“伪共享”(False Sharing)。
不同模型的权重可能被映射到同一cache line,导致一个模型更新权重时,另一个模型的cache line被invalidated,引发大量refill。

规避策略:

  • 启用芯片的Cache Partitioning功能(如有),为每个模型分配独占bank;
  • 若无硬件支持,则在模型加载时,手动padding权重尺寸至cache line边界(通常64B);
  • 更激进的做法:用mlock()系统调用锁定模型权重内存页,防止OS swap。

我在部署12路视频分析时,最初所有模型共用一个权重buffer,平均延迟320ms;启用cache partitioning后,降至185ms,且各路延迟方差<5ms。

5. 脉动阵列的未来演进:从“固定节奏”到“自适应脉动”

5.1 当前瓶颈:静态调度 vs 动态负载的永恒矛盾

脉动阵列最大的软肋,是其静态数据流调度。它像一台精密瑞士手表,所有齿轮啮合严丝合缝,但一旦输入“时间”(数据)不按预设节奏来,整个系统就会卡顿。这在传统CNN时代问题不大,因为layer shape高度规则;但在Transformer时代,问题变得尖锐——Attention矩阵的shape随sequence length动态变化,Sparse Attention更是引入大量零值跳过。现有脉动阵列要么强行pad浪费资源,要么fallback到低效的scalar mode。

行业已在尝试突破。Graphcore的IPU采用**“动态脉动”(Dynamic Systolic)**:其PE具备条件跳过能力,可根据输入mask信号,自主决定是否执行本次乘加。这相当于给每个齿轮加装了离合器,不需时自动脱开。实测显示,在处理稀疏BERT推理时,其能效比固定脉动阵列高3.2倍。

5.2 下一代形态:存算一体(PIM)与脉动阵列的融合

纯数字脉动阵列的终极瓶颈是“存储墙”。即便片上缓存再大,数据仍需在存储与计算单元间搬运。学术界和产业界正探索将脉动思想迁移到存算一体架构:在DRAM或SRAM阵列内部,直接构建微型脉动结构,让数据“不动”,计算“动”起来。

例如,ISSCC 2023上公布的Samsung PIM芯片,在1Gb HBM2 stack内嵌入了128个4×4脉动子阵列,每个子阵列直接读取所在bank的bitline数据。这样,一次矩阵乘法的数据搬运距离从毫米级缩短至微米级,能效提升15倍。虽然目前容量和精度受限(仅支持2-bit),但方向已明确:未来的“脉动”,可能不再是一块独立计算单元,而是渗透到存储介质内部的计算基因。

5.3 对开发者的启示:不必追逐“最新架构”,但要深谙“数据流本质”

最后分享一个个人体会:过去五年,我见过太多团队,一听说“新架构”就立刻推翻现有技术栈。但真正拉开差距的,从来不是用了什么芯片,而是对数据如何流动、在哪里受阻、怎样绕过瓶颈的理解深度。

脉动阵列教给我的最重要一课,是计算效率的本质,不在晶体管开关速度,而在数据旅程的每一步是否被精心设计。当你能一眼看出模型中哪个op会造成bank conflict,哪个layer会触发Psum overflow,哪个tensor layout会让DMA带宽打七折——你就已经超越了90%的AI工程师。

所以别把“脉动阵列”当成一个需要 memorize 的名词。把它当作一把尺子,去丈量你手中每一个模型、每一行代码、每一次部署背后的数据流动成本。这才是它真正想告诉你的事。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进