第二章 计算机体系结构基础:CPU/GPU/NPU架构差异、存储层次与计算单元

各位同学,欢迎来到第二章。这一章是算子优化的“地基”。

我见过太多人一上来就调指令、改循环,结果性能纹丝不动。为什么?因为他不理解硬件在想什么。你想想看,你连数据放在哪里、计算单元怎么工作都不知道,怎么可能写出高效的算子?

这一章,我们就来把CPU、GPU、NPU这三兄弟的底裤扒干净。顺便聊聊存储层次和计算单元——说白了,就是数据怎么搬、怎么算。

2.1 CPU / GPU / NPU 架构差异

先问个问题:为什么CPU什么都能干,但跑AI模型慢得要死?而GPU天生适合矩阵运算?NPU又是何方神圣?

嗯,这得从它们的设计哲学说起。

2.1.1 CPU:控制狂魔

CPU的设计目标是“低延迟、高通用”。它花了大把晶体管在分支预测、乱序执行、缓存一致性上。说白了,CPU擅长处理复杂的控制流——if-else、函数调用、指针跳转。

但代价是什么?计算单元(ALU)只占芯片面积的很小一部分。我做过一个测试,同样做一次浮点乘法,CPU的延迟确实低,但吞吐量被GPU甩开两个数量级。

核心特点:

  • 强控制能力,弱计算密度
  • 适合串行、分支密集的任务
  • 缓存大,延迟敏感

2.1.2 GPU:吞吐怪兽

GPU的设计目标是“高吞吐、并行计算”。它放弃了复杂的控制逻辑,堆了大量的计算单元(CUDA Core / Stream Processor)。

我记得第一次用CUDA写矩阵乘法时,看到10000个线程同时跑,那种感觉——嗯,就像你指挥一支军队。GPU的核心理念是:用大量轻量级线程掩盖访存延迟。

核心特点:

  • 弱控制能力,强计算密度
  • 适合数据并行、计算密集的任务
  • 线程切换开销极低

2.1.3 NPU:专用加速器

NPU(比如华为昇腾、Google TPU)是“为AI而生”的专用芯片。它把计算单元固定为矩阵乘法器(比如脉动阵列),连指令集都是定制的。

我曾在昇腾上优化过一个卷积算子,发现它的数据流和GPU完全不同——NPU更强调“数据不动,计算流动”。这玩意儿一旦用对了,能效比是GPU的几倍。但缺点也很明显:灵活性差,不是所有算子都能跑。

核心特点:

  • 专用计算单元,极致能效
  • 适合固定模式的矩阵/卷积运算
  • 编程模型受限,调试困难

2.2 存储层次:数据搬运的艺术

算子优化的本质是什么?我个人习惯把它总结为:减少数据搬运,提高计算密度。

为什么?因为从DDR搬数据到计算单元,能耗是计算本身的几百倍。你想想看,你辛辛苦苦优化了10%的计算指令,结果数据搬运多了一次,性能直接打回原形。

2.2.1 存储金字塔

从快到慢、从贵到便宜:

层级 典型容量 延迟(周期) 带宽
寄存器 几百字节 1 极高
L1 Cache 32KB - 128KB 3-5 高
L2 Cache 256KB - 几MB 10-20 中
HBM(高带宽内存) 几GB - 几十GB 100-200 极高(1-2TB/s)
DDR(主存) 几十GB 200-400 低(几十GB/s)

避坑指南:我曾经在优化一个矩阵转置算子时,忽略了L1 Cache的容量限制。数据量一大,Cache疯狂抖动,性能反而比朴素实现还差。后来我用了分块(Tiling)技术,把数据切到L1能装下的大小,性能直接翻倍。

2.2.2 局部性原理

这是存储层次能工作的前提:

  • 时间局部性:刚用过的数据,很可能马上再用。比如循环中的累加变量。
  • 空间局部性:刚用过的数据附近的数据,很可能马上要用。比如数组的连续访问。

我建议你在写算子时,时刻问自己:我的数据访问模式,有没有利用好局部性?

2.3 计算单元:SIMD 与 Tensor Core

好了,数据搬到位了,接下来就是怎么算。这里有两个关键概念:SIMD和Tensor Core。

2.3.1 SIMD:单指令多数据

SIMD(Single Instruction, Multiple Data)就是一条指令同时处理多个数据。比如CPU的AVX指令集,一条加法指令可以同时加8个float。

嗯,这里要注意:SIMD要求数据在内存中是连续排列的。如果你数据是乱序的,SIMD反而会拖慢速度——因为你要花时间做数据重组。

代码示例(伪代码):

// 标量加法
for i in 0..N:
    C[i] = A[i] + B[i]

// SIMD加法(一次处理4个float)
for i in 0..N step 4:
    vec_a = load(A[i:i+4])
    vec_b = load(B[i:i+4])
    vec_c = vec_a + vec_b
    store(vec_c, C[i:i+4])

2.3.2 Tensor Core:矩阵乘法专用

Tensor Core是NVIDIA从Volta架构开始引入的专用单元。它能在一条指令内完成一个4x4矩阵的乘加运算(D = A * B + C)。

我刚开始用Tensor Core时犯过一个错误:以为只要调用API就能自动加速。结果发现,如果矩阵维度不是4的倍数,或者数据排布不是特定的格式(比如行主序 vs 列主序),Tensor Core根本不会生效。

警告:Tensor Core对数据精度有要求。比如FP16输入、FP32累加。如果你用FP32输入,Tensor Core不会工作,会回退到普通CUDA Core。我曾经因为这个原因,性能反而下降了30%。

2.4 本章知识体系图

下面这张图,是我自己总结的算子优化知识体系。你可以把它当作一张地图,随时回来看看。

算子优化知识体系 CPU GPU NPU 存储层次:寄存器 → L1 → L2 → HBM → DDR 时间局部性 空间局部性 SIMD:单指令多数据 Tensor Core:矩阵乘加 目标:减少数据搬运,提高计算密度

2.5 本章小结

这一章我们聊了:

  • CPU是控制狂魔,GPU是吞吐怪兽,NPU是专用加速器
  • 存储层次决定了数据搬运的成本,局部性是优化的关键
  • SIMD和Tensor Core是两种重要的计算单元,各有各的脾气

嗯,说实话,这些概念光看一遍是不够的。我建议你找个实际的算子(比如矩阵乘法),在CPU和GPU上分别实现一遍,感受一下差异。纸上得来终觉浅,绝知此事要躬行。

个人经验:我每次接触新硬件,第一件事就是画一张类似上面的知识体系图。把架构、存储、计算单元的关系理清楚,后面优化起来思路就清晰多了。你也可以试试。

可可精品资料库