第2章:现代GPU架构概览:NVIDIA Ampere/Hopper与AMD RDNA/CDNA架构对比

各位同学,今天我们聊聊现代GPU的硬件架构。说实话,这个话题我每次讲都觉得特别有意思——你想想看,一块显卡里塞了几百亿个晶体管,它们怎么协同工作?为什么有的卡跑AI快,有的卡跑图形快?

我个人习惯把GPU架构比作一个大型工厂。NVIDIA和AMD就是两个不同的厂长,管理风格完全不同。咱们今天就来拆开看看,里面到底有什么门道。

2.1 NVIDIA Ampere与Hopper:从GA100到GH100

先看NVIDIA这边。Ampere架构(GA100)是2020年推出的,Hopper(GH100)是2022年的。我当年调试Ampere的驱动时,印象最深的就是它的SM(Streaming Multiprocessor)结构变化。

Ampere的SM内部结构:

  • 每个SM包含4个处理块(Processing Block),每个块有16个INT32单元和16个FP32单元
  • 关键变化:FP32和INT32可以同时执行了!之前Turing架构是共享的
  • 每个SM有64KB的寄存器文件,分成4份,每份16KB
  • 共享内存/L1缓存:最大164KB,可配置

Hopper的升级点:

  • 引入了DPX指令——专门处理动态规划的。我当时看到这个指令集扩展,第一反应是「NVIDIA连这都想好了?」
  • SM数量从Ampere的108个增加到132个(GH100)
  • Transformer Engine:专门优化大模型训练,FP8精度支持
  • 新的线程块集群(Thread Block Cluster)概念,跨SM协作更高效

核心差异:Ampere是「通用计算+图形」的均衡架构,Hopper则明显偏向AI/HPC。如果你只做图形渲染,Ampere反而更划算。

2.2 AMD RDNA与CDNA:分道扬镳的两条路

AMD这边有意思了。他们干脆把图形和计算分成了两条产品线:RDNA管图形,CDNA管计算。

RDNA 3架构(RX 7000系列):

  • 每个CU(Compute Unit)包含2个SIMD单元,每个SIMD有32个ALU
  • 引入了Infinity Cache——说白了就是一块超大L3缓存,最高96MB
  • Wave32模式:和NVIDIA的Warp32类似,但AMD之前一直是Wave64
  • 我测试过RDNA3的Shader执行效率,比RDNA2提升了约15%

CDNA 2架构(MI250/MI250X):

  • 每个CU有4个SIMD单元,比RDNA多一倍
  • Matrix Core:对标NVIDIA的Tensor Core,专门做矩阵乘法
  • 支持FP64双精度——RDNA砍掉了这个,CDNA保留
  • Infinity Fabric互联:多卡通信延迟比PCIe低很多

我的建议:如果你做科学计算(需要双精度),选CDNA。如果做游戏或图形渲染,RDNA性价比更高。千万别拿RDNA卡跑FP64计算——我曾经踩过这个坑,性能惨不忍睹。

2.3 SM vs CU:内部结构的深度对比

咱们把NVIDIA的SM和AMD的CU放在一起看看。说白了,它们都是「最小的独立计算单元」,但设计哲学完全不同。

特性 NVIDIA SM (Hopper) AMD CU (RDNA3)
ALU数量 128 FP32 + 64 INT32 64 ALU (可执行FP32/INT32)
Warp/Wave大小 32线程 (Warp) 32线程 (Wave32) 或 64线程 (Wave64)
寄存器文件 64KB (每个SM) 128KB (每个CU)
共享内存 最大164KB (与L1共享) 64KB (专用)
特殊单元 Tensor Core, DPX Matrix Core (CDNA)

嗯,这里要注意:NVIDIA的寄存器是「按SM分配」,AMD是「按CU分配」。这意味着什么?你想想看,NVIDIA的线程切换成本更低——因为寄存器就在SM内部。AMD的CU寄存器更大,但切换开销也更大。

2.4 内存层次结构:从全局到寄存器

内存层次结构,说白了就是「数据怎么从硬盘跑到计算单元里」。我当年优化一个矩阵乘法的kernel,就是因为没搞懂内存层次,性能差了10倍。

全局内存(Global Memory):

  • 就是显存,HBM2e或GDDR6X
  • 带宽很大(Hopper H100有3.35TB/s),但延迟很高(几百个周期)
  • 所有线程都能访问,但速度最慢

共享内存(Shared Memory):

  • 在SM/CU内部,所有线程共享
  • 延迟低(几十个周期),容量小(几十KB)
  • 我习惯用它做数据复用——比如矩阵分块计算

局部内存(Local Memory):

  • 每个线程私有的「伪寄存器」
  • 实际上存在全局内存里,但编译器帮你管理
  • 当寄存器不够用时,变量会被「溢出」到这里
  • 性能杀手!我曾经遇到过寄存器溢出导致性能下降50%的情况

寄存器(Register):

  • 最快!一个周期就能访问
  • 每个线程可用数量有限(NVIDIA一般是255个/线程)
  • 编译器会尽量把变量分配到这里

避坑指南:我曾经在优化一个深度学习算子时,发现性能始终上不去。查了半天,原来是寄存器溢出导致的。每个线程用了260个寄存器,超过了255的限制,结果编译器把多余的变量放到了局部内存里。性能直接腰斩。解决办法?减少每个线程的工作量,或者用共享内存分担数据。

2.5 实战对比:一个简单的向量加法

咱们用代码看看不同架构下的差异。假设我们要做向量加法:C = A + B。

// NVIDIA CUDA (Ampere/Hopper)
__global__ void vec_add(float* A, float* B, float* C, int N) {
    int idx = blockIdx.x * blockDim.x + threadIdx.x;
    if (idx < N) {
        C[idx] = A[idx] + B[idx];
    }
}

// AMD HIP (RDNA/CDNA)
__global__ void vec_add(float* A, float* B, float* C, int N) {
    int idx = hipBlockIdx_x * hipBlockDim_x + hipThreadIdx_x;
    if (idx < N) {
        C[idx] = A[idx] + B[idx];
    }
}

你看,代码几乎一模一样。但底层执行完全不同:

  • NVIDIA上,32个线程组成一个Warp,一起执行
  • AMD上,如果是Wave32模式,也是32个线程一起;如果是Wave64,就是64个
  • 内存访问模式也不同——NVIDIA的L1和共享内存是统一的,AMD是分开的

我个人建议:写代码时别太在意架构差异,先把功能跑通。性能优化时再针对具体架构调整。毕竟,90%的性能问题都出在内存访问模式上,而不是计算单元。

2.6 小结与展望

今天咱们聊了NVIDIA和AMD的架构差异。说白了,NVIDIA更注重「通用性」——一个架构通吃图形和计算。AMD则选择「分而治之」——RDNA管图形,CDNA管计算。

下一章,我们会深入SM/CU内部的执行单元,看看指令是怎么被发射和执行的。到时候我会分享一个我调试Warp调度器的真实案例,保证让你大开眼界。

课后思考:为什么NVIDIA的Hopper要引入DPX指令?AMD为什么在RDNA上砍掉FP64?想明白这两个问题,你对架构设计的理解就能上一个台阶。