前言
(这篇文章是我本人很早之前发在百度贴吧的,所以完全不存在侵权问题。最近感觉需要存档一下,万一哪天贴吧倒掉了呢XD)
Intel是全球GPU出货量最大的科技公司, 这点是毋庸置疑的.
I家在20世纪末21世纪初就在研究板载GPU, 最为人所知的就是GMA (Graphics Media Accelerator), 但它与CPU package还是完全分离的. 从2010年的Westmere (就是酷睿i系列的源头) 开始, I家将GPU真正集成在了CPU package的内部, 称为HD Graphics, 中文称为"核芯显卡", 一直到现在.
当然, 现在HD Graphics的分级更加复杂了, 也出现了规格更高的Iris系列, 不过我们还是可以从最常见的产品入手, 看一下I家的核显到底是怎样设计的.
至于AMD的Raven Ridge, 就是锐龙APU, 它的Vega核显架构更有意思一些, 与独显高度趋同, 但这篇就只讲HD Graphics.
入门介绍
下图是一块四核心Intel处理器的die shot框图, 带有Gen9核显, 在左侧红框中.
![](https://i-blog.csdnimg.cn/blog_migrate/ff9cf8cdf70bb088f51fdc2c0d7f877c.png)
I家的核显也是分代的 (毕竟历经了很多年的迭代). 从6代酷睿Skylake开始, 接下来的Kaby Lake和Coffee Lake均采用Gen9微架构. Broadwell采用Gen8, 而更久远的Haswell则采用Gen7.5.
因此, 图中的这个CPU型号可以想象成桌面端的i5-6500/i7-7700K/i3-8100, 或者移动端的i7-6700HQ/i5-7300HQ/i5-8300H/i7-8550U, 不一而足. 而Gen9核显就是目前最常见的(U)HD Graphics 620以及(U)HD Graphics 630.
从图中还可以看出, 所有CPU核心共享最后一级缓存 (Last Level Cache, LLC), 也就是三级缓存. 最右侧的"系统Agent"模块则负责一切控制类工作, 如内存控制, 显示控制, I/O控制等.
I家核显占用die的面积相当大, 一般都在40%, 更强的Iris核显则可以占到70%.
片上环状互联、LLC和eDRAM
只凭die shot是无法看出各个组件之间是如何协同工作的, 因此还有下面的流程图.
![](https://i-blog.csdnimg.cn/blog_migrate/ee6c9f35c4912d6d78602140b33951c5.png)
除去左侧的核显之外, 解释三个要点.
- 片上环状互联 (SOC Ring Interconnect)
这个设计是从Sandy Bridge时代引入的, 其本质是将CPU核心、LLC分片、Agent模块、核显等组件联系在一起的环形拓扑总线. 它可以双向传输, 宽度32字节, 具有自己的时钟域, 带宽非常高. 这种设计有利于芯片的模块化扩展, 同时还可以加强存储资源的共享. - 最后一级缓存 (LLC)
其实就是我们常说的三级缓存 (L3 Cache), 它是一个全分布式的存储单元, 位于CPU核心的外部. 相反地, 一级、二级缓存都在核心内部.
每个CPU核心都可以得到一个相对固定的LLC分片, 核显也可以利用它. 它是系统访问内存前的最后一道关卡, 由于访问缓存的速度要比访问内存快得太多, 因此较大的LLC有利于减少延迟. 目前, 双核心的Intel处理器一般配备3~4M LLC, 四核心的是6~8M, 六核心的则是9~12M. - 嵌入式DRAM (eDRAM)
从上图右下角可以看出, Agent模块中有一个可选的eDRAM控制器. 所谓eDRAM, 是位于整个SoC之外的一块快速记忆体, 一般是64M或128M大, 也有自己的时钟域. 它有独立的总线用于读写, 速度也远比内存快. 它的作用与LLC基本相同, 可以近似认为是L4缓存或者核显的内嵌显存, 由CPU核心和核显共用, 对提高整体性能很有用. 目前, 拥有Iris核显的型号才可能会配备eDRAM, Skull Canyon NUC中的i7-6770HQ就是个很好的例子.
执行单元
做了很多铺垫之后, 可以来看核显本身了. 就像NVidia GPU有CUDA核心, AMD GPU有流处理器一样, Intel在狭小的核显空间内也是有设计基本运算组件的, 名为执行单元 (Execution Unit, EU). EU图示如下.
![](https://i-blog.csdnimg.cn/blog_migrate/11a4ead037696679bdc47e61ad92f72f.png)
从本质上来讲, EU是一个细粒度多线程的、符合单指令流多数据流(SIMD)规范的处理模型. 图中左侧绿色的部分是存储单元, 它右侧蓝色的则是功能单元.
仔细读图, 可以得到如下信息:
- 每个EU的存储单元中有7条线程, 或者叫做通道;
- 每条线程(通道)上有128个通用寄存器;
- 每个寄存器可以存储8元素SIMD向量;
- 每个SIMD向量的元素长为32bit.
这样就可以理解图中的"28KB GRF"是怎么来的了, 简单相乘就行了. 所谓GRF, 就是通用寄存器文件(General Purpose Register File), 当然它并非一个真正的文件. 此外, 每条线程还有一个用于保存其状态的特殊寄存器组, 它们叫做架构寄存器文件(Architecture Register File, ARF).
EU经过取指(Instruction Fetch) 阶段之后, 通过译码, 与GRF中存储的数据配合, 进行功能性的操作. 在每个时钟周期, EU最多可以并发执行4条指令, 这4条指令必须来自于四条完全不同的线程. 这些指令交由后面的线程枚举器(Thread Arbiter), 再由枚举器分发给后面的功能单元之一进行处理.
由上图可以看到, 功能单元也是有4个, 分别是:发送单元(Send)、分支单元(Branch)、两个SIMD浮点运算单元(SIMD FPU).
SIMD FPU是EU中起图形运算功能的核心部件. 虽然它们的名字叫做"浮点运算单元", 但它们既可以进行浮点运算, 也可以进行整型运算. 在一个周期中, 每个FPU可以以SIMD的方式执行4个32bit运算, 或8个16bit运算. 在Gen9之前的核显架构中, 都是不原生支持16bit运算的.
在FPU中, 一次浮点运算是由乘加操作组成. 因此, 对于32位浮点运算而言, 一个EU每周期可以执行(add + mul) * SIMD-4 * 2 FPU = 16次操作.
另外, 具有分支效果的指令, 如跳转、条件、循环指令, 则被枚举器转给分支单元处理. 而那些需要较长延迟时间的操作(如访问内存), 则被转给发送单元, 再由它与外部组件联系进行下一步操作.
子分片
GPU设计大多奉行模块化、可扩展的原则, 也就是说, 把一定数量的基本计算单元形成团簇, 然后再由数量不等的团簇加上某些控制单元来构成整个GPU的运算组件. NVidia和AMD都是这样做的.
N卡的计算单元团簇叫做流式多处理器 (Stream Multiprocessor, SM), 由一定量的CUDA核心组成. 在Kepler架构中, 192个CUDA核心组成一个SM, 而在Maxwell以及除GP100之外的Pascal架构中, 128个CUDA核心组成一个SM. 例如, GT750M (GK107) 有2个SM, GTX965M (GM204) 有8个SM, GTX1070 (GP104) 有15个SM.
A卡的计算单元团簇就叫做计算单元 (Compute Unit, CU), 在GCN架构中提出, 每个CU包含64个流处理器. 例如, R9 M270X (Venus XT) 有10个CU, RX560完全版 (Baffin) 有16个CU, 而R5 2500U/R3 2200G自带的Vega 8核显有8个CU.
Intel从Haswell时代的Gen7.5核显开始, 也采用类似的设计思路. 这是Intel核显性能开始跃进的关键点所在. 它采用两级EU团簇结构设计, 较低一级叫做子分片 (subslice), 较高一级叫做分片(slice).
下图示出子分片的架构, 一个子分片由8个EU组成.
![](https://i-blog.csdnimg.cn/blog_migrate/635d06f20e1a58deb3e579f175fe1de4.png)
图中仍然有三个需要解释的点:
- 本地线程分发器 (Local Thread Dispatcher)
顾名思义, 它用于向每个EU中的每个线程来分配任务. 所有的指令先进入它内部的指令缓存, 然后再由它来将这些指令分发给有空闲的EU. - 采样器 (Sampler)
采样器是一个只读的访存单元, 用于从其外部的存储单元中获取纹理或图像数据, 并进行采样. 除了采样之外, 它还可以完成图像的坐标转换、过滤等. 采样器内有专用的两级缓存. - 数据口 (Data Port)
它是专门管理数据存取的单元, 负责与外部存储单元进行通用的数据交换. 另外, 它还可以进行SIMD操作的聚合, 也就是将多个长度相同, 并且偏移地址落在同一个地址段内的分散的SIMD操作放在一起处理, 这样可以使带宽最大化, 提高效率.
其实到了这里, 就可以看出核显 (以及所有能够运算的SOC) 在架构上的精细性. 每个单元都需要具有指令和数据的处理能力, 并且需要保持与其高层或低层组件的信息交换.
分片
将子分片集合在一起, 然后再加上一些必要的组件, 就变成分片了. 常见的(U)HD Graphics 620/630都是由3个子分片组成一个分片, 也就是说包含24个EU.
![](https://i-blog.csdnimg.cn/blog_migrate/d30b3b1abec2ea0b4c74eedd16e23803.png)
由上图也可以看出, 各主要数据总线的读写速率都是64B/周期, 这个值十分重要. 为什么一定要是64B呢?
回忆一下, EU中的一个通用寄存器可以存储32B的数据, 也就是SIMD-8x32bit. 但在实际的运算过程中, 有很多指令是SIMD-16的, 这样的话就需要将一对通用寄存器视为一个SIMD-16寄存器, 数据量就变成了64B.
这样, 每个子分片的采样器和数据口在从分片的缓存 (L3数据缓存) 中读写数据时, 宽度是64B. L3缓存中存储的数据, 每条也是64B. L3缓存到整个SoC的LLC缓存的数据总线的宽度, 当然也是64B了. 这种统一性有利于各存储单元和运算单元间的协同工作.
分片的L3数据缓存 (L3 Data Cache) 是相对于各子分片的采样器缓存而言的, 是高度bank化的存储结构, 在Gen9架构中, 它的大小是768KB.
每个子分片的数据口都要先从L3缓存中读取它们需要的数据, 而采样器则先访问自身的L1、L2缓存, 若找不到数据才要从L3缓存中读取. 一旦出现缓存未命中的情况, 分片L3缓存就要从LLC甚至系统内存中读取数据, 再返回给子分片. 由于带宽很大, 因此总体来讲效率并不低.
核显的全貌
再把分片组合起来, 加上必要的组件, 就形成了核芯显卡架构的全貌, 如下图所示.
![](https://i-blog.csdnimg.cn/blog_migrate/b627732548f530f513235a3a333ac092.png)
Gen9核显可以由1~3个分片组成. 图中的是有2个分片48个EU, 对应的型号为Iris Plus 640/650/655. 具有3个分片的顶级型号就是Iris Pro 580.
当然, 在这些分片的上面, 还是多了两个控制组件的, 一是命令流 (Command Streamer), 二是全局线程分发器 (Global Thread Dispatcher).
命令流主要负责从核显驱动程序栈来接收底层的命令, 并且将它们进行高效的组合和解释. 至于全局线程分发器, 它则是负责整个核显模块的负载均衡, 统一管理所有子分片的本地线程调度器, 并与它们协同工作.
上图中最下方的图形技术接口 (Graphics Technology Interface, GTI), 则是整个核显模块的大门, 所有与SoC其余部分的交互都要穿过它, 就像细胞膜之于细胞一样. 另外, GTI还负责一些原子性的LLC读写操作, 以及最重要的电源管理功能.
按照Intel的说明, 除去分片之后的其他组件, 也就是命令流、全局线程分发器和GTI, 它们所处的区域叫做"未分片" (unslice)区域. 未分片区域处于一个特殊的、有很大自主性的clock domain中, 通过它可以调节整个核显的性能表现.
与核显相关的存储
最后, 来看一下整个核显模块与SoC的存储结构.
![](https://i-blog.csdnimg.cn/blog_migrate/ca561a177536da2d66f0b7633b90ca1d.png)
所有人都知道, 核显的显存除了可能有的eDRAM之外, 全部来自共享系统内存. I家认为这种设计方式可以简化系统的复杂度, 降低能耗, 并且不需要添加额外的数据缓冲区.
Gen9核显可利用的最大系统内存量是64GB, 但相信没有人会用到这个极端值. 由于核显的GTI与内存之间只隔了一个LLC, 因此我们可以认为系统内存的位宽和频率就是核显显存的位宽和频率.
在Haswell和Broadwell的时代, 最大可以支持到双通道DDR3 1866MHz内存 (等效频率). 由于每条内存通道的位宽为64bit (8B), 因此在之前常见的DDR3(L) 1600MHz的条件下, 内存和核显显存的带宽就是25.6GB/s. 而Skylake之后, 对DDR4的支持全面铺开, 在DDR4 2133/2667MHz的条件下, 带宽可以分别达到34.1GB/s与41.8GB/s, 提升相当大. 因此在装机时, 一般都会建议核显用户配备双通道内存, Ryzen APU更是要配高频内存.
由图中也可以看到各时钟域对数据交换速率的影响. CPU核心与LLC交换数据时, 其频率由环状互联的时钟决定; GTI与LLC交换数据时, 其频率由核显核心频率决定; eDRAM和系统内存与LLC交换数据时, 其频率由eDRAM或内存决定.