GPU vs CPU
名词解释
| 名词 | 通俗解释 |
|---|---|
| Die(芯片裸片) | 封装壳里那块薄薄的硅片,是芯片的"本体",上面刻着几十亿个晶体管电路 |
| Core(核心) | 芯片里能独立执行计算任务的"大脑单元",就像一个工人。4核CPU = 4个工人 |
| ALU(算术逻辑单元) | Core里专门做整数加减乘除和逻辑判断的电路,是最基本的计算部件 |
| FPU(浮点运算单元) | Core里专门处理小数(如3.14)运算的电路,"浮点"指小数点位置会随数值大小移动 |
| Cache(缓存) | 芯片内部的"小抄本",把常用数据存在离Core最近的地方,避免每次去远处内存取 |
| L1/L2/L3 Cache | 缓存分三层,L1最快最小(离Core最近),L3最慢最大 |
| RAM / 主内存 | 电脑运行时存放数据的地方,断电消失,比Cache慢但容量大(如16GB) |
| DDR5 | 目前主流CPU使用的内存规格,第五代 |
| HBM(高带宽内存) | GPU专用内存,紧贴芯片封装,传输速度是普通内存的约30倍 |
| 时钟周期(cycle) | 芯片工作的最小时间间隔,由时钟频率决定。3GHz芯片每秒震荡30亿次,即每个周期约0.33纳秒。频率越高,每个周期越短,运算越快 |
| 线程(Thread) | 一段独立执行的指令序列,可理解为"一个任务" |
| SM(流式多处理器) | GPU里的"小计算集群",包含大量简单计算核心 |
| 计算核心 | GPU里最基础的计算单元,只做简单运算,但数量极多 |
| Warp(线程束) | GPU里32个线程被捆绑成一组,必须步调一致地执行同一条指令 |
| SIMD | 一条指令同时处理多份数据,通过加宽硬件通道实现 |
| SIMT | GPU的并行方式,一条指令让多个线程同时执行 |
| 分支预测 | CPU技巧:if-else还没判断完时提前猜走哪条路 |
| 乱序执行 | CPU技巧:不按代码顺序,把"现在能做的"先做了 |
| 带宽 | 数据传输的"马路宽度",带宽越高,单位时间搬运的数据越多 |
一、核心哲学的对立
CPU和GPU虽然都叫"处理器",但设计目标截然相反:
| 维度 | CPU | GPU |
|---|---|---|
| 设计目标 | 最小化延迟(让单个任务尽快完成) | 最大化吞吐量(让尽量多任务同时运行) |
| 核心数量 | 少(常见4~64个) | 极多(数千个,高端超过一万个) |
| 核心能力 | 每个核心都很"聪明"(会预判、会变通) | 每个核心都很"简单"(只管埋头干活) |
| 适合任务 | 复杂逻辑、操作系统、数据库、网页服务 | 大规模重复计算:AI训练、图形渲染、科学模拟 |
生活类比:
- CPU = 顶尖外科医生:一次专注一台手术,技术全面,能应对各种突发情况
- GPU = 流水线工厂:几千个工人同时各做一道工序,单个工人技能简单,但整体产出惊人
二、硬件架构拆解
2.1 什么是Die(芯片裸片)?
买到手的CPU/GPU外面是一个封装外壳。把外壳打开,里面有一块薄薄的硅片,这就是Die。Die上面用光刻技术刻出了几十亿个晶体管,组合成各种电路。
你买到的CPU芯片(外观):
┌──────────────────┐
│ 封装外壳 │
│ ┌────────────┐ │
│ │ Die(硅片)│ │ ← 真正的"芯片本体"
│ └────────────┘ │
│ 金属触点(针脚) │ ← 插在主板上的接口
└──────────────────┘
CPU Die 和 GPU Die 都是硅片,只是上面刻的电路设计完全不同。
2.2 CPU架构:为"聪明的独奏家"打造
┌──────────────────────────────────────────────┐
│ CPU Die(芯片本体) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Core 0 │ │ Core 1 │ │ Core 2 │ │
│ │ ──────── │ │ ──────── │ │ ──────── │ │
│ │ ALU │ │ ALU │ │ ALU │ │ ← 整数加减乘除
│ │ FPU │ │ FPU │ │ FPU │ │ ← 小数运算
│ │ L1 Cache │ │ L1 Cache │ │ L1 Cache │ │ ← 每核自己的"便签"
│ │ L2 Cache │ │ L2 Cache │ │ L2 Cache │ │ ← 稍大的"抽屉"
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ 共享L3 Cache(所有核共用的"文件柜") │
│ │
│ 内存控制器 → 外部主内存(RAM) │
└──────────────────────────────────────────────┘
各组件解释:
| 组件 | 作用 |
|---|---|
| ALU | 处理整数运算(1、100这类没有小数点的数)的"计算器" |
| FPU | 处理小数(如3.14,AI模型权重)的专用计算器 |
| L1/L2 Cache | Core自己的"随身便签",存最近用过的数据,极快但很小 |
| L3 Cache | 所有Core共享的"公共资料室",比L1/L2慢但比内存快 |
| 主内存(RAM) | 容量大(16-64GB),但访问需要约200个时钟周期 |
为什么要分这么多层缓存?
主内存距离Core较远,取一次数据要等200个时钟周期。好比工人需要资料,但资料室在2公里外。解决:在工人旁边放小书架(L1),再远点放大书架(L2/L3),常用资料提前放进来,大多数时候就不用跑远路。
2.3 GPU架构:为"万人合唱团"打造
GPU的设计思路和CPU完全不同------不追求每个工人有多聪明,而是追求有多少工人同时干活。
关于厂商差异: 下图是通用GPU架构,不依赖特定厂商。NVIDIA、AMD、Intel的GPU都遵循类似的层次结构。各厂商只是叫法不同(NVIDIA叫SM,AMD叫CU,Intel叫Xe-core),但原理完全相同。
┌──────────────────────────────────────────────────────────┐
│ GPU Die(芯片本体) │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ SM(流式多处理器,GPU的"小计算集群") │ │
│ │ │ │
│ │ ┌────┐┌────┐┌────┐┌────┐ (几十到上百个核心) │ │
│ │ │核心││核心││核心││核心│ │ │ ← 简单计算单元
│ │ └────┘└────┘└────┘└────┘ │ │
│ │ │ │
│ │ Warp调度器 + 寄存器堆 + 共享内存 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ × N个SM(几十到上百个同时工作) │
│ │
│ 高速专用内存(HBM)←→ L2 Cache ←→ SM │
└──────────────────────────────────────────────────────────┘
各组件解释:
| 组件 | 作用 |
|---|---|
| SM | GPU里的"工人小队",包含几十到上百个简单计算核心 |
| 计算核心 | 最基本的计算单元,只做简单运算,但数量极多,靠数量取胜 |
| Warp调度器 | SM里的"小管家",决定哪32个线程接下来执行 |
| 寄存器堆 | 每个线程专属的"便签纸",始终保留在硬件里------这是GPU能快速切换线程的关键 |
| 高速专用内存(HBM) | GPU专用内存,传输速度是普通电脑内存的约30倍 |
三、延迟 vs 吞吐量:最根本的取舍
3.1 先理解这两个概念
- 延迟:完成一件事需要多长时间(越短越好)
- 吞吐量:单位时间内能完成多少件事(越多越好)
生活例子:
| 延迟 | 吞吐量 | |
|---|---|---|
| 外科医生 | 一台手术 = 2小时 | 一天能做 = 4台 |
| 流水线工厂 | 做一件衬衫 = 1天 | 每天产出 = 10,000件 |
两者都有价值,只是适用场景不同。
3.2 CPU的策略:消灭等待
CPU最大的"敌人"是内存访问的等待。每次从主内存读数据需要等约200个时钟周期------相当于"停工200步":
没有优化时:
[取指令] → [等主内存....200步....] → [执行] → [写回]
↑
这200步CPU什么都没干,纯粹在等
CPU的三大优化手段:
① 大Cache:把常用数据提前存在"手边"
② 分支预测:if-else还没判断完,提前猜结果
③ 乱序执行:当前指令在等待时,把后面"不依赖这个结果"的指令先执行
乱序执行举例:
代码顺序(如果严格按序):
步骤1: 从内存读A(等200步)
步骤2: 从内存读B(等200步)
步骤3: C = A + B
顺序执行总耗时:200 + 200 + 1 = 401步
CPU实际执行(乱序):
→ 同时发出"读A"和"读B"的请求(两个等待并行进行!)
→ 200步后A和B都到了
→ 执行C = A + B
实际耗时:200 + 1 = 201步(节省近一半!)
CPU核心像一位全能魔术师:当一件事卡住时,它会变着花样继续工作。
3.3 GPU的策略:用线程淹没等待
GPU根本不试图消灭等待,而是备好海量线程,让等待中的线程"靠边站",立刻切换到其他准备好的线程:
GPU同时有1000组线程(每组32个,叫一个Warp)在排队:
时间轴:
Warp 0: [执行] → [要读内存,等着...]
↓ 立刻切换,零代价
Warp 1: [执行] → [要读内存,等着...]
↓ 立刻切换
Warp 2: [执行] → [要读内存,等着...]
↓ ...
...(还有997个Warp在排队)...
当所有Warp都轮完一圈,Warp 0的内存数据早就回来了:
Warp 0: [继续执行] ✅
为什么GPU切换线程是"零代价"的?
CPU切换线程需要保存/恢复所有寄存器数据,需要额外时间。GPU不同:每个Warp的所有寄存器始终保留在硬件里(不会被覆盖),切换瞬间完成。
顿悟点: GPU不怕延迟,它靠海量并发线程来掩盖延迟。只要排队的线程足够多,等待时间就会被完全"填满"。
四、SIMD vs SIMT:两种"一次干多件事"的方式
4.1 CPU的SIMD:加宽数据"通道"
SIMD = Single Instruction Multiple Data = 单指令多数据
"一条指令可以附带多个数据参数吗?"
不是"附带参数",而是数据本身被打包成更宽的格式,硬件电路同时处理这一大包数据。
通俗类比:
-
普通加法:单车道,每次只能过1辆车(处理1个数)
-
SIMD加法:把道路拓宽成16车道,同一时刻过16辆车(处理16个数)
普通加法(一次1个数,需要16条指令):
指令1: 1 + 1 = 2
指令2: 2 + 2 = 4
...
指令16: 16 + 16 = 32SIMD加法(一次16个数,只需1条指令):
输入A:[1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16]
输入B:[1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16]
输出C:[2, 4, 6, 8,10,12,14,16,18,20,22,24,26,28,30,32]
16次运算同时完成!
关键理解: 这不是顺序执行16次,而是硬件电路本身就有16条并行的计算通道,物理上同时完成------就像16车道收费站和单车道的区别。
局限: 每次最多处理多少个数,由芯片电路宽度决定(不能无限扩展)。程序员需要专门改写代码才能用上SIMD。
4.2 GPU的SIMT:让多个线程"齐步走"
SIMT = Single Instruction Multiple Threads = 单指令多线程
GPU把32个线程捆绑成一个Warp,这32个线程共用一个"指令发射器",每个时钟周期只发出一条指令,但32个线程同时执行这条指令,各自处理自己的数据:
Warp(32个线程同步执行一条加法指令):
线程 0: a[0] + b[0] = c[0] ┐
线程 1: a[1] + b[1] = c[1] │
线程 2: a[2] + b[2] = c[2] │ ← 这32个线程在同一时刻
... │ 执行"同一条"加法指令
线程31: a[31]+ b[31]= c[31] ┘ 但每个线程处理各自的数据
SIMT最妙的地方: 程序员写代码时,就像在写"只处理一个元素的单线程程序",硬件自动把它复制到32个线程同时执行。程序员不需要关心向量化的细节。
4.3 SIMD和SIMT的本质区别
| SIMD(CPU) | SIMT(GPU) | |
|---|---|---|
| 并行方式 | 加宽数据通道(电路更宽) | 多个线程同步执行 |
| 并行上限 | 受芯片电路宽度限制(如一次最多16个float) | 受线程数量限制(可以几百万个线程) |
| 编程难度 | 需要手动向量化或特殊指令 | 写普通逻辑,框架自动并行 |
| 适合规模 | 小规模批量运算(几十个数) | 超大规模并行(几百万个数) |
五、内存层次结构对比
5.1 CPU内存层次(追求低延迟)
速度最快 ↑ 寄存器 ~1个时钟周期 ← Core的"双手"
容量最小 ↑ ↓
L1 Cache ~4个时钟周期 ← 每核自己的"便签",32-64KB
↓
L2 Cache ~12个时钟周期 ← 每核自己的"抽屉",256KB-1MB
↓
L3 Cache ~40个时钟周期 ← 所有核共享的"文件柜",32-192MB
↓
速度最慢 ↓ 主内存(RAM)~200个时钟周期 ← 电脑的"书架",8-128GB
容量最大 ↓
5.2 GPU内存层次(追求高带宽)
速度最快 ↑ 寄存器 ~1个时钟周期 ← 数量极多,始终保留在硬件里
容量最小 ↑ ↓
共享内存 ~20-30个时钟周期 ← SM内线程共享的"小黑板"
↓
L1/L2 Cache ~30-200个时钟周期
↓
速度最慢 ↓ 高速专用内存 ~400个时钟周期 ← 延迟更高!但带宽是普通内存的30倍
容量最大 ↓
重要发现: GPU高速专用内存(HBM)的延迟(400周期)比CPU主内存(200周期)还高!但它的带宽是普通内存的约30倍。
为什么GPU反而要用延迟更高的内存?
矩阵乘法需要同时处理几百万个数字,关键在于"一次能搬多少数据"(带宽),而不是"某一个数据多快到达"(延迟)。
类比:
- CPU内存 = 快递专车:速度快,但一辆只送一个包裹
- GPU内存 = 大型货轮:单趟等待时间长,但一次运几万个集装箱,整体货物流量惊人
六、分支处理:GPU的软肋
6.1 什么是"分支"?
程序里的if-else就是分支,根据条件走不同的执行路径。
6.2 CPU如何处理分支(非常擅长)
CPU有分支预测器:在条件还没判断出来之前,根据历史规律猜测"这次可能走哪条路",提前开始执行。
- 猜对了:节省等待时间
- 猜错了:丢弃结果重来
现代CPU分支预测准确率高达95%以上,大量if-else对CPU几乎没有影响。
6.3 GPU如何处理分支(很痛苦)
GPU的32个线程(一个Warp)必须步调一致,同一时刻只能执行同一条指令。
当if-else让不同线程走不同路径时:
Warp里32个线程遇到if-else:
线程 0~15:应该执行A路径
线程16~31:应该执行B路径
但32个线程只能统一行动!GPU的处理方式(很无奈):
第1步:所有线程执行A路径
线程 0~15:正常执行A [有效]
线程16~31:被"屏蔽",空转 [浪费!]
第2步:所有线程执行B路径
线程 0~15:被"屏蔽",空转 [浪费!]
线程16~31:正常执行B [有效]
效率减半!
这就是Warp Divergence(线程束分化)------原本步调一致的线程,因为if-else而"分道扬镳"。
结论:
- CPU写满if-else的复杂逻辑 → 完全没问题
- GPU写满if-else的复杂逻辑 → 性能大幅下降
- 好的GPU程序要尽量让同一Warp里的线程"命运相同"
七、适用场景总结
CPU擅长
- ✅ 复杂业务逻辑(大量判断、分支、递归调用)
- ✅ 追求快速响应的任务(网页请求、键盘输入)
- ✅ 操作系统、任务调度、文件读写
- ✅ 不规则数据结构(链表、树)
GPU擅长
- ✅ 矩阵乘法(深度学习核心运算)
- ✅ 图像/视频处理(每个像素独立处理)
- ✅ 科学模拟(流体动力学、气象预报)
- ✅ 图形渲染(游戏画面)
八、深度学习为什么离不开GPU?
以矩阵乘法 C = A × B(1024×1024)为例:
需要计算:1024 × 1024 × 1024 = 约10亿次乘加运算
CPU(8核,3GHz):
- 每核每周期约1次FMA(乘加)
- 理论峰值:3GHz × 8核 = 24 GFLOPS
- 耗时:约40毫秒
GPU(高端型号):
- 专用Tensor Core,每周期可完成多次小矩阵乘法
- 理论峰值:约1000 TFLOPS
- 耗时:约0.001毫秒
- 加速比:约40,000倍!
Tensor Core(张量核心):GPU专门为矩阵乘法设计的硬件单元,比普通计算核心快几十倍。
九、顿悟总结
| CPU | GPU | |
|---|---|---|
| 哲学 | 让一件事做得极快 | 让很多事同时做 |
| 核心 | 少而强 | 多而简 |
| 延迟 | 极力消除 | 用并发掩盖 |
| 分支 | 擅长(预测器) | 怕(Warp分化) |
| 内存 | 大缓存,低延迟 | 高带宽,HBM |
| 编程 | 串行思维 | 数据并行思维 |
| 类比 | 瑞士军刀 | 流水线工厂 |
最终顿悟: GPU不是"更快的CPU",而是完全不同的计算范式 。当你的问题能被拆解成百万个相同的小问题时,GPU就是终极武器。深度学习训练的本质,就是把学习过程变成了这样一个"百万相同小问题"。