如果说CPU是计算机的“大脑”,善于处理各种通用型计算任务,但在面对大规模并行计算时,CPU 就有点力不从心了。尤其是在图形渲染、游戏画面处理、人工智能训练等场景中,需要处理成千上万的像素、顶点和矩阵运算,CPU 的串行处理方式明显效率不够。
于是,GPU 应运而生。最早的 GPU 被设计用于图形渲染,一说到图形渲染,聪明的你一定会想到显卡,没错,GPU就是显卡的核心元器件。
GPU的目标就是通过成百上千个小而高效的运算单元,实现对图像的快速并行处理。
随着AI、HPC的快速增长,GPU加速计算已经成为推动科学发展的关键力量,在天文学、物理学等研究领域,GPU加速的AI正在帮助科学家们解决前所未有的复杂问题。
同样是处理器,为什么 GPU 在并行计算中表现得如此出色?今天我们就从底层运作机制和设计原理,聊聊为什么GPU在并行计算的时候更高效。
处理器的三个组成部分
任何处理器的核心组成都离不开三部分:算术逻辑单元(ALU)、控制单元和缓存。但 CPU(中央处理器)和 GPU(图形处理器)的 "工作思路" 却大相径庭。
简单来说,CPU更善于一次处理一项任务,而且GPU则可以同时处理多项任务。这是因为CPU是为延迟优化的,而GPU则是带宽优化的。就好比有些人善于按顺序一项项执行任务,有些人可同时进行多项任务。
通过打比方来通俗的解释二者的区别。
CPU就好比一辆摩托车赛车,而GPU则相当于一辆大巴车,如果二者的任务都是从A位置将一个人送到B位置,那么CPU(摩托车)肯定会更快到达,但是如果将100个人从A位置送到B位置,那么GPU(大巴车)由于一次可以运送的人更多,则运送100人需要的时间更短。
换句话说,CPU 单次执行任务的时间更快,但是在需要大量重复工作负载时,GPU 优势就越显著(例如矩阵运算:(A*B)*C)。
因此,虽然CPU单次运送的时间更快,但是在处理图像处理、动漫渲染、深度学习这些需要大量重复工作负载时,GPU优势就越显著。
综上所述,CPU 是个集各种运算能力的大成者。它的优点在于调度、管理、协调能力强,并且可以做复杂的逻辑运算,但由于运算单元和内核较少,只适合做相对少量的运算。
GPU 无法单独工作,它相当于一大群接受 CPU 调度的流水线员工,适合做大量的简单运算。
那么是什么导致CPU和GPU工作的方式不同呢?那还要从二者设计理念来说。
FLOPS并不是核心问题?
提到处理器性能,很多人会关注 FLOPS。但实际上,它并非决定效率的核心因素。
FLOPS每秒浮点运算次数(FLoating point Operations Per Second,简称 FLOPS)是基于处理器在一秒内可以执行的浮点算术计算数量,经常用来来衡量计算机性能的指标,表示每秒可以执行的浮点运算次数。
虽然有一些专家或特定算法的时候会特别关注FLOPS,但FLOPS其实并不是大众关心的焦点。
为什么会这样说呢?举个例子,在CPU运行情况:CPU能以大约2000 GFLOPs FP64的速度进行运算,但内存却只能以200 GB/s的速度向CPU提供数据,这是现代处理器的典型性能。
于是当CPU想要每秒处理2万亿个双精度数值,但内存每秒只能提供250亿个。这个时候就会产生设备的“计算强度”不平衡,这个时候就需要CPU设备需要付出多少努力来弥补内存提供数据的速度不足。
否则,处理器就会因为闲置造成浪费,陷入所谓的“内存带宽限制”模式。
而 GPU 的解决思路是:用高带宽内存平衡计算能力。
比如 NVIDIA 的 GPU,不仅 FLOPS 更高,还配备了高带宽内存(如 HBM),让数据供给速度能跟上计算需求。但随着 GPU 性能提升,计算能力增长往往快于内存带宽,这就需要不断优化算法,才能让硬件高效运转。
当然,高内存支持和代码优化并不是GPU性能优势的全部,我们还需要看一下延迟。
为何延迟如此关键呢?
延迟,指的是从发起数据请求到数据返回的等待时间。它之所以关键,是因为物理定律的限制 —— 电信号在『芯片』内传输需要时间,从内存取数据甚至可能耗时十到二十个时钟周期。
我们可以通过一个时间线来直观理解。从最基础的运算操作来看:ax + y。
首先,要加载变量x。接着,加载y。因为运算是a乘以x再加上y。
所以,会同时发起对y的加载请求。然后,会经历一段相当长的等待时间,直到x的数据返回。这段时间往往是空闲的,也就是我们所说的延迟,这样就导致计算非常不高效。
虽然这个时间很短,也可能被其它有用的计算工作所掩盖,不会造成明显的延迟。但处理器编译器实际上花费了大量精力来进行流水线优化,确保数据加载尽可能早地发起,以便被其它计算操作所覆盖。
这种流水线处理是大多数程序性能优化的关键,因为内存访问的延迟往往比计算延迟要大得多。
那么为什么会这样呢?
这是因为在一个时钟周期内,光只能传播很短的距离。考虑到『芯片』的尺寸,电信号从『芯片』的一侧传输到另一侧可能需要一个或多个时钟周期。
因此,物理定律成为了限制性能的关键因素。尤其是当需要从内存中获取数据时,数据的往返传输可能就需要十到二十个时钟周期。
延迟就意味着花费了大量时间等待数据的到来。
CPU 为了减少延迟,会通过复杂的流水线优化,让数据加载尽可能提前,用其他计算覆盖等待时间。但即便如此,内存延迟仍可能成为瓶颈。
以Xeon 8280为例,这款CPU拥有131GB的内存和89纳秒的延迟。当内存带宽为131GB/s时候,那么在一个内存延迟周期内,只能移动约11659字节的数据。
即使有高带宽的内存来应对计算强度,实际上几乎没有利用到它的优势。为高性能的CPU和内存付出了巨大的成本,但结果却并不理想。
这是因为程序受到了延迟绑定的影响,这是一种常见的内存限制形式,其发生的频率远高于我们的想象。
如果将11659字节的数据除以16字节(即DAXPY操作加载x和y所需的总字节数),发现需要同时执行729个DAXPY迭代,才能让花在内存上的钱物有所值。因此,面对这种低内存效率,需要同时处理729个操作。
这个时候,就需要并发来解决这个问题了。并发,顾名思义,就是同时进行许多事情。
GPU编译器有一种优化手段叫做循环展开,它能够识别出可以独立执行的部分,并将它们连续地发出,从而提高执行效率。
但是在实际循环进行的优化方式受限于硬件能够同时跟踪的操作数量,几乎是不可能完成的。在硬件的流水线中,它只能同时处理有限数量的事务,超出这个数量就不得不等待之前的事务完成。
因此,循环展开确实有益,它可以让流水线更加饱满,但显然它也受到机器架构中其它多种因素的制约。
这个时候,就需要看硬件的所能支持的最大线程数了,这意味着多个操作是真正同时发生的。
线程在GPU中起到什么作用?
GPU与CPU之间一个非常值得关注的差异点,GPU的延迟和带宽要求比CPU高得多,这意味着它需要大约40倍的线程来弥补这种延迟。
实际上,GPU拥有的线程数量比实际运算需要的多出五倍半,而其它类型的CPU,它们的线程数量可能只够覆盖1.2英寸范围内的操作,这就是GPU设计中最为关键的一点。
GPU拥有大量的线程,远超过它实际需要的数量,这是因为它被设计为“超量订阅”(oversubscription)。它旨在确保有大量线程在同时工作,这样即使某些线程在等待内存操作完成,仍然有其它线程可以继续执行。
GPU通常被称为“吞吐量机器”。GPU的设计者将所有的资源都投入到了增加线程数量而不是减少延迟上。相比之下,CPU则更侧重于减少延迟,因此它通常被称为“延迟机器”。
记住,GPU设计者通过增加线程数量来对抗延迟,而不是通过减少延迟来降低延迟。
另外,需要注意的是GPU是被超量订阅的。这意味着,当一些线程在等待读取数据时,其它线程已经完成了读取并准备执行。
GPU内存需要足够大
除了线程设计,内存架构也是 GPU 高效的关键。所有计算都围绕数据展开,而数据的存储和访问速度直接影响效率。
GPU为每个线程分配了大量的寄存器来存储实时数据,从而实现了非常低的延迟。这是因为与CPU相比,GPU中每个线程都需要处理更多的数据,因此它需要能够快速访问这些数据。
所以,GPU需要一种靠近其计算核心的快速内存,并且这种内存需要足够大,以便能够存储进行有用计算所需的所有数据。
不仅如此,当你发出一个加载操作(比如将某个指针的值加载到变量x中)时,硬件需要一个地方来暂存这个加载结果。而GPU所拥有的寄存器数量直接决定了它能够同时处理的内存操作数量。
GPU的主内存就是高带宽的HBM内存。如果我把GPU主内存的带宽看作一个单位,无论它有多快,都只能算作一。而L2缓存带宽则是它的五倍,L1缓存,也就是我即将提到的共享内存,更是快了13倍。
因此,随着带宽的增加,它更容易满足计算强度的需求,这无疑是一件好事。
GPU工作中是如何获取吞吐量的?
GPU 之所以在并行计算中高效,核心在于它的设计完全围绕 "吞吐量" 展开。
我们假设训练了一个AI来识别『互联网』上的猫。现在,我们有一张猫的图片。我会在这张图片上覆盖一个网格,这个网格将图片分割成许多工作块。
然后,我会独立地处理每个工作块。这些工作块是彼此独立的,它们在图片的不同部分工作,而且工作块的数量非常多。
因此,GPU会被这些工作块过度订阅。但记住,过度订阅是我们追求高效执行和最大内存使用的一种策略。
在每个工作块中,都有许多线程共同工作。这些线程可以共享数据并完成共同的任务。所有的线程都同时并行运行,这样GPU就能够实现高效的并行处理。
现在,已经构建了层次结构。在最高层,有总工作量,它通过网格被分解成工作块,这些工作块为GPU提供了所需的过度订阅。然后,在每个工作块中,有一些本地线程,它们一起协同工作。
通过这种方式,能够充分利用GPU的并行处理能力,实现高效的吞吐量。
如今,GPU 已从图形处理扩展到 AI、科学计算等众多领域。未来,随着带宽瓶颈的进一步突破,GPU 还将解锁更多可能性。
理解 GPU 的设计原理,不仅能帮我们更好地利用硬件,更能启发我们:面对问题时,换一种思路(比如从 "减少延迟" 到 "掩盖延迟"),或许能找到更高效的解决方案。




