为什么 GPU 比 CPU 快?
为什么 NumPy 比 Python list 快?
先弄清楚:每个概念是什么
- Latency vs throughput
- CPU 追求单个任务尽快做完(latency),GPU 追求单位时间做完的总量(throughput)。CPU 像几位全能大厨,GPU 像几千个只会切菜的帮工,单个慢,但同一个动作几千人一起做。例:本机 CPU 10 个 core、16 个 thread,RTX 2080 Ti 有 4,352 个 CUDA cores。
- SIMD / SIMT 与 warp
- 一条指令同时算一排数据。CPU core 里有 SIMD 向量单元(x86 上是 AVX)。GPU 把 32 个 thread 编成一个 warp,同一时刻执行同一条指令(SIMT),所以分支多就吃亏,见 warp divergence。
- Memory bandwidth
- 每秒能从内存 / 显存搬多少字节。例(d2l):CPU 的 DDR4 合计 40–100 GB/s,消费级 GPU 的 GDDR6 超过 500 GB/s。
- Arithmetic intensity
- 每搬 1 byte 数据能做几次运算。高的操作卡在算力上(compute-bound),低的操作卡在 bandwidth 上(memory-bound)。
- Python object overhead
- Python list 里存的是 pointer,每个数都是一个单独的 Python object(带 type 和引用计数)。算
a[i] * b[i]时解释器要取 object、查 type、找乘法的实现,再新建一个 object 放结果。例:100 万个 float,list 本身 8.4 MB + 每个 float object 24 bytes,共约 32 MB,ndarray 只要 8 MB。 - Vectorization 与 BLAS
- ndarray 是同一 dtype、连续存放的原始数值。
A * B一次调用,整个循环在编译好的 C 里跑,能用上 SIMD。matmul 交给 BLAS 库(本机是 Intel MKL),它还会按 cache 分块、多线程并行。
30 秒回答
A CPU is built for latency: a few powerful cores, with transistors spent on caches, branch prediction and control logic, so one branchy thread runs fast. A GPU is built for throughput: thousands of simple cores running threads in 32-wide warps that execute the same instruction on different data. It hides memory latency by switching between warps and has much higher memory bandwidth. Deep learning is mostly large matrix multiplies and elementwise ops with little branching, exactly that workload. NumPy beats a Python list for a similar reason. A list holds pointers to boxed Python objects, so every element costs interpreter dispatch, a type check and a new object. An ndarray stores raw values of one dtype contiguously, so one vectorized call runs the whole loop in compiled C with SIMD, and matmul goes to a multithreaded BLAS.
关键公式:arithmetic intensity
float64 每个数 8 bytes。长度 \(n\) 的向量加法:\(n\) 次运算,读 2 个向量、写 1 个:
\[ \frac{n}{3n \times 8} = \frac{1}{24} \]\(n\times n\) matmul:\(2n^3\) 次运算(乘 + 加),理想情况下每个矩阵只搬一次:
\[ \frac{2n^3}{3n^2\times 8} = \frac{n}{12} \]实际速度 ≈ \(\min(\text{峰值算力},\ \text{intensity}\times\text{bandwidth})\)。intensity 低的操作加再多 core 也没用,只能靠 bandwidth;matmul 的 intensity 随 \(n\) 变大,才喂得饱成千上万个 core。GPU 两样都强。
本机实测
Intel Core 7 240H(10 cores / 16 threads),NumPy 2.3.5 + MKL,float64,多次运行取最小值:
| 操作 | 写法 | 时间 |
|---|---|---|
| 100 万个数逐个相乘 | Python for + append | ≈ 70 ms |
| list comprehension | ≈ 50 ms | |
NumPy A * B | ≈ 2 ms | |
| 200×200 matmul | 纯 Python 三重循环 | ≈ 480 ms |
NumPy X @ Y | ≈ 0.1 ms | |
| 2000×2000 matmul | MKL 限 1 个 thread | ≈ 0.6–0.7 s |
| MKL 默认(10 threads) | ≈ 50–57 ms,≈ 300 GFLOP/s | |
| 2000 万个数相加 | NumPy A + B | ≈ 16 ms,≈ 1.2 GFLOP/s |
最后两行里 matmul 每秒做的运算是加法的约 250 倍。加法 intensity 只有 1/24,被约 30 GB/s 的 bandwidth 卡住。matmul 的 intensity 约 167,能吃满算力。
import numpy as np, timeit
n = 1_000_000
a = [float(i) for i in range(n)]
b = [float(i % 7) for i in range(n)]
A, B = np.array(a), np.array(b)
t_list = timeit.timeit(lambda: [x * y for x, y in zip(a, b)],
number=10)
t_np = timeit.timeit(lambda: A * B, number=10)
print(t_list / t_np) # 本机约 20–30
必答点
- CPU:少数强 core,晶体管给 cache 和控制逻辑 → latency;GPU:几千个 thread,晶体管给计算单元 → throughput
- GPU memory bandwidth 高,靠切换 warp 把访存 latency 藏起来
- DL 是大量 matmul 和逐元素运算,同一条指令、分支少 → 适合 GPU
- matmul intensity \(n/12\) 高 → compute-bound;逐元素运算低 → memory-bound
- NumPy:同 dtype 连续存放 + 循环在 C 里 + SIMD + BLAS 多线程;list:pointer 指向 Python object,逐个解释执行、查 type、新建 object
追问
CPU 和 GPU 算 matmul 有什么区别?(NVIDIA 原题)
思路一样,都是把矩阵切成小块(tiling)搬到快的片上存储里反复用。CPU 上 BLAS 按 cache 分块,每个 core 用 SIMD 算一块。GPU 上每个 thread block 算输出的一个 tile,小块放进 shared memory,几千个 thread 同时算。完整的对比见 matmul 与 roofline。
GPU 访存 latency 很高,为什么还快?
每个 SM 上同时驻留很多 warp,它们的上下文一直在片上,切换没有开销。一个 warp 等内存时,scheduler 马上发射另一个 ready 的 warp。
什么时候 GPU 不比 CPU 快?
数据很小(kernel launch 和 CPU↔GPU 拷贝的开销盖过计算)、分支多(warp divergence)、稀疏或随机访存(打不满 bandwidth)、本身是前后依赖的串行任务。
用 for 循环遍历 ndarray 会快吗?
更慢。本机 100 万次 C[i] = A[i] * B[i] 约 160 ms,比 list 还慢,因为每次 A[i] 都要新建一个 NumPy scalar object。np.vectorize 也不加速,文档原话是 "essentially a for loop"。
来源CUDA Programming Guide v13.4 §1.1.2、§1.2.2.2、§2.3.3.2、§3.2.2.2 · NumPy: What is NumPy? · d2l §13.4 Hardware(§13.4.2、§13.4.4.2、§13.4.5)· Goodfellow et al., Deep Learning §12.1.2(pp.439–441)