1、GPU计算概述:GPU vs CPU架构差异、GPU在并行计算中的优势、GPU编程模型简介(SIMT/SIMD)
好,咱们直接进入正题。今天聊的是GPU计算最基础的东西——它跟CPU到底差在哪?凭什么GPU能跑得那么快?以及,它那个有点反直觉的编程模型到底是怎么回事。
我刚开始接触GPU编程时,说实话,挺懵的。脑子里全是CPU那套“单核主频高、缓存大、乱序执行”的思维定势。结果一上手,发现完全不是那么回事。嗯,咱们今天就把它掰扯清楚。
1.1 CPU vs GPU:两种完全不同的哲学
先看一张图,这是我个人觉得最能说明问题的对比:
你看,CPU就像个全能特种兵——控制单元大、缓存大、逻辑复杂,什么活儿都能干,但数量少。GPU呢?更像一支军队——每个士兵(ALU)很简单,但数量成千上万。控制单元和缓存被压缩到最小,把面积全让给了计算单元。
核心差异一句话:CPU追求低延迟,GPU追求高吞吐。CPU要的是“单个任务跑得快”,GPU要的是“一堆任务一起跑得快”。
我在项目中遇到过这样一个场景:用CPU做图像滤波,一张1080p的图要跑几十毫秒。换成GPU后,同样的算法,同样的精度,直接降到1毫秒以内。为什么?因为图像里每个像素的计算是独立的,GPU可以同时处理上万个像素。
1.2 GPU在并行计算中的优势
说白了,GPU的优势就三个字:多、快、省(当然,是相对的)。
- 多:核心数量多。一个现代GPU有几千个CUDA核心,而CPU最多几十个核。
- 快:内存带宽高。GPU用HBM或GDDR显存,带宽轻松上TB/s,CPU的DDR5也就几十GB/s。
- 省:能效比高。同样的计算量,GPU消耗的功耗往往比CPU集群低得多。
但注意,不是所有任务都适合GPU。你想想看,如果任务之间有强依赖——比如“必须等上一步算完才能算下一步”——那GPU的优势就发挥不出来。我见过有人硬把链表遍历塞进GPU,结果比CPU还慢。嗯,这就是典型的“用错工具”。
| 特性 | CPU | GPU |
|---|---|---|
| 核心数 | 4-64 | 1024-16384+ |
| 内存带宽 | ~50 GB/s | ~1-2 TB/s |
| 适合任务 | 串行、复杂逻辑、分支多 | 并行、数据密集、分支少 |
| 典型功耗 | 65-150W | 150-450W |
我的经验:判断一个算法适不适合GPU,就看它能不能拆成“一堆独立的小任务”。能拆,GPU就是神器;拆不了,老老实实用CPU。
1.3 GPU编程模型:SIMT vs SIMD
好,接下来是重点。GPU的编程模型,说白了就是“怎么让这么多核心协同工作”。这里有两个关键概念:SIMD 和 SIMT。
SIMD(单指令多数据)
这个其实CPU也有。一条指令,同时对多个数据执行相同操作。比如CPU的AVX指令集,一次能处理256位数据(8个32位整数)。但CPU的SIMD宽度有限,而且需要程序员手动写向量化代码。
SIMT(单指令多线程)
这是GPU的看家本领。你写一个线程函数,然后告诉GPU:“给我启动一万个线程,每个线程执行这个函数。”GPU硬件会自动把这些线程分组(比如32个一组,叫一个warp/wavefront),然后让一组线程共享一条指令。
为什么会这样?因为GPU的设计哲学是“用控制单元的简化换取计算单元的倍增”。一组线程共享指令,意味着只需要一个指令解码器,省下来的晶体管全拿去堆ALU。
避坑指南:我曾经在一个项目中,让同一个warp里的32个线程走了不同的if-else分支。结果呢?性能直接腰斩。因为SIMT要求同一组线程执行同一条指令,遇到分支时,硬件只能串行执行所有分支路径。这就是所谓的“warp divergence”。
来看一个简单的例子,感受一下SIMT的写法:
// CUDA 示例:向量加法
__global__ void vecAdd(float* A, float* B, float* C, int N) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < N) {
C[i] = A[i] + B[i];
}
}
// 启动 256 个线程块,每块 128 个线程
vecAdd<<<256, 128>>>(A, B, C, 100000);
你看,每个线程只处理一个元素。你写的是“一个线程的行为”,但GPU帮你把成千上万个这样的线程同时跑起来。这就是SIMT的精髓——用线程抽象隐藏硬件细节。
1.4 小结:一张图看懂本章知识体系
最后,我用一张流程图把今天的内容串起来:
嗯,今天的内容就到这。记住一句话:GPU不是万能的,但在对的任务上,它是无敌的。 下一章我们会深入编译器视角,看看GPU是怎么把高级语言翻译成这些并行指令的。
公众号:蓝海资料掘金营,微信deep3321