第二章 计算机体系结构基础: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 本章知识体系图
下面这张图,是我自己总结的算子优化知识体系。你可以把它当作一张地图,随时回来看看。
2.5 本章小结
这一章我们聊了:
- CPU是控制狂魔,GPU是吞吐怪兽,NPU是专用加速器
- 存储层次决定了数据搬运的成本,局部性是优化的关键
- SIMD和Tensor Core是两种重要的计算单元,各有各的脾气
嗯,说实话,这些概念光看一遍是不够的。我建议你找个实际的算子(比如矩阵乘法),在CPU和GPU上分别实现一遍,感受一下差异。纸上得来终觉浅,绝知此事要躬行。
个人经验:我每次接触新硬件,第一件事就是画一张类似上面的知识体系图。把架构、存储、计算单元的关系理清楚,后面优化起来思路就清晰多了。你也可以试试。