想象一家爆火的“流水线汉堡店”。后厨不是一个大厨师掌勺,而是一排整齐排列的工作台。每个工作台的任务极其单一:从左侧配料架上拿固定配方,把从顶部传送带滑下来的面包胚按配方加一点料,再传给下一个工位继续加料。所有工位同时开工,面包胚像浪潮一样在传送带上流动,十几秒就能做出一批口味一致的汉堡。Google TPU里的矩阵乘法单元(MXU)就是这样一个“后厨快餐流水线”:无数个乘加小单元(PE)组成二维阵列,让矩阵计算像做汉堡一样被拆解、并行、流水,最终吞吐惊人。 这篇文章会带你走进这个后厨,看看数据如何流入流出、每个工位内部如何工作、大矩阵怎样切块、什么时候流水线会空转,以及从TPU v1到v4,这个“后厨”经历了怎样的进化。读完你会明白,所谓AI算力,很多时候就是一口气把无数个“小乘法”用流水线节奏倒腾出结果。
理解MXU的脉动阵列,是解读TPU算力来源、从事AI芯片设计或性能调优的硬核基本功,也是硬件工程师与算法工程师沟通的关键桥梁。
目录
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
脉动阵列概述:给矩阵乘法装上流水线
回到那家汉堡流水线店。老板发现单靠一个大厨炒一锅菜太慢,于是把做汉堡拆成:放面包、加肉饼、加酱汁、加蔬菜、打包。每个工位只做一步,所有工位同时开工,一个面包胚从第一个工位走到最后一个工位,后厨就能批量出货。MXU之所以叫“矩阵乘法单元”,就是因为它把数学里的矩阵乘法拆成了无数个“乘加小步”,让数据自己流经工位,而不是让一个计算核心反复取数。
矩阵乘法是神经网络最基本的运算——全连接层、卷积层、注意力权重计算,本质上都是二维矩阵相乘。MXU把这种运算映射到二维脉动阵列上:所有PE按网格排列,每个PE负责一个输出元素的一部分累加工作。它不像CPU那样有复杂控制逻辑,而是用“数据驱动计算”的方式,让每个PE在数据到达时自动运算。
脉动阵列最大的优势是数据复用和低数据搬运。权重先加载进阵列并锁存,激活值像传送带上的面包胚一样流经每个PE,每个PE只需要在本地取权重、乘上流入的激活值、累加部分和,然后传给下一个PE。整个过程中不需要频繁访问寄存器堆或主存,能耗和延迟大幅降低。
就像一个汉堡包的“总成本”由所有工位累加的成本组成,矩阵乘法的每个输出也由所有中间乘积累加而来。理解了这一步,后面所有流水线细节都顺理成章。
- 输出矩阵维度输出矩阵的行数由左乘矩阵的行数决定,列数由右乘矩阵的列数决定。
- 乘法维度匹配矩阵能相乘要求左侧矩阵列数等于右侧矩阵行数,这个共同维度记为 $k$。
- 求和累加输出中的每个元素都要把 $k$ 个乘积加起来,这是计算量的来源。
数据流入流出:左侧进配方,顶部进食材
流水线后厨的规则很精确:配料架上的“配方卡”必须在开工前挂到每个工位上方,面包胚则从顶部传送带一个个滑下来。工位师傅不会主动去取料,只等食材自己送到面前。MXU的数据流与此几乎一模一样。
脉动阵列有三种数据流:权重流、激活值流、部分和流。权重从阵列左上角开始注入,沿着每一行逐列向右流动,到达目标PE后锁存下来;激活值从阵列顶部进入,逐行向下流动;每个PE把当前流过的激活值和本地锁存的权重相乘,结果累加到“部分和”上,再传给下面的PE。数据流是“脉动”的核心——数据像心跳一样按固定节奏在阵列中移动。
具体来说,在第 $t$ 个时钟周期,激活值 $x_t$ 进入第一行PE;下一个周期,这个激活值流到第二行,同时新的 $x_{t+1}$ 进入第一行。每个PE在收到激活值时执行一次乘加,并把部分和向下传递。这样整个阵列每个周期都在满负荷工作,不会像传统处理器那样停下来等数据。
就像汉堡工位上,师傅看到面包胚滑下来,就按配方撒料,再把半成品推给下一位师傅;下一周期下一位师傅继续加料,直到完成全部工序。数据流方向对了,整个阵列就“活”了起来。
- 旧的部分和每个PE内部都有一个累加器,保存之前累计的结果。
- 本次乘加项$x_{ik}$ 是当前周期流经该PE的激活值,$w_{kj}$ 是PE锁存的权重。
- 更新累加器把乘积加到旧的部分和上,形成新值。
PE单元设计:一个只会“乘加”的小工位
流水线工位上的师傅不需要会做满汉全席,他只需要记住“肉饼+面包+酱汁”然后机械重复。PE单元就是这样一个“偏科”师傅:它不会跳转、不会分支,只做一件事——乘加运算,但做得极快。
一个PE至少包含三部分:乘法器、加法器和寄存器(锁存器)。乘法器把流入的激活值和本地的权重相乘;加法器把乘积累加到部分和上;寄存器把权重锁存在原位,防止下一个周期数据覆盖。这些PE按二维网格排列,每个PE只与相邻PE通信,所以布线极短、功耗很低。
权重数据在进入阵列后,先通过“数据锁存”让每个PE的权重寄存器保存固定值。之后无论激活值怎么流动,权重都不会变,直到下一次任务重载。这种设计让PE单元非常小巧,可以在一个芯片上集成几万甚至几十万个PE。例如TPU v1的MXU就是一个256×256的PE阵列,单周期能执行65,536次乘加。
工位师傅手里的“配方卡”就是权重寄存器,它被图钉钉住不会飘走;只要面包胚(激活值)一来,师傅就乘以菜量(权重),记到小票(累加器)上。PE越简单,后厨就能排列越多的工位,产能自然越高。
- 读取本地权重$w$ 是PE锁存寄存器中的权重,不需要外部总线。
- 读取流入激活值$a$ 是从上方PE流入的激活值。
- 乘加并写回$acc$ 是累加器,乘加结果写回累加器。
矩阵分块策略:大矩阵怎么塞进小阵列
如果一次要做的汉堡数量超过传送带容量,后厨不会停摆,而是把订单拆成几批,每批做一部分,再拼成完整出货。TPU拿到超大矩阵时同样要“分块”。
MXU阵列尺寸是固定的(例如128×128或256×256),而现实中的矩阵往往更大。分块策略就是把大矩阵按阵列的行数和列数切成小块,小块依次送入MXU计算。比如一个1024×1024的矩阵在256×256的MXU上会切成4×4=16块。
分块不仅解决尺寸问题,还能提高数据复用:每个权重块留在阵列中,多次参与不同激活值块的计算,避免重复搬运权重。实际中还要考虑“边界块”(块小于阵列尺寸时造成浪费)和“块间累加”(相邻块的输出需要相加才能得到最终结果)。
后厨把大订单拆成多批面包胚,每批送上传送带,最后把批次结果拼接起来,顾客收到的还是同一批完美汉堡。分块的本质是“用固定尺寸的流水线,消化任意大小的订单”。
- 行方向分块矩阵总行数除以阵列行数,不够整块就多分一块。
- 列方向分块矩阵总列数除以阵列列数,向上取整。
- 总块数两个方向的切块数相乘。
利用率分析:不要让工位闲着
流水线后厨最怕“半空”:传送带上只有零星几个面包胚,后面十个工位等着发呆,厨师们还全额领工资。MXU也有同样的苦恼:当矩阵维度小于阵列尺寸时,很多PE在“空转”。
MXU的利用率可以用实际参与计算的PE数除以总PE数来衡量。假设阵列是 $R \times C$,实际输入的矩阵维度是 $m \times n$。如果 $m\ge R$ 且 $n\ge C$,所有PE都被喂饱,利用率100%;如果矩阵很小,比如 $m=32,n=32$,在256×256的MXU上,只有约1.56%的PE在工作,其余都在“摸鱼”。
为什么小矩阵在小阵列上利用率低?因为脉动阵列的设计目标是让数据尽量就地复用,一旦输入数据量填不满阵列,新增的PE只会白白等待。这也是为什么Google在TPU中加入批处理和向量单元来调度小矩阵,把多个小矩阵拼接成一个大矩阵一起计算,或者把矩阵维度填充到阵列大小。
只有每批订单都填满整条传送带,后厨的每一分工位才算物尽其用;只来一个顾客时,老板只能看着流水线叹气。利用率优化,本质是“让阵列永远吃满”。
- 实际使用行数矩阵行数如果小于阵列行数,只有前者部分PE被用到。
- 实际使用列数矩阵列数同理。
- 利用率比值被使用的PE数除以总PE数。
MXU演进:从v1到v4的升级之路
当年的后厨只有一条手工流水线,后来老板不断扩建,从单层厂房到多层自动化仓库,再到用机械臂替换部分工人,每一代都带来新的工序安排。Google的TPU从v1到v4,MXU也在不断演化。
TPU v1(2016年)是专用推理芯片,MXU是一个256×256的脉动阵列,峰值算力92 TOPS(int8),主打每一瓦电都用在推理上。TPU v2(2017年)转向训练,增加了可编程性,但单个MXU缩小到128×128,同时引入片上高带宽存储;TPU v3延续v2的架构思路,增加了算力和液冷。TPU v4(2023年)更强调大规模互联,把多个MXU组合成更大的“超级阵列”,还加入了稀疏化支持——跳过无用的零值,进一步提高有效算力。
虽然MXU的尺寸和数量在变,但脉动阵列的核心思想从未改变:用大量小PE、短数据路径和固定数据流换取高吞吐。每一代TPU都在平衡三件事:阵列规模(乘加器数量)、数据供给(存储带宽)和利用率(对多样性矩阵的适应性)。
从手工小作坊到工业级中央厨房,MXU始终是那个“把所有配料流水线化”的核心工序。变的是规模,不变的是脉动。
- 乘加通道数阵列中总的PE数就是一次能执行的乘加数。
- 每秒周期数乘以时钟频率(GHz),得到每秒乘加次数。
- 换算为TOPSTOPS是每秒万亿次操作,乘加算两次操作所以乘以2。
AI 应用场景
推荐系统里的超大规模矩阵乘法
阶段:模型推理
解决的问题:用户行为和物品特征构成的高维矩阵,传统CPU一次只能算一小块,延迟高、功耗高。MXU把用户向量和物品向量的乘积累加流水线化,像流水线同时处理多个汉堡一样处理海量打分任务。
具体案例:某短视频App每天有数百亿次请求,召回阶段用TPU MXU计算“用户兴趣向量×物品库向量”的分数矩阵。权重锁存在MXU里,用户向量像面包胚一样批量流入,吞吐提高数倍,单次请求延迟降低到毫秒级。
大语言模型推理的线性层加速
阶段:模型推理
解决的问题:Transformer每一层都有多次矩阵乘法,例如 $Q=XW_Q$、$K=XW_K$、$V=XW_V$。这些运算在CPU上访存开销极大,MXU的权重复用和脉动数据流非常适合。
具体案例:部署一个大语言模型时,把训练好的权重矩阵加载到TPU MXU中,批量输入多个token的隐状态。激活值从顶部流入,每个PE用固定权重做乘加,最终一次前向推理时间缩短三分之二。
边缘设备上的小批量推理
阶段:实时推理
解决的问题:边缘设备往往一次只推理一张图或一句话,矩阵很小。如果直接搬用云端大MXU,利用率会非常低。Edge TPU采用较小的MXU阵列,让32×32这类小矩阵也能达到较高利用率,类似“小后厨只服务散客”。
具体案例:一台工业相机用Edge TPU做缺陷检测,输入是224×224图像卷积后的特征矩阵。小尺寸MXU配合矩阵填充和批处理,使利用率从个位数提升到60%以上,且功耗只有几瓦。
Python 演示
"""模拟一个二维脉动阵列完成矩阵向量乘法权重锁存在每个PE中,激活值从顶部流入,部分和逐行向下累加,最终输出结果。"""class PulseArray: def __init__(self, rows, cols): self.rows = rows self.cols = cols self.weights = [[0] * cols for _ in range(rows)] self.accumulator = [[0] * cols for _ in range(rows + 1)] def load_weights(self, weight_matrix): for i in range(self.rows): for j in range(self.cols): self.weights[i][j] = weight_matrix[i][j] def run(self, activations): self.accumulator = [[0] * self.cols for _ in range(self.rows + 1)] total_cycles = self.rows + len(activations) - 1 for t in range(total_cycles): for i in range(self.rows): k = t - i if 0 <= k < len(activations): a = activations[k] for j in range(self.cols): self.accumulator[i + 1][j] += a * self.weights[i][j] return self.accumulator[self.rows] def direct_matvec(weight_matrix, activations): rows = len(weight_matrix) cols = len(weight_matrix[0]) out = [0] * cols for i in range(rows): for j in range(cols): out[j] += weight_matrix[i][j] * activations[i] return out if __name__ == "__main__": w = [[1, 2], [3, 4]] x = [2, 5] mxu = PulseArray(2, 2) mxu.load_weights(w) print("MXU结果:", mxu.run(x)) print("直接结果:", direct_matvec(w, x))
self.weights = [[0] * cols for _ in range(rows)]初始化每个PE的权重寄存器。total_cycles = self.rows + len(activations) - 1计算流水线需要多少个时钟周期才能灌满并排空。k = t - i当前周期第 $i$ 行PE接收到的激活值编号,体现逐行延迟。self.accumulator[i + 1][j] += a * self.weights[i][j]每个PE执行乘加,并把部分和传给下一行。return self.accumulator[self.rows]读取阵列底部的最终输出。
常见误区
误区我以为每个PE都需要像CPU那样取指令、译码、执行,否则不知道怎么算。所以看到TPU代码时总在想“指令在哪里”。
正解MXU的PE是“数据驱动”的,没有复杂的指令流。权重锁存后,激活值一到,PE就自动执行乘加。你可以把PE想象成流水线上听到铃铛就会撒料的师傅,不需要有人喊“开始”,食物到了自然开工。
为何常见:因为我们习惯用GPU或CPU的编程模型理解计算,忽略了专用芯片可以用固定数据流代替控制流。
误区我一直以为每次乘加都要从内存里取权重,所以不理解为什么MXU能省这么多功耗。
正解权重先通过加载阶段写入每个PE的寄存器,并锁存固定值。整个计算期间,权重只读不写,相当于后厨把配方卡一次性贴满所有工位。只有换任务时才会重新加载。
为何常见:通用处理器中数据都放在寄存器或缓存里,导致我们默认权重也需要反复取用。
误区我以为无论矩阵多小,MXU总比CPU快,所以没有关注利用率。
正解当矩阵维度小于阵列尺寸时,大量PE空转,有效吞吐会断崖式下降。比如32×32矩阵在256×256的MXU上利用率只有约1.56%,可能还不如CPU划算。所以需要用批处理或填充提高利用率。
为何常见:只看了峰值算力,忽略了“实际参与计算的PE数”才是关键。
误区既然PE越多算得越快,那把阵列从256×256扩大到1024×1024不就行了?
正解阵列变大后,权重加载和激活值注入的带宽需求也线性增长。如果片上存储或外部内存喂不饱数据,利用率反而下降。好比传送带变宽了,但后厨备菜速度跟不上,工位依然闲着。
为何常见:把峰值算力等同于实际算力,忽略了数据和带宽约束。
面试题
Q1. MXU中的PE最核心的计算操作是什么?
- A.乘加运算← 正确答案
- B.除法运算
- C.开方运算
- D.逻辑与运算
答案:A。PE完成的是“激活值×权重+部分和”的乘加操作,这是矩阵乘法的基础。后厨师傅只负责“加料”和“记账”,不会去做除法和开方这些复杂动作。
Q2. TPU v1的MXU阵列规模是?
- A.64×64
- B.128×128
- C.256×256← 正确答案
- D.512×512
答案:C。TPU v1的MXU是256×256的脉动阵列,共65536个PE。其他选项是常见的混淆尺寸,尤其是128×128出现在TPU v2之后。
Q3. 脉动阵列中权重是如何进入MXU的?
- A.从左侧加载并锁存← 正确答案
- B.从顶部动态流入
- C.每个周期访问内存
- D.由外部中断写入
答案:A。权重从阵列左上角/左侧注入,到达PE后锁存固定不变。激活值才是从顶部流入的,如果混淆了这两个方向,就无法理解脉动数据流。
Q4. 一个256×256的MXU计算32×32矩阵,利用率约为多少?
- A.0.39%
- B.1.56%← 正确答案
- C.6.25%
- D.12.5%
答案:B。利用率=(32×32)/(256×256)=1024/65536=1.5625%,约1.56%。这说明绝大多数PE空转,流水线没被填满。
Q5. 矩阵分块时使用向上取整的目的是?
- A.保证整块划分← 正确答案
- B.提高计算精度
- C.减少计算时间
- D.增加阵列尺寸
答案:A。当矩阵行数/列数不能整除阵列维度时,向上取整可以保证所有数据都被覆盖,不会漏块。就像后厨分批做汉堡,最后一批哪怕只有一个也要单独做一轮。
Q6. 下列哪一项属于PE单元的组成部分?
- A.累加器单元← 正确答案
- B.指令解码器
- C.浮点比较器
- D.分支预测器
答案:A。PE由乘法器、加法器和累加器构成,没有指令解码或分支预测这类通用处理器组件。累加器保存部分和,是脉动计算的关键。
Q7. 矩阵乘法Y=XW中,脉动阵列内部数据流的正确描述是?
- A.权重静止、激活值流入← 正确答案
- B.激活值静止、权重流动
- C.两者同时随机流动
- D.先全部读入再计算
答案:A。权重加载后锁存不动,激活值像传送带一样从顶部流入,部分和向下游累加。这正是与普通矩阵乘法器的最大区别:用固定数据流替代取数运算。
Q8. 下列哪些措施可以提升MXU的利用率?
- A.小矩阵批量拼接← 正确答案
- B.填充到阵列大小← 正确答案
- C.只处理大矩阵任务← 正确答案
- D.缩小MXU尺寸
答案:ABC。批量拼接小矩阵可以填满阵列,填充可以把矩阵补到阵列维度,缩小MXU能让小矩阵也获得较高利用率。只处理大矩阵不是通用方案,因为现实场景小矩阵很多。
Q9. 关于MXU的数据锁存,下列说法正确的有?
- A.权重加载后不再改变← 正确答案
- B.激活值在PE间延迟传递← 正确答案
- C.部分和向下游累加← 正确答案
- D.累加器每个周期清零
答案:ABC。权重锁存后保持不变,激活值逐周期逐行传递,部分和不断累加而不是清零。累加器清零只发生在任务切换时,如果每周期清零就无法完成乘法。
Q10. TPU v4相比TPU v1在MXU上的主要改进包括哪些?
- A.支持稀疏化计算← 正确答案
- B.引入更强互联← 正确答案
- C.放弃脉动阵列思想
- D.去掉所有寄存器
答案:AB。TPU v4加入稀疏化支持,并强化多个MXU之间的互联,但依然沿用脉动阵列思想。脉动阵列是MXU的根基,不会放弃寄存器,否则无法锁存权重。