1. GPU架构基础:从图形卡到通用计算引擎

各位同学,今天咱们聊聊GPU架构的根。说实话,我入行那会儿,GPU还只是个「画图卡」。谁能想到,十几年后它成了AI时代的算力核心?

这一章,我会从发展简史讲起,再对比CPU和GPU的本质区别,最后深入现代GPU的架构细节。嗯,内存层次结构这块,我踩过不少坑,会重点说。

1.1 GPU发展简史:三个关键转折点

GPU的历史,我习惯分成三个阶段来看:

  • 1999-2006:固定功能管线时代
    NVIDIA发布GeForce 256,第一次提出「GPU」这个概念。那时候的渲染管线是固定的——顶点着色、像素着色,硬件写死了,程序员改不了。我记得当时做游戏开发,想实现个水波效果,得用一堆trick,累得够呛。
  • 2007-2014:可编程着色器时代
    DirectX 10和CUDA的推出,彻底改变了游戏规则。着色器变成了统一架构,顶点、像素、几何着色器共用一套计算单元。我2010年第一次在CUDA上写矩阵乘法,那种「CPU跑不动的东西GPU秒出」的震撼,到现在还记得。
  • 2015至今:通用计算与AI时代
    Tensor Core、RT Core这些专用硬件加入,GPU从图形处理器变成了「通用并行计算引擎」。说白了,现在的GPU更像一个「超级并行数学协处理器」。

关键洞察:GPU的演进,本质上是「从专用走向通用,再从通用走向专用+通用混合」的过程。你想想看,Tensor Core不就是为AI量身定做的专用电路吗?

1.2 GPU与CPU的区别:一个比喻让你彻底明白

很多人问我:「CPU和GPU到底差在哪?」我一般这么打比方:

  • CPU:像一个全能教授,什么题都会做,但一次只能做一两道。
  • GPU:像一千个小学生,每个人只会加减乘除,但可以同时算一千道题。

从硬件设计上看,区别更明显:

维度CPUGPU
控制逻辑复杂(分支预测、乱序执行)简单(顺序执行为主)
缓存大(L1/L2/L3,几十MB)小(L1/L2,几MB)
ALU数量几个到几十个几千个
设计目标低延迟高吞吐

为什么会这样?因为GPU假设你要处理的是「大量相同操作的数据」——比如对100万个像素做同样的颜色变换。这种情况下,每个像素不需要复杂的控制逻辑,只需要一个简单的ALU就够了。

个人经验:我曾在项目中遇到一个性能问题——CPU上跑得好好的算法,搬到GPU上反而变慢了。后来发现是分支太多,导致GPU的SIMT(单指令多线程)执行效率极低。记住:GPU怕分支,CPU怕并行。

1.3 现代GPU架构概览:SM/CU/ALU

现代GPU的架构,我习惯从三个层次去理解:

1.3.1 芯片级:GPC(图形处理集群)

一个GPU芯片由多个GPC组成。每个GPC包含若干SM(NVIDIA叫法)或CU(AMD叫法)。GPC之间通过片上互联网络通信。

1.3.2 核心级:SM/CU

这是GPU最重要的计算单元。以NVIDIA的SM为例:

  • 每个SM包含多个CUDA Core(其实就是ALU)
  • 还有共享内存、寄存器文件、调度器
  • SM内部采用SIMT模型——32个线程组成一个warp,一起执行同一条指令

AMD的CU(Compute Unit)类似,但内部是SIMD(单指令多数据)模型,一个wavefront是64个线程。嗯,这里要注意,虽然概念相似,但调度方式有差异,写驱动时得区分对待。

1.3.3 执行级:ALU

ALU就是干活的。现代GPU的ALU分几种:

  • 整数ALU:处理整数运算
  • 浮点ALU:处理FP32/FP64运算
  • 特殊函数单元(SFU):处理sin、cos、log等复杂函数
  • Tensor Core:专门做矩阵乘加,AI训练推理的利器

避坑指南:我曾经在写驱动时,没注意Tensor Core的对齐要求——输入矩阵必须是16x16的倍数。结果跑出来的结果全是错的,查了两天才发现。所以,用Tensor Core前,一定检查数据对齐。

1.4 GPU内存层次结构:速度与容量的博弈

GPU的内存层次,说白了就是「越快的越贵,越贵的越小」。我画个简化的层次图:

寄存器(最快,最小)
    ↓
L1缓存(每个SM私有)
    ↓
共享内存(可编程,同一SM内线程共享)
    ↓
L2缓存(芯片全局共享)
    ↓
显存(HBM/GDDR,最大,最慢)
    ↓
系统内存(通过PCIe访问,最慢)

每个层次的特点:

层次典型大小延迟带宽作用
寄存器256KB/SM~1 cycle极高线程私有变量
共享内存48-96KB/SM~5 cycles极高线程间通信
L1缓存128KB/SM~20 cycles自动缓存数据
L2缓存4-8MB~200 cycles全局缓存
显存8-80GB~400 cycles低(相对)主存储

这里有个关键点:共享内存是可编程的,而L1缓存是硬件自动管理的。我个人的习惯是:如果数据访问模式是规则的(比如矩阵分块),就用共享内存;如果是随机的,就靠L1缓存。

实战建议:写GPU驱动时,内存分配策略直接影响性能。我一般遵循三条原则:

  1. 能放寄存器的绝不放共享内存
  2. 能放共享内存的绝不放显存
  3. 显存访问尽量合并(coalesced)——连续线程访问连续地址

1.5 小结:理解架构才能写好驱动

这一章我们走马观花看了GPU的架构基础。你可能会问:「知道这些有什么用?」

我告诉你,写GPU驱动,本质上就是「把上层应用的请求,翻译成硬件能理解的指令」。如果你不懂SM怎么调度warp,不懂共享内存和L1缓存的区别,那写出来的驱动要么性能差,要么直接崩溃。

下一章,我们会深入驱动开发的第一个环节——设备初始化。到时候你会看到,这些架构知识是怎么落地到代码里的。

课后思考:为什么GPU的L1缓存通常比CPU小得多?这和它们的工作负载有什么关系?想明白这个问题,你就理解了GPU设计的核心哲学。