第三章:硬件架构基础——GPU、NPU、TPU的架构对比

做算子优化,说白了就是跟硬件打交道。你连对手长什么样都不知道,怎么打得赢?

我见过不少工程师,上来就调CUDA kernel,结果性能死活上不去。为什么?因为没搞懂硬件到底怎么干活。今天咱们就把GPU、NPU、TPU这三兄弟扒开看看,它们的计算单元、存储层次、带宽瓶颈到底差在哪。

3.1 计算单元:核心的差异

先说说最核心的东西——计算单元。这三家的设计哲学完全不同。

GPU:大量小核,并行之王

GPU的设计思路是「堆核」。一个现代GPU里有几千个CUDA core,每个core都很轻量。我刚开始做AI加速时,总觉得GPU的核越多越好,后来发现不是这么回事。

GPU的SM(Streaming Multiprocessor)里包含多个warp scheduler,每个warp调度32个线程。这32个线程执行同样的指令,但处理不同的数据——这就是SIMT(单指令多线程)。

关键点:GPU的强项是数据并行。如果你的算子能拆成大量独立的小任务,GPU就是最佳选择。

举个例子,矩阵乘法里每个输出元素的计算是独立的,GPU可以同时算几千个。但如果是递归或者强依赖的计算,GPU就抓瞎了。

NPU:脉动阵列,数据流驱动

NPU(比如华为的昇腾)用的是脉动阵列(Systolic Array)。这玩意儿跟GPU完全不同。数据像流水一样在阵列里「流动」,每个计算单元只做一件事:乘加。

我记得第一次看脉动阵列的架构图时,觉得这设计太巧妙了。数据从左边进来,权重从上面进来,计算结果在底部输出。整个过程没有复杂的控制逻辑,就是数据流驱动。

我的经验:在NPU上做算子优化,关键是要让数据流起来。如果数据在阵列里「堵车」了,性能直接腰斩。我曾经优化一个卷积算子,就是把数据排布方式从NHWC改成NCHW,吞吐量提升了40%。

TPU:矩阵乘法专用机

Google的TPU更极端——它几乎只做矩阵乘法。TPU v1的核心是一个65,536个MAC(乘加单元)的矩阵乘法单元。这玩意儿一次能算一个256x256的矩阵乘。

说白了,TPU就是为Transformer这类模型量身定做的。你想想看,Transformer里全是矩阵乘法,TPU正好对口。

特性 GPU NPU TPU
计算单元类型 CUDA Core / Tensor Core 脉动阵列(Systolic Array) 矩阵乘法单元(MXU)
并行粒度 线程级(SIMT) 数据流级 矩阵级
典型规模 数千个core 数百到数千个MAC 数万个MAC
控制复杂度 高(warp调度) 低(数据流驱动) 极低(固定流水线)

3.2 存储层次:谁离计算最近?

计算单元再快,数据喂不进去也是白搭。存储层次的设计直接决定了你能跑多快。

GPU:多级缓存,显存带宽是命

GPU的存储层次很复杂:寄存器 -> L1/shared memory -> L2 -> HBM显存。每一级的速度差一个数量级。

我踩过最大的坑就是shared memory。有一次写一个矩阵乘法的kernel,直接用global memory读写,带宽利用率只有20%。后来改成tiling + shared memory,直接干到80%。

注意:GPU的shared memory只有几十KB,但速度比global memory快两个数量级。算子优化的核心就是怎么把数据「搬」到shared memory里复用。

NPU:片上buffer,数据流编排

NPU的存储设计跟GPU完全不同。它没有传统意义上的cache,而是用大块的片上buffer(比如L0 buffer、L1 buffer)。数据在buffer之间流动,由编译器决定什么时候搬数据。

嗯,这里要注意:NPU的编译器非常关键。你写算子时,编译器会帮你做数据排布和搬移的优化。但如果你不了解硬件的buffer大小,写出来的代码可能根本跑不满。

TPU:统一buffer,简化设计

TPU的存储更简单:一个统一buffer(Unified Buffer)作为中间存储,直接跟矩阵乘法单元交互。数据从HBM读到UB,算完再写回HBM。

这种设计的好处是控制简单,坏处是灵活性差。你没法像GPU那样精细控制数据复用。

层次 GPU NPU TPU
最近 寄存器 / Shared Memory L0 Buffer 统一Buffer
中间 L2 Cache L1 Buffer (无)
最远 HBM(高带宽显存) DDR / HBM HBM
带宽瓶颈 显存带宽 片外带宽 片外带宽

3.3 带宽瓶颈:算力再高也怕带宽不够

这是最现实的问题。你算力再高,数据搬不进来就是白搭。

GPU:显存带宽是硬伤

GPU的算力增长远快于带宽增长。你看NVIDIA的H100,算力快到2000 TFLOPS,但显存带宽只有3.35 TB/s。这意味着什么?

算一个简单的算术:如果每个数据需要做100次运算,那带宽还勉强够。但如果只做1次运算(比如element-wise操作),那算力利用率可能不到1%。

核心结论:GPU上做算子优化,一定要提高计算密度(compute intensity)。说白了就是让每个数据被多算几次,别让计算单元闲着。

NPU:片外带宽与片上buffer的平衡

NPU的带宽瓶颈更复杂。片外带宽(DDR/HBM)是有限的,但片上buffer可以缓解这个问题。你想想看,如果能把权重常驻在片上buffer里,那每次计算只需要搬输入数据就行了。

我曾经优化过一个卷积层,就是把权重预加载到L1 buffer里,然后反复使用。这样片外带宽的需求直接降了一个数量级。

TPU:矩阵乘法单元的「饥饿」问题

TPU的矩阵乘法单元胃口太大了。一次矩阵乘法需要搬大量的数据进来。如果数据搬得慢,计算单元就会「饿死」。

Google的解决方案是用systolic array的流水线特性——数据一边搬一边算,尽量减少等待。但说实话,这需要编译器做非常精细的调度。

3.4 架构对比总结

说了这么多,咱们用一张图来总结一下三者的核心差异。

GPU vs NPU vs TPU 架构对比 GPU SM (Streaming Multiprocessor) 多个CUDA Core + Tensor Core Shared Memory / L1 几十KB,可编程控制 L2 Cache 几MB,所有SM共享 HBM 显存 几十GB,带宽是关键 特点:多级缓存,灵活但复杂 瓶颈:显存带宽 NPU 脉动阵列 (Systolic Array) 数据流驱动,乘加单元 L0 Buffer 紧耦合,低延迟 L1 Buffer 较大容量,数据复用 DDR / HBM 片外存储 特点:数据流驱动,编译器关键 瓶颈:片外带宽 + 数据排布 TPU 矩阵乘法单元 (MXU) 65,536个MAC,固定流水线 统一Buffer (UB) 中间存储,数据暂存 HBM 显存 片外存储 特点:专用化,控制简单 瓶颈:数据搬移速度 对比 对比

这张图把三者的存储层次和计算单元画出来了。你可以看到,GPU的层次最多,NPU次之,TPU最简单。但简单不代表不好——TPU在矩阵乘法上的效率是最高的。

3.5 实战建议:如何选择?

说了这么多理论,最后给点实际的建议。

  • 如果你的模型以卷积和矩阵乘法为主:NPU或TPU可能更合适。它们的脉动阵列和矩阵乘法单元就是为这类算子设计的。
  • 如果你的模型有大量自定义算子:GPU更灵活。你可以用CUDA写任意复杂的kernel。
  • 如果你追求极致性能:一定要理解硬件的存储层次。我见过太多人把时间花在优化计算上,结果瓶颈在带宽。

一个小技巧:拿到一个新硬件,先跑一个简单的带宽测试(比如memcpy),看看实际带宽是多少。然后再跑一个计算密集型算子(比如矩阵乘法),看看算力利用率。这两个数字能告诉你很多信息。

好了,这一章的内容就到这。硬件架构是算子优化的地基,地基打不牢,上面盖的房子再漂亮也经不起推敲。下一章咱们会深入具体的算子优化技巧,到时候这些硬件知识都会用上。

想要更多资料,联系可可