GPU是协处理器,CPU才是主人,GPU并不是一个独立运行的计算平台,而需要与CPU协同工作,可以看成是CPU的协处理器,因此GPU并行计算,是指CPU+GPU的异构计算架构。在异构计算架构中,GPU与CPU一般通过PCI-E总线连接在一起来协同工作,CPU所在的位置称为主机端(host),而GPU所在的位置称为设备端(device)。
早期的CPU都是针对整数运算的,也有的CPU有少量的浮点运算处理,处理低像素还凑合。随着像素数越来越高,需要大量的浮点运算处理器,GPU就出现了。所以标准意义的GPU其性能参数为FLOPS,Floating-pointOperations Per Second。最常用来测量FLOPS的基准程式(benchmark)之一,就是Linpack。一个GFLOPS(gigaFLOPS)等于每秒十亿(=10^9)次的浮点运算,一个TFLOPS(teraFLOPS)等于每秒一万亿(=10^12)次的浮点运算。而CPU的性能单位一般是DMIPS,DhrystoneMillion Instructions executed Per Second,每秒百万次整数运算指令执行数。AI的性能单位则是TOPS,TeraOperations Per Second,1TOPS代表处理器每秒钟可进行一万亿次(10^12)操作,不区分整数还是浮点,也不是指令而是operation。通常只有CPU才能做任务调度,是host,AI处理器和GPU都是需要CPU配合,是协处理器device。
典型GPU架构图
图片来源:互联网
典型GPU架构,从2010年的Fermi开始NVIDIA使用类似的原理架构,使用一个Giga Thread Engine来管理所有正在进行的工作,GPU被划分成多个GPCs (Graphics Processing Cluster),每个GPC拥有多个SM(SMX、SMM,Streaming Multiprocessor流多处理器)和一个光栅化引擎(Raster Engine),它们其中有很多的连接,最显著的是Crossbar,它可以连接GPCs和其它功能性模块(例如ROP或其他子系统)。 GPU制造厂商经常宣传说他们拥有“240”个“core”,但他们所有的core其实是指 “ALUs/FPUs”,与CPU中提到的core还是有区别的,一般NVIDIA或ATI称core为SP (streamingprocessor)或者SC (shadercore)或者TP (Threadcore),都是一个东西,一个SM中包含多个SP,但这些SP并不是完全独立的执行单元,每个SP虽然有单独的register file,独立的指令指针,但SP并没有完整的独立front-end比如取指和指令派发,因此SP更像是CPU中执行部分。
Fermi的SM处理器内部框架
图片来源:互联网
Fermi是英伟达2010年发布的GPU架构,从此英伟达的GPU架构开始都以科学家的名字做代号,其前端结构差别不大。
Warp Scheduler管理一组32个线程作为Warp(线程束,可以想象是纺纱机器把多条线纺织在一起)并将要执行的指令移交给Dispatch(派遣,发送)Units。英伟达升级GPU一般都是升级Core内核,别的都与Fermi高度一致。Fermi有32768个32-bit寄存器,Fermi有512个SM,也就是每个SM(每个线程束)分到64个寄存器,每个线程两个。
Fermi的Core包含一个整数运算单元,一个浮点运算单元。每个SM还有SFU (SpecialFunction Units,特殊数学运算单元),它们用于进行三角函数和指对数等特殊运算。每一个核都相当于GPU中的ALU,一个CPU内核最多有8个ALU,GPU则可以轻易达到数千个。但GPU通常只能应对一种算法,CPU中的可以胜任非常多算法,ALU像一个教授,从加减乘除到微积分无所不能,GPU像几千个小学生的集合,通常只做乘法。FP后来在GPGPU时代变为FMA或FFMA (Fusedmultiply-and-accumulate),基本上所有所谓人工智能算法拆解到最底层都是乘积累加计算,也特指乘积累加指令集。
英伟达用于AI的采用Ampere架构的A100GPU物理架构
图片来源:互联网
2019年推出的Ampere架构与Fermi架构整体一致。
英伟达安培架构中的SM内部框架
图片来源:互联网
英伟达安培架构中的SM内部框架中,将ALU分得更细,INT32即整数32位,FP32为浮点32位,FP64为浮点64位。专门针对矩阵运算则有Tensor核。
实际CPU与GPU最大的区别是带宽,CPU像法拉利,跑的很快,但要是拉货,就不如重卡。GPU像重卡,跑的不快,但一次拉货多。有些货可以全部打包装车运输,如这些货都来自一个地方,大小相同,需要运输到一个地方,这就是计算密集型任务。有些货不行,比如这些货要去不同地方,体积大小不一,不能多个打包,只能多次运输,这就是控制密集型任务。CPU在缓存、分支预测、乱序执行方面花了很多精力,用大量寄存器实现这些功能,保证了高速度,频率一般都远高于GPU,每次速度很快,但大量寄存器占用大量空间,考虑到成本以及半导体的基本定律(单颗die面积不超过800平方毫米,否则良率会急速下降),CPU的核心数非常有限,每次能带的货很少。GPU相反,不考虑分支预测与乱序执行,用最快的寄存器代替缓存,结构简单,晶体管数量少,可以轻易做到几千核心,每次能带的货很多,但速度不快。
深度学习或者说人工智能都是计算密集型任务,数据一般占用大块连续的内存空间,GPU可以提供最佳的内存带宽,并且线程并行带来的延迟几乎不会造成影响。
除此之外,GPU的并行计算架构,通常来说与执行单元(指的是CPU的核心或者GPU的流处理器)的距离越近,则访问速度越快,其中寄存器和L1缓存与CPU距离最近。GPU的优势在于:GPU为每个处理单元(流处理器或者SM)均配备了一些寄存器,而GPU成百上千个处理单元就使得寄存器总的数量非常多(为CPU的30倍,高达14MB)且速度达到80TB/s。
相比之下,CPU的寄存器大小通常为64-128KB,运行速度为10-20TB/s。当然以上的比较在数值上会不太精确,并且CPU寄存器和GPU寄存器并不相同。两者的大小和速度的差异是主要关键点。最后一点GPU的优势在于:大量且快速的寄存器和L1缓存的易于编程性,使得GPU非常适合用于深度学习。
英伟达Volta V100 GPU的存储架构图
图片来源:互联网
上图可以看出每个处理block都有高达64KiB的寄存器,寄存器是速度最快的,只要一个周期,缓存或者说SRAM还是要通过总线访问的,速度远远不及寄存器。
早期GPU
早期的GPU和目前的GPGPU差别还是不小的,早期的GPU主要是图形生成与渲染,第一步就是画三角形,三角形光栅器一次要处理一堆东西:跟踪三角形的形状,插值出坐标u和v (对于透视矫正映射,是u/z,v/z和1/z),执行Z缓冲测试(对于透视矫正映射,可以用1/z缓冲替代),然后处理实际的纹理(还有着色)。这些都是浮点运算,早期的CPU都是针对整数运算的,也有的CPU有少量的浮点运算处理,处理低像素还凑合,随着像素数越来越高,GPU就出现了。
GPU的图形处理逻辑管线
图片来源:互联网
通常把图形逻辑管线叫渲染管线,渲染流水线是在显存中开启的。CPU将网格、材质、贴图、着色器等注入显存后,GPU开始渲染流水线。渲染流水线分为几何阶段和光栅化阶段(也被称为像素阶段),并最终将运算结果送到显示器的缓冲区中。几何阶段分为顶点着色器->曲面细分着色器(DirectX11和OpenGL4.x以上可编程)->几何着色器->裁剪->屏幕映射五个步骤。
光栅(ROP)化阶段分为三角形设置->三角形遍历->片元着色器->逐片元操作四个步骤。渲染流水线重点强调的和CPU的指令流水线并不同。它实际不是流水线,不同阶段运行的是同一次Draw Call(Draw Call指令是由CPU发出的),但一次Draw Call中先提交的数据可以不等待它之后的数据就进入下一个流水线阶段。
顶点渲染单元(也叫顶点着色或顶点着色引擎),主要负责描绘图形,也就是建立模型。一个就是像素渲染管线(也叫像素渲染管道),主要负责把顶点绘出的图形填上颜色。然后再加上纹理贴图单元贴上纹理,一个精美的图形就出来了。在微软的DIRECTX10出来后,顶点渲染和像素渲染将淡出我们的视线,因为它将采用统一架构。也就是一个核心中是由一组专门的通道既负责顶点渲染又负责像素渲染的。不过道理还是一样的。
这就引出了GPU的两个关键参数像素填充率和纹理填充率。当然是越高越好,这样显示图像帧率高且更细腻,分辨率更高。 像素填充率=显卡的显示核心频率乘以像素渲染管线(即光栅单元)数量。单位是GB Pixel/s
纹理填充率=核心频率乘以像素渲染管线数量再乘以纹理贴图单元数量。单位是GB texture/s
GPU的CPU开销即Draw Call驱动开销
长久以来人们都觉得ARM的GPU即MALI效果不如高通的Adreno,实际单论像素填充率和纹理填充率,MALI更强,但ARM的缺点就是Draw Call驱动开销太高。调用一次图像编程接口(图形API),以命令GPU进行渲染的过程就称为一次Draw Call,图像编程接口(图形API),是对GPU硬件的抽象,其地位类似C语言,属于GPU编程的中低层。几乎所有GPU都既可以和OpenGL合作也可以和DirectX合作。OpenGL是纯粹的图形API;DirectX是多种API的集合体,其中DirectX包含图形API——Direct3D和Direct2D。DirectX支持Windows和Xbox;OpenGL支持Windows、MacOS、Linux等更多平台,在Android、iOS上允许使用OpenGL的简化版本OpenGL ES。OpenGL相对来说易上手,门槛低;DirectX难上手,门槛高。OpenGL渲染效率相对低,特性少;DirectX相对效率高,特性多。如DirectX12提供了底层API,允许用户一定程度上绕过显卡驱动之间操纵底层硬件。
对于图像生成的应用,第一步,CPU把一个网格的顶点数据从硬盘中加载到内存中(存在这一步的原因是大规模3D渲染中内存可能不足)。
第二步,CPU对这个网格设置渲染状态(每个网格不等于每个模型/图片,因为存在批处理)。所谓渲染状态包括纹理贴图、材质属性和被编译为二进制文件的着色器。随着渲染状态一起被传递到GPU的还有光照和摄像机相关的信息。图形API可以更深层次的定义渲染状态需要的数据。
第三步,CPU将网格顶点数据与渲染状态打包,将数据包按照指定格式交给DMA,由DMA将数据包传入显卡。DMA (Direct Memory Access,内存直接访问)。 指令到达GPU驱动程序后,驱动会首先检查指令的合法性,如果指令非法,驱动会通过DMA向CPU发送错误信息。如果指令合法,驱动通过DMA确认Draw Call接收,然后将指令放入GPU缓冲。 一段时间后当显卡中存在空闲流水线,或者CPU显式发送flush命令后,驱动程序把缓冲区中的一份指令发送给GPU,GPU通过主机接口接受命令,并开始处理命令。
GPU将所有顶点存入顶点缓冲区(Vertex Buffer),GPU中的“图元分配器(Primitive Distributer)”开始通过顶点生成三角形,并将他们分成批次(batch),发送给一个或多个GPCs,如果显卡不存在GPCs,则直接分发给SMs。SM获得数据后,束管理器安排多边形引擎将三角形数据提取出来存入SM的L1缓存,随后开始顶点着色器阶段。
GPU会依次处理缓存中的每一个网格,网格的处理顺序与CPU的提交顺序有关。正因如此,CPU总是最后提交透明物体。等一帧中的所有Draw Call处理完毕后,显示器才会将图像打印在屏幕上。
Draw Call的性能瓶颈是CPU而非GPU。CPU每进行一次Draw Call,都要调用一次DMA将数据输入显存。在每次调用时,对显存的映射寻址、DMA控制块的注入、等待DMA响应等系统消耗都会浪费时间周期,多次进行Draw Call就会有多次消耗。同时,DMA擅长一次传输大量数据,而不擅长多次传输少量数据。这使得降低Draw Call对于优化显示性能很有必要。
ARM MALI系列GPU
ARM的GPU设计项目最早从上个世纪90年代末期开始,由挪威科技大学开始开展,随后在2001年,这个项目的Mali小组成员从研究中脱离出来,成立了一个名为Falanx Microsystems的公司。Falanx公司的人员刚开始瞄准的是PC图形市场,但当时已是后3DFX时代,群雄并起,包括S3、Rendition、Revolution以及Imagination等公司最后都失败了,最终Falanx无法筹集到足够的资金,被迫放弃了PC图形市场。
在那个“紧迫期”,由于资金有限和PC图形硬件极高的研发成本,Falanx最终决定转向移动SoC GPU设计。因为移动GPU设计更简单且较容易成功。Falanx的产品Mali GPU也迎来了他们的第一个客户—美国Zoran公司,使用了Mali-55作为他们Approach 5C SoC芯片的GPU,这颗芯片还被用在LG's Viewty这样广受欢迎的手机产品中。即使如此,Falanx还不满足,最终在2006年迎来了他们的“大鱼”。鉴于SoC市场持续增长以及将带来的移动计算大潮,ARM公司终于决定买下Falanx,组建自己的GPU事业部,并联合ARM的CPU一起推动整个产业的增长。ARM作为一个处于上升期、资金充裕的公司,完全有能力给Falanx充足的资金和研发资源来实现梦想。
ARM第一代微架构Utgard(北欧神话人物:乌特加德)。这一代架构出来的比较早,主要是图形加速IP。可以追溯到2007年的Mali-200。不过最让人惊讶的是Mali-4xx系列,现在很多电视芯片都还在用这个IP。比如小米的智能电视,还有很多是Mali-4xx系列的。第二代微架构Midgard(北欧神话人物:米德加德)。Midgard这一代GPU开始属于同一着色器的架构,也就是上面说的vertex shader和fragment shader已经统一在一起了,相当于同一个shader计算单元可以处理多种着色器。当然也开始支持通用计算。特别是对OpenCL的支持。第三代微架构Bifrost(北欧神话中连接天宫和大地的:彩虹桥)。第四代微架构Valhall(北欧神话中的瓦尔哈拉神殿,是战死的勇士死后进入奥丁神的神殿)是2019年第二季度推出来的。该系列是基于超标量实现的。
常见高通与ARM MALI GPU参数对比
资料来源:互联网
ARM MALI-G710
目前ARM最新的GPU为G710,ARM的架构变动比较频繁。G710可能是ARM最成功的GPU架构,采用4纳米制造工艺。 与英伟达桌面级GPU比,ARM的架构差异比较大,ARM采用大核设计,一般写为MALI G710 MPX或MCX,X代表核心数,MALI的核心就是渲染核即Shader Core,可以近似于英伟达的SM流多处理器。渲染核里有执行引擎,可以算是CPU领域的ALU。
MALIG710渲染核
图片来源:互联网
早期ARM是SIMD设计,近期变为GPU常用的SIMT。G710的执行引擎比G77翻倍,有两个执行引擎,每个执行引擎包含两个簇,执行16位宽的线程,等于是64个ALU,G710支持7-16核设计,也就是最高1024个ALU。
MALIG710执行引擎
图片来源:互联网
G710的执行核,前端没有透露具体信息,应该与G77一样,是64个warp或1024个线程。每个处理单元都有三个ALU:FMA(混合的乘积累加计算)和CVT(Convert)单元是16-wide,而SFU(特殊功能单元)是4-wide。每个FMA每周期可以做16次运算,运算数据精度为FP32,换成FP16就是32次,8位整数INT8就是64次,像英伟达的桌面级GPU,FP16和FP32是分开计算的,也就是说可以同时计算,但移动级的MALI不需要这样设计。Convert单元处理基本整数操作和自然类型转换操作,并充当分支端口。
MALI-G77的渲染核
图片来源:互联网
图片来源:互联网
ARM MALI GPUCSF
G710最大的变化,添加了CSF。G710首次将Mali的Job Manager换成了所谓的Command Stream Frontend。这个CSF负责处理调度和draw call,CSF这个模块由CPU、HW和固件构成。ARM表示,HW本身是全新设计的;而固件层的引入,能够针对一些比较复杂的图形负载提供更具弹性的性能,而且能够减少驱动开销、提升效率;对诸如Vulkan等API提供更简捷的支持等。固件处理来自host的请求和通知,负责硬件资源调度,减少诸如protected mode进出的开销,还能通过指令模拟来提供硬件原本不具备的特性。