gpu芯片简介 (国产gpu芯片)

早在21世纪初,在机器学习广泛使用GPU之前,CPU曾经是计算的最重要硬件。 GPU 全称Graphic Processing Unit,图像处理单元,主要是为图形开发的,很难用于科学计算。不足为奇的是,很少有程序员能够为使用GPU进行非图形相关的计算编写高效的代码。研究人员和一些显卡制造商意识到,程序员需要将GPU视为计算的一个重要部分,而不仅仅是花哨的超级专业硬件。因此,他们引入了一种新的编程思维方式,通常称为编程模型。在这个新的编程模型中,不同的计算可以在最适合该任务的不同设备上进行。例如,由于CPU擅长顺序计算,而GPU在设计上擅长并行计算,因此编程模型引入了CPU和GPU交换数据和同步操作的方法。

这方面最重要的进步之一是GPU和其从单纯的渲染处理器过渡到通用浮点处理器,能够在一定范围内进行任何我们希望的计算。两个主要的硬件供应商,AMD和NVIDIA,都制造了通常有数千核心以上的处理器的GPU,每个处理器都能够并行处理数据。这是一个很大的处理能力,而且不需要把它全部用于高端图形。甚至在当时Xbox 360也有一个能够进行基本通用处理技术的GPU,而索尼的PS3也有一个类似于GPU的架构,能够进行同样的处理。这些处理器的一个共同缺点是CPU和GPU之间的延迟和带宽问题。但在将GPU的线性计算模型与CPU的通用处理模型(称为加速处理单元(APU))相结合方面正在取得进展,这将大大减少这种延迟并提供其他优势。

随着硬件的发展,这种统一的模型简化了异质编程,NVIDIA将其称为计算统一设备架构或 CUDA (Compute Unified Device Architecture),其本质就是一种用于异质计算的统一的编程模型或架构。同样的CUDA编程模型有一个C/C++的编程接口,允许程序员同时为CPU和GPU的计算编写代码。当人们说他们在 "用CUDA编程 "时,最常提到的就是这个C/C++接口。几乎所有其他主要语言,如Python、Java、MATLAB甚至Fortran都有绑定。深度学习框架,如TensorFlow或PyTorch,使用C/C++ CUDA接口来实现矩阵乘法等操作,这些操作构成了密集层、卷积层、递归层和注意层的支柱,仅举几例。

CUDA在普及GPU编程方面取得了巨大的成功,没有其他异质计算模型能像CUDA一样在开发者中具有如此大的影响力和知名度。另一个竞争性的标准,OpenCL(由Apple 、AMD等公司支持),在2008年首次发布。2008 OpenCL为开发者提供了便捷的途径,使其能够为多核CPU和GPU等异构架构编写高效的跨平台应用程序,甚至包括索尼的PS3,其单一编程接口基于现代的C语言。OpenCL的规范是由Khronos管理的,他们也提供了一套C++支持。这些绑定大大简化了主机API的设置和代码开发的速度。 由于目前NVIDIA在深度学习应用中的主流地位,以下CUDA软硬件框架进行介绍。

GPU原理及编程是一个涉及范围较广的话题,通过了解GPU实现可以对此类高性能计算芯片当下的能力及未来的发展方向产生一些判断。

以下将从CUDA程序员的角度解释GPU硬件是如何组织的,一个GPU包含以下硬件块:

CUDA核心(Core)

GPU硬件的核心是一个被称为 "CUDA核心(Core) "的硬件单元,在软件方面执行一个 "线程" (Thread) 。一个CUDA核心可以执行乘法、除法或计算特殊函数的指令,例如,激活函数。尽管它们之间有许多不同之处,但把CUDA内核看作是相当于CPU内核的GPU是有帮助的。尽管一个CUDA核比一个CPU核要弱,但GPU有成千上万个CUDA核。例如,即使是消费级的RTX 3090 GPU也有超过10,000个CUDA核心。然而,围绕CUDA核心可以执行的指令有一些限制。

gpu芯片有哪些,gpu芯片发展现状

CUDA块和网格

CUDA的基本运算单元是线程(Thread),CUDA线程被分组为所谓的 "块"(Block)。一个块内的所有线程(Thread)都执行相同的指令,所有线程都在同一个SM上运行。块被进一步分组为称为CUDA网格(Grid)的实体。即Grid是最高级别的线程组织方式,Block位于Grid下一层,表示一个线程块,Thread是最小的线程单位,Block中包含一定数量的Thread。

gpu芯片有哪些,gpu芯片发展现状

CUDA内核(Kernel)

若想运行矩阵乘法,只是使用CPU,可以用for循环来编写矩阵乘法,通过矩阵的所有条目并执行所需的工作。因此,同一个CPU线程将产生输出矩阵的所有条目。而在GPU上,每个CUDA线程将只工作于输出矩阵的一个条目。因此需要一种方法来指定每个CUDA线程应该执行的计算。这可以通过被称为 "CUDA内核 "(Kernel)的特殊函数来实现。内核的编写方式是让不同的线程进行相同的计算,但对不同的数据进行计算,这种计算范式被称为单指令多线程或SIMT(Single Instruction Multiple Threads)。在CUDA术语中,通过 "启动 "CUDA内核在GPU上进行计算,能否写出合适的kernel,决定了能否正确解决问题和能否高效的解决问题。

流式多核处理器(SMs)

前面描述了在构建硬件层次结构时,从最小的计算硬件单元CUDA核心(Core)开始,可观察到线程(Thread)如何被分组为块(Block),块如何被分组为网格(Grid)。此外,网格的计算指令是由C++函数指定的,称为CUDA内核 (Kernel) 。流式多处理器(SM)是硬件层次结构中的第二高层。一个SM是GPU中的一个复杂的处理器,它包含协调数百个CUDA线程执行的硬件和软件。现代GPU包含几十个SM。例如,RTX 3090有82个SM。为了执行的目的,SM将线程块划分为 "卷"/束,即32个大小的组。物理上相互靠近的SM被进一步分组为称为图形处理集群(Graphics Processing Cluster,GPCs)的实体。

gpu芯片有哪些,gpu芯片发展现状

全局存储器或VRAM

到目前为止,已经介绍了GPU的计算单元,接下来讨论的内存单元更重要。计算的大部分功耗和延迟都是由于内存传输而不是计算造成的。因此,了解和优化内存访问延迟可以将工作负载的速度提高几个数量级。GPU中最高级别的内存层次是全局内存或消费级GPU中的VRAM(Video RAM), 这是营销材料中引用的规格,也是大多数用户对GPU内存的理解。我们都曾在TensorFlow或PyTorch中遇到过CUDA内存不足的错误。当模型或不能适应GPU的全局内存时,就会发生OOM错误。这就是为什么大内存允许我们以大批量的方式训练更大的模型,但CUDA为程序员提供了对更丰富的内存层次的控制。

gpu芯片有哪些,gpu芯片发展现状

共享内存

共享内存大致相当于CPU中的高速缓存。当编写在CPU上运行的软件时,程序员无法控制高速缓存,由CPU自行控制。而CUDA为程序员提供了一种方法,可以直接控制数据应该存在于缓存中,及线程应该可以访问的数据。在CUDA中,共享内存在物理上靠近CUDA核心,从共享内存中获取数据的速度至少是全局内存的10倍。这在许多深度学习的工作流程中是非常有帮助的。例如,当对图像应用众所周知的高斯模糊滤波器时,许多CUDA线程都需要输入图像的每个像素位置。在这种情况下,CUDA允许在附近像素上工作的线程使用共享内存尽快访问他们需要的数据。共享内存对同一区块的线程是可见的。

只读缓存/纹理内存 Tex

顾名思义,这是一个位于物理上靠近CUDA核心的只读缓存,在一个经线内共享。在这里,只读意味着存储在这个内存中的数据在内核执行的过程中不会发生变化。常见的图像处理工作负载,如缩放二维或三维阵列(如图像大小调整),大大受益于这种内存,这就是为什么它也被称为纹理内存。

寄存器

到目前为止,我们讨论的所有内存类型都是在一个经线、块或SM的线程之间共享的。相比之下,寄存器是专门用于每个线程的小型内存库。我们已经讨论了一个块中的线程如何执行相同的指令。然而,每个线程(核心)的中间计算结果的数值是不同的。寄存器允许线程存储变量的本地副本,这些副本只对那一个线程(核心)可见。

CUDA编程不需要知道或关心寄存器来编写功能性的CUDA代码,但理解每个SM都有固定的、有限的寄存器数量是基础。一个声明了很多不必要的局部变量的CUDA内核比一个尽可能有效地使用寄存器的内核要慢得多。通常情况下,寄存器的分配是由编译器(nvcc)处理的,编程时唯一能控制的就是局部变量的数量和大小。

统一内存(UM)

编程时,跟踪哪些变量属于哪个处理器是相当费力的。UM(Unified Memory)是CUDA的一个软件功能,它允许程序员忘记CPU和GPU内存之间的区别,将系统中所有可用的内存视为一个大的统一的整体。在编程方面,这意味着你可以声明一个变量,为它分配一次内存,并在CPU和GPU上使用它。

GPU微架构及编程总结

以上介绍了构成深度学习基础的硬件和软件栈。当前主要的GPGPU(General-purpose computing on graphics processing units,通用图形处理器)基本类型都是按照此类硬件构建,基础构成即内核、线程、块、SMs和内存层次结构的各个层次,用CUDA实现了密集层的前向传递。与CPU相比,通过相对较少的时钟周期, GPU可以将矩阵乘法的速度提高数百倍。由于密集层的后向传递也需要矩阵乘法,同样的基本思想可以用来实现后向传递和所有其他主要类型的层。