撰文 | Will Zhang
随着软件从1.0进化到2.0,即从图灵机演进到类深度学习算法。计算用的硬件也在加速从CPU到GPU等迁移。本文试图整理从2010年到2020年这十年间的英伟达GPU架构演进史。
1
CPU and GPU
我们先对GPU有一个直观的认识,如下图:
众所周知,由于存储器的发展慢于处理器,在CPU上发展出了多级高速缓存的结构,如上面左图所示。而在GPU中,也存在类似的多级高速缓存结构。只是相比CPU,GPU将更多的晶体管用于数值计算,而不是缓存和流控(Flow Control)。这源于两者不同的设计目标,CPU的设计目标是并行执行几十个线程,而GPU的目标是要并行执行几千个线程。
可以在上面右图看到,GPU的Core数量要远远多于CPU,但是有得必有失,可以看到GPU的Cache和Control要远远少于CPU,这使得GPU的单Core的自由度要远远低于CPU,会受到诸多限制,而这个限制最终会由程序员承担。这些限制也使得GPU编程与CPU多线程编程有着根本区别。
这其中最根本的一个区别可以在上右图中看出,每一行有多个Core,却只有一个Control,这代表着多个Core同一时刻只能执行同样的指令,这种模式也称为 SIMT (Single Instruction Multiple Threads). 这与现代CPU的SIMD倒是有些相似,但却有根本差别,本文在后面会继续深入细究。
从GPU的架构出发,我们会发现,因为Cache和Control的缺失,只有 计算密集 与 数据并行 的程序适合使用GPU。
-
计算密集:数值计算的比例要远大于内存操作,因此内存访问的延时可以被计算掩盖,从而对Cache的需求相对CPU没那么大。
-
数据并行:大任务可以拆解为执行相同指令的小任务,因此对复杂流程控制的需求较低。
而深度学习恰好满足以上两点,本人认为,即使存在比深度学习计算量更低且表达能力更强的模型,但如果不满足以上两点,都势必打不过GPU加持下的深度学习。
2
Fermi
Fermi是Nvidia在2010年发布的架构,引入了很多今天也仍然不过时的概念,而比Fermi更早之前的架构,也已经找不到太多资料了,所以本文从Fermi开始,先来一张总览。
GPU通过Host Interface读取CPU指令,GigaThread Engine将特定的数据从Host Memory中拷贝到内部的Framebuffer中。随后GigaThread Engine创建并分发多个Thread Blocks到多个SM上。多个SM彼此独立,并独立调度各自的多个Thread Wraps到SM内的CUDA Cores和其他执行单元上执行。
上面这句话有几个概念解释一下:
-
SM: 对应于上图中的SM硬件实体,内部有很多的CUDA Cores
-
Thread Block: 一个Thread Block包含多个线程(比如几百个),多个Blocks之间的执行完全独立,硬件可以任意调度多个Block间的执行顺序,而Block内部的多个线程执行规则由程序员决定,程同时程序员可以决定一共有多少个Blocks
-
Thread Warp: 32个线程为一个Thread Warp,Warp的调度有特殊规则,本文后面会继续深入
由于本文不是讲怎么写CUDA,所以如果对SM/Block的解释仍然不明白,可以参考这一小节:https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#scalable-programming-model
上图存在16个SMs,每个SM带32个Cuda Cores,一共512个Cuda Cores. 这些数量不是固定的,和具体的架构和型号相关。
接下来我们深入看SM,来一张SM总览。
从上图可知,SM内有32个CUDA Cores,每个CUDA Core含有一个Integer arithmetic logic unit (ALU)和一个Floating point unit(FPU)