文章目录
- 4.1 引言
- 4.2 向量体系结构
- 4.2.1 VMIPS
- 4.2.2 向量处理器如何工作:一个示例
- 4.2.3向量执行时间
- 4.2.7内存组:为向量載入/存储单元提供带宽
- 4.4 图形处理器
- 4.4.1 GPU编程
- 4.4.2 NVIDIA GPU计算结构
- 4.4.3 NVIDA GPU指令集体系结构
- 4.4.4 GPU中的条件分支
- 4.4.5 NVIDA GPU存储器结构
- 图4-12是NVIDIA GPU的存储器结构。
- 每个多线程SIMD处理器本地的片上存储器称为本地存储器。
- 整个GPU和所有线程块共享的片外DRAM称为GPU存储器。
- 主机的系统处理器可读或写GPU存储器。
- GPU不依赖大型缓存来包含应用程序的整个工作集,
- 尽管隐藏存储器延迟是一种优选方法,但注意,最新的GPU和向量处理器都已经添加缓存。
- 为提高存储器带宽、降低开销,
- 参考链接
4.1 引言
- SMID体系结构(1章介绍)
- 人们总问一问题:有多少应用程序拥有大量的DLP
- 50年后,答案不只包含科学运算中的矩阵计算,还包括媒体图像和声音处理。
- 单条指令可启动许多数据运算,so SIMD在能耗效率方面要比MIMD更高效
- MIMD每进行一次数据运算都需要提取和执行一条指令。
- 这两个答案使SIMD对于个人移动设备极吸引力。
- SIMD与MIMD比的最大优势:
- 由于数据操作是并行的,所以程序员可以采用顺序思维方式但却能获得并行加速比。
- 本章介SIMD的3变体:向量体系结构、多媒体SIMD指令集扩展和GPU
- 第一种变体要比其他两早30年
- 它实际上就是以流水线形式来执行许多数据操作。
- 与其他变体比,向量体系结构更易理解和编译,但过去对于微处理器来说太贵,这看法直到最近才变。
- 此体系结构的成本,一部分用在晶体管上,另一部分用于提供足够的DRAM带宽,
- 二变借用SIMD名称来表示基本同时进行的并行数据操作,
- 在今天支持多媒体应用程序的大多数指令集体系结构中都可以找到这种变体。
- x86的SIMD指令扩展是在1996年以MMK开始的,接下来的10年间出现了几个SSE(流式SIMD
扩展)版本,到今天的AVX(高级向量扩展)。
- 为使x86计算机达到最高计算速度,
- 3变来自GPU社区
- 它的潜在性能高于当今多核计算机
- GPU的一些特征与向量体系同,
- 但有自己的一些独特特征,部分原因在于它们的发展生态系统。
- 在GPU的发展环境中,除了GPU及其图形存储器之外,还有系统处理器和系统存储器。
- 为辦识这些差别,GPU社区将这种体系结枃称为异类。
- 对大量DLP的问题,
- 这三种SIMD变体都有一个共同的好处:
- 为对比SDMD与MIMD的重要性,
- 图4-1
- x86中MIMD的核数与SIMD模式中每个时钟周期的32位及64位运算数随时间的变化
- 对x86,
- 预期每芯片上每两年加两核,SIMD的宽度四年翻一番
- 给定这些假设,
- 在接下来10年,由SIMD并行获得的潜在加速比为MIMD并行的两倍。
- MIMD并行最近受到的关注要多得多,但理解SIMD并行与理解MIMD并行ー样重要。
- 对同时具有数据级并行和线程级并行的应用程序,2020年的潜在加速比将比今天的加速比高一个数量级
- 让架构师理解向量为什么比多媒体SIMD更具一般
- 向量与GPU体系结构之间的相似与区别。
- 由于向量体系结构是多媒体SIMD指令的超集(包括一个更好的编译模型),
- 所以先从向量体系结构人手,
- 下节介向量体系结构,
4.2 向量体系结构
- 向量体系结构获得在存储器中散布的数据元素集,
- 将它们放在一些大型的顺序奇存器堆中
- 对这些寄存器堆中的数据进行操作,然后将结果放回存储器中。
- 一条指令对数据向量执行操作,从而会对独立数据元素进行数十个“寄存器一寄存器”操作。
- 这些大型寄存器堆相当于由编译器控制的缓冲区,
- 用于隐藏存储器延迟、充分利用存储器带宽。
- 向量载入和存储是尝试流水化的,
- 所以这个程序仅在每个向量载人或存储操作中付出较长的存储器延迟时间,
- 而不需在每个元素时耗费这一时间,
- 这一延迟时间分散在如64个元素
- 向量程序尽力使存储器保持繁忙
4.2.1 VMIPS
- 先看一个向量处理器,由图4-2的主要组件组成。
- 这个处理器大体以Cay-1为基础,是本节讨论的基础。
- 将这种指令集体系结构称为VMIPS;它的标量部分为MIPS,它的向量部分是MIPS的逻辑向量扩展。
- 这一小节的其他部分研究 VMIPS的基本体系结构与其他处理器有什么关系。
- VMIIPS指令集体系结构的主要组件如下所示。
- 向量寄存器
- 每个向量寄存器都是一个固定长度的寄存器组,保存一个向量。
- VMIPS有8个,每个保留64个元素,元素的宽度为64。
- 向量寄存器堆霑要提供足够的端口,向所有向量功能单元馈送数据。
- 这些端口允许将向量操作高度重叠,发送到不同向量寄存器。
- 利用一对交又交换器将读写端口(至少共有16个读取端口和8个写入端口)连接到功能单元的输入或输出。
- 向量功能单元
- 每个单元都完全实现流水化,可在每个时钟周期开始一个新操作。
- 需有个控制单元来检测冒险,
- 既包括功能单元的结构性冒险,又包括关于寄存器访问的数据冒险。
- 4-2显示有5个功能单元。
- 为简,仅关注浮点功能单元。
- 向量载入/存储单元
- 这个向量存储器单元从存储器中载入向量或者将向量存储到存储器中。
- VMIPS向量载入与存储操作是完全流水化的,所以在初始延迟之后,可以在向量寄存器与存储器之间以每个时钟周期一个字的带宽移动字。
- 这个单元通常还会处理标量载入和存储。
- 标量寄存器集合
- 供数据,作为向量功能单元的输入,计算传送给向量载人存储单元的地址。
- MIPS的32个通用和32个浮点
- 在从标量寄存器堆读取标量值时,向量功能单元的一个输入会闩锁住这些值。
- VMPS的基本结构。
- 拥有类似于MIPS的标量体系结构。
- 有8个64元素向量寄存器,所有功能单元都是向量功能单元。
- 这一章为算术和存储器访问定义了特殊的向量指令。
- 图中显示用于逻辑运算与整数运算的向量单元,所以VMPS看起来像是一种通常包含此类单元的标准向量处理器;
- 这些向量与标量寄存器有大量读写端口,允许同时进行多个向量运算。
- 一组交叉交换器(粗灰线)将这些端口连接到向量功能单元的输人和输出
- 表4-1列出VMIPS向量指令。
- 向量运算的名字与标量MIPS指令相同,但后面加“vv"。
- ADDVV.D就是两个双精度向量的加法。
- 向量指令的输入
- 一对向量寄存器(AW.D),
- 一个向量和一个标量,附加“VS来标识(ADDVS.D)。
- 后种情况下,所有操作使用标量寄存器的相同值来作为一个输人:将向量寄存器中的每个元素都加上标量寄存器的内容。
- 向量功能单元在发射时获得标量值的一个副本。
- 多数向量运算有一个向量目标寄存器,一些会产生标量值,这个值将存储在标寄中。
- LV和SV:向量载入和向量存储,载入或存储整个双精度数据向量。
- 载人或存储的向寄,MIPS通寄,是该向量在存储器中的起始地址。
- 还需要两个通用寄存器向量长度寄存器和向量遮罩寄存器。
- 功率瓶颈使架构师看重具有以下特点的体系结构:能够提供高性能,又不要高度乱序超标量处理器的能耗与设计复杂度。
- 向量指令天生就与这一趋势吻合,架构师可用它们来提高简单循序标量处理器的性能,又不会显著增大能耗要求和设计复杂度
- 实践中,开发人员可采用向量指令的方式来表达许多程序,
- 数据级并行可以高效地在复杂乱序设计中运行, Kozyrakis和 Paterson[2002]证明了这点。
- 用向量指令,系统可采用许多方式对向量数据元素进行运算,包括对许多元素同时操作。
- 因为有了这种灵活性,向量设计可以采用慢而宽的执行单元,以较低功率获得高性能。
- 向量指令集中各个元素是相互独立,不要成本高昂的相关性检查就能调整功能单元,
- 向量本身就可容纳不同大小的数据。
- 如果一个向量寄存器可以容纳64个64位元素,同样可容纳128个32位、256个16位,甚至512个8位。
- 向量体系结构既能用于多媒体应用,又能用于科学应用,就是因为具备这种硬件多样性
4.2.2 向量处理器如何工作:一个示例
- 查看VMIPS的向量循环,可更好地理解向量处理器。
- 看一个典型的向量问题SAXPY或 DAXPY循
环,它们构成了 Linpack基准测试的内层循环。
- SAXPY(single-- precision a x X plus Y);
- DAXPYdouble precision a xX plus Y)。
- Linpack是一组线性代数例程, Linpack基准测试包括执行高斯消去法的例程
- 现假定向量寄存器的元素数或者说其长度为64,
- 与我们关心的向量运算长度匹配。(稍后取消这一限制)
- 向量处理器大幅缩减动态指令带宽,仅执6条,而MIPS600。
- 向量运算对64个元素执行,MIPS中差不多占据一半循环的开销指令在VMPS中不存在。
- 当编译器为这样一个序列生成向量指令时,所得到的代码会将大多数时间花费在向量运行模式中,将这种代码称为已向量化或可向量化。
- 如果循环的迭代之间没有相关性(这种相关被称为循环间相关,见4.5节),那么这些循环就可以向量化。
- 另一个区别是流水线互锁的频率。
- MIPS中,每个ADD.D都必须等待MUL.D,每个S.D都等AOD.D。
- 向量中,每个向量指令只会因为等待每个向量的第一个元素停顿,后续元素会沿着流水线顺畅流动。
- 因此,每条向量指令仅需要一次流水线停顿,而不是每个向量元素需一次。
- 向量架构师将元素相关操作的转发称为链接( chaining),因为这些相关操作是被“链接”在一起的。
- 例子中,MIPS中的流水线停顿頻率比VMPS高64。
- 软件流水线或循环展开(见附录H)可减少MPS中的流水线停顿,但难缩减指令带宽方面的差別。
4.2.3向量执行时间
here!!!
4.2.7内存组:为向量載入/存储单元提供带宽
- 载入存储向量单元比算术功能单元的行为复杂。
- 载入操作的开始时间
- 如果在无停顿情况下提供向量的其他元素,那么向量初始化速率就等于提取或存储新字的速度。
- 这一初始化速率不一定是一个时钟周期,因为存储器组的停顿可能降低有效吞吐量,这一点不同于较简单的功能单元
- 一般情况,载入存储单元的起始代价要高于算术单元的这一代价
- 在许多处理器中要多于100周期。
- 对于 VMIPS,我们假定起始时间为12周期,与Cray-1相同。
- 最近的向量计算机使用缓存来降低向量载入与存储的延迟
here!!!
4.4 图形处理器
这儿没写
4.4.1 GPU编程
- CPU程序员的挑战在GPU上获得出色性能,
- 还协调系统处理器与GPU上的计算调度、
- 系统存储器与GPU存储器间的数据传输
- 本节后面将看到,
- NVIDIA开发与C类似的语言和编程环境,通过克服异质计算及多种并行带来的双重挑战来提高GPU程序员的效率
- “计算统一设备体系结构”( Compute Unified Device Architecture)
- CUDA为系统处理器(主机)生成C/C++,
- 类似的编程语言是OPENGL,几家公司共同开发,
- NVIDIA认为这些并行形式的统一主题就是CUDA线程
- 以这种最低级别的并行作为编程原型,编译器和硬件可将数千CUDA线程聚合在一起,利用CPU中的各种并行类型:
- 因此, NVIDIA将CUDA编程模型定义为“单指令多线程”(SIMT)
- 这些线程进行分块,执行时32个线程一组,称线程块
- 将执行整个线程块的硬件称为多线程SIMD处理器
每个线程块是32个线程,这32个线程可以用一条指令搞完,每个16个车道的东西就是一个SIMD处理器
- GPU(设备)与系统处理器(主机)
- device__或__global,用__host__后者
- 被声明为__device__或 __global__functions的CUDA变量被分配给GPU存储
- GPU内核函数
- name<<>>(…parameter list …)
- 代码大小(块)
- 块的大小(线程)
- 块识别符(blockIdx)和每个块的线程识别符(threadIdx)
- CUDA还为每个块的线程数提供了一个关键字(blockDim),
所有的SIMD处理器都可以搞到GPU内存上取东西
每个线程的下标:blockDim*blockIdx+threadIdx,但是下面人家写的是blockDim.x * blockIdx.x+threadIdx.x
- 在一个多线程SIMD处理器中启动n个线程
- 每个向量元素一个线程,
- 每个线程块256个CUDA线程
- 根据块ID、
- 所有迭代都
- 向量化编译器也要求循环的迭代间无相关性,
- 明确指定
- 网格大小及每个SIMD处理器中线程数
- dimGrid和线程块里的thread数!
- 明确指出CUDA中的并行
- 由于为每个元素都分配了一个线程
每个内核都是一个格子,格子由块组成,块里面有很多thread
- 行执行和线程管理由GPU硬件负责,不由应用程序或OS
- 为简化硬件处理的排程,
- 尽管不同的线程块可用
- 全局存储器中的原子存储器操作进行协调
- 但它们之间不能直接通信
每个线程块独立干活
- 许多GPU硬件概念在CUDA中不明显。
- 生产效率来看,好事,
- 但重视性能的程序员用CUDA编程时须时刻惦记GPU硬件。
- 知道需将控制流中的32个线程分为一组
- 以从多线程SIMD处理器中获得最佳性能,
- 并在每个多线程SIMD处理器中另外创建许多线程,
- 以隐藏访问DRAM的延迟,稍后将解释原因
- 它们还需要将数据地址保持在一个或一些存储器块的局部范围内,
还需要将一些数据放在一些特定范围,这样还可以获得存储器性能啊
- 和许多并行系统一样,CUDA在生产效率和性能间折中
- 提供一些本身固有的功能,让程序员能显式控制硬件
- 一方面是生产效率,另一方面是使程序员能够表达硬件所能完成的所有操作,并行计算中,这两方面经常竞争
- 了解编程语言在这著名的生产效率与性能大战中如何发展,
- 了解CUDA是否能够在其他GPU或者其他类型的体系结构中变得普及
- 将非常有意义
4.4.2 NVIDIA GPU计算结构
- 为什么GPU有自己的体系结构类型,为什么有与CPU独立的专门术语
- 理解GPU的一个障碍就是术语,有些词汇的名称甚至可能导致误解
- 克服这一障碍的难度很大,这章多次重写就是例证
- 既能理解GPU体系结构,
- 又能学习许多采用非传统定义的GPU术语,
- 最终的解决方案是用CUDA术语来描述软件
- 用更具描述性的术语来介绍硬件,有时还借用OpenCL的术语
- 在用我们的术语解释GPU体系结构之后,
- 本节使用的一些更具描述性的术语、主流计算中的最接近术语、官方 NVIDIA GPU术语,以及这些术语的简短描述。
- 本节后续用该表左侧描述性术语来解释GPU徽结构
- 用上面CUDA并行编程语言的术语,以Fermi体系结构为例(见4.7节)
- 和向量体系结构一样,GPU只能很好解决数据级并行
- 这两种类型都拥有集中-分散数据传送和遮罩寄存器,
- 由于它们没有一种接近的标量处理器,
- 所以GPU有时会在运行时以硬件实现一些功能
- 而向量计算机通常是在编译时用软件来实现这些功能
- 与大多数向量体系结构不同,
- GPU还依靠单个多线程SIMD处理器中的多线程来隐藏存储器延迟(见2和3章)。
- 想为向量体系结构和GPU编写出高效代码,还需要考虑SIMD操作分组
多线程SIMD处理器指的是SIMD处理器
- 网格是在GPU上运行、由一组线程块构成的代码
- 表4-5:网格与向量化循环、线程块与循环体(已进行了条带挖掘,所以它是完整的计算循环)之间的相似
- 希望把两个向量乘在一起,每个向量8192
- 图4-8给出了这个示例与前两个GPU术语之间的关系
- 执行所有8192个元素乘法的GPU代码被称为网格(或向量化循环)
- 为了将它分解为更便于管理的大小,网格由线程块(或向量化循环体)组成,每个线程块最多512元素
- 一条SIMD指令次执行32个元素
- 向量有8192个元素,所以有16个线程块(16=8192÷512)。
- 网络和线程块是在GPU硬件中实现的编程抽象,帮助程序员组织自己的CUDA代码
- 线程块类似于一个向量长度为32的条带挖掘向量循环。
- 线程块调度程序将线程块指定给执行该代码的处理器(多线程SIMD处理器)
- 线程块调度程序与向量体系结构中的控制处理器有相似
- 它决定了该循环所需要的线程块数,在完成循环之前,一直将它们分配给不同的多线程SIMD处理器
- 示例将16个线程块发送给多线程SDMD处理器,计算这个循环的所有8192个元素。
- 每个SIMD指令线程的每条指令计算32个元素
- 每个线程块含16个SIMD指令线程
- 网格含16个线程块
- 硬件线程块调度程序将线程块指定给多线程SIMD处理器
- 硬件线程调度程序选择某个SIMD指令线程来运行一个SIMD处理器中的每个时钟周期
- 只有同一线程块中的SIMD线程可以通过本地存储器通信
- (Tesla代GPU,每个线程块可同时执行的最大SIMD线程数为16, Fermi一代32)
例子中每个线程块含16个SIMD线程,每个SIMD线程处理32个数
- 图4-9
- 与向量处理器类似,但它有许多并行功能单元都是深度流水化,
- 图4-8中,向每个多线程SIMD处理器分配这些向量的512个元素处理
- SIMD处理器都有独立PC的完整处理器,用线程进行编程(见3章)
- GPU硬件包含一组用来执行线程块网络(向量化循环体)的多线程SIMD处理器
- GPU是
- Fermi体系结构的前四种实现
- 未来的版本可能仅2或4
- 为在拥有不同个多线程SIMD处理器的GPU型号之间实现透明可伸缩
- 线程块调度程序将线程块(向量化循环主体)指定给多线程SIMD处理器
- 图4-10
线程块调度程序把每个块调度给SIMD处理器,不管你有几个SIMD处理器,我有这个线程块调度程序那我分配起来就很灵活啊
- 硬件创建、管理、调度和执行的机器对象是SIMD指令线程
- 这些SIMD指令线程有其自己的PC,运行在多线程SIMD处理器上
- SIMD线程调度程序包括一个记分板,
- 哪些SIMD指令线程已做好运行准备,
- 然后将它们发送给分发单元,
- 以在多线程SIMD处理器上运行。
- 与传统多线程处理器中的硬件线程调度程序相同(见3章),
- GPU硬件有两硬件调度程序
- 线程块调度程序,将线程块(向量化循环体)分配给多线程SDMD处理器,确保线程块被分配给其局部存储器拥有相应数据的处理器,
- SIMD处理器内部的SIMD线程调度程序,由它来调度应当何时运行SIMD指令线程。
虽然软件写起来是CUDA线程,但硬件干起来的处理对象是SIMD 指令线程,他是一个线程,只不过指令都是SIMD指令
所以你知道了,GPU由两种硬件调度程序:线程块调度程序和SIMD内部的SIMD线程调度程序
- 这些线程的SIMD指令的宽度为32,
- 线程块含512÷32=16SIMD线程(图4-8)。
- 线程由SIMD指令组成,
- 称为SIMD车道,它们与4.2节的向量车道很类似
- 每个SIMD处理器中车道数在各代GPU中不同
- 对Fermi,宽度为32的SIMD指令线程被映射到16个物理SIMD车道
- SIMD指令线程中的每条SIMD指令要两时钟周期完成
- 每个SIMD指令线程在锁定步骤执行,仅在开始时进行调度。
- 将SIMD处理器类比为向量处理器,可说它有16个车道,向量长度为32,钟鸣为2个时钟周期
- 用术语“SIMD处理器”,而不是“向量处理器”,就是因为这种既宽且浅的本质,前者的描述性更强一些
一条SIMD指令可以处理32个元素,但是只有16个SIMD车道哈哈哈。
- SIMD指令的线程是独立的
- SIMD线程调度程序可选择任何已准备就绪的SIMD指令线程,
- SIMD线程调度程序包括一个记分板(见3章)
- 跟踪多达48个SIMD线程
- 以了解哪个SIMD指令已做好运行准备
- 之所以需记分板,
- 因为存储器访问指令占用的时钟周期数无法预测
- 比如存储器组的冲突就可能导致这一现象
- 图4-11给出的SIMD线程调度程序在不同时间以不同顺序选取SIMD指令线程
- 假定GPU应用程序拥有很多的SIMD指令线程
- 多线程既可隐藏到DRAM延迟
- 又可提高多线程SIMD处理器使用率。
- 但最近的NVIDIA Femi GPU包含一个L2缓存(见4.7节)
- 每个SIMD处理器须将两个向量的32个元素从存储器载入寄存器中,通过读、写寄存器来执行乘法
- 为保存这些元素,SIMD处理器有32768个32位寄存器
- 就像向量处理器样,从逻辑上在向量车道之间划分这些寄存器,这里自然是在SIMD车道之同划分
- 每个SIMD线程被限制为不超过64个寄存器,所以我们可以认为一个SIMD线程最多拥有64个向量寄存器,
- 每个向量寄存器有32个元素,每个元素的宽度为32位。
- (双精度用两个相邻32位寄存器,
- 每个SIMD线程有32个包括32个元素的向量寄存器,
每个SIMD线程有2048个32位寄存器
意思是每个处理器上最多16个线程吗??
- Femi有16个物理SIMD车道,各含2048寄存器
怪不得16x2048=32768呢
- (GPU没有尝试根据位来设计硬件寄存器,使其拥有许多读取端口和写入端口,而是像向量处理器一样,使用较简单的存储器结构,但将它们划分为组,以获得足够带宽)
- 每个CUDA线程获取每个向量寄存器中的一个元素。
- 为用16个SIMD车道处理每个SMD指令线程的32个元素,线程块的CUDA线程可共用2048个寄存器的一半
- 为能够执行许多SIMD指令线程
- 需要在创建SIMD指令线程时在每个SIMD处理器上动态分配一组物理寄存器,并在退出SIMD线程时释放
- CUDA线程就是SIMD指令线程的垂直抽取
- 与ー个SMD车道上执行的元素相对应。
- 要当心,CUDA线程与POSX线程完全不同;
4.4.3 NVIDA GPU指令集体系结构
- 不同, NVIDIA编译器的指令集目标是硬件指令集的抽象。
- PTX(并行线程执行)为编译器提供一种稳定的指令集,实现各代GPU兼容。
- PTX指令描述了对单个CUDA线程的操作,通常与硬件指令一对一映射,但一个PTX可扩展到许多机器指令,反之亦然。
- PTX用虚寄,所以编译器指出一个SIMD线程需要多少物理向量寄存器,然后,由优化程序在SIMD线程之间划分可用的寄存器存储。
- 这一优化程序还会清除死亡代码,将指令打包在一起,并计算分支发生发散的位置和发散路径可能会聚的位置。
- x86与PTX类似,
- 这两者都会转换为一种内部形式(x86的微指令),
- 区别:对于x86,这一转换是在执行过程中在运行时以硬件实现的,
- 对GPU,则是在载入时以软件实现的。
- PTX指令的格式为:
- codetype d, a, b, c
- d是目标操作数,a、b和c是源;
- 操作类型如表4-6。
- 表4-7基本PTX指令集
- 所有指令都可由1位谓词寄存器进行判定,这些寄存器可由设置谓词指令(setp)来设定。
- 控制流指令为函数call和 return,线程exit、 branch以及线程块内线程的屏障同步(bar.svc)。
- 分支指令之前放谓词 可提供条件分支。
- 编译器或PTX程序员将虚拟寄存器声明为32位或64位有类型或无类型值。
- R0,R1.用于32位值,RDO,RD1.用于64位寄存器。
- 将虚拟寄存器指定给物理寄存器的过程
- PTX指令序列是4.4.1节 DAXPY循环一次迭代的指令:
- CUDA为每循环迭代定一个CUDA,
- 创8192CUDA线程,用唯一编号完成数组中每个元素的寻址,
- 前3条在R8中计算出唯一的元素字节偏移,
- 以下PTX指令载入两个双精浮点,对其乘加,并存储结果
- (下面描述与CUDA代码if(i
- GPU与向量体系不同
- 无用于顺序数据传送、步幅数据传送和集中——分散数据传送的指令。
- 所有数据传送都是集中——分散的
- 为重新获得顺序(单位步幅)数据传送的效率,
- GPU包含特殊的“地址接合”硬件,
- 来判断SIMD指令线程中的SIMD车道什么时候一同发出顺序地址。
- 运行时硬件随后通知存储器接口单元来请求发送32个顺序字的分块传送。
- 为实现这一重要的性能改进,
- GPU程序员须确保相邻的CUDA线程同时访问可以接合为一个或一些存储器或缓存块的相邻地址,
- 示例就这样
4.4.4 GPU中的条件分支
- 和单位步幅数据传送,向量体系结构和GPU在处理IF相似,
- 前以软,硬支持有限,
- 后面将看,除显式谓词寄存器外,
- GPU分支硬件使用了内部遮罩、
- 分支同步栈和指令标记来
- 控制分支何时分为多个执行路,这些路径何时汇合。
- 在PTX汇编程序级别,
- 一个CUDA线程的控制流是由PTX指令分支、调用、返回和退出描述的,
- 另外还加上每条指令的各个按线程车道给出的谓词来描述,
- 这些谓词由程序员用每个线程车道的1位谓词寄存器指定。
- PTX汇编程序分析PTX分支图,对其进行优化,实现最快速的GPU硬件指令序列。
- GPU硬件指令级,控制流:分支、跳转、索引跳转、调用、索引调用、返回、退出和管理分支同步栈的特殊指令。
- GPU硬件提供每个拥有自己栈的SIMD线程;
- 一个堆栈项含:一个标识符标记、一个目标指令地址和一个目标线程活动遮罩。
- GPU特殊指令为SIMD项目压入栈项,有些特殊指令和指令标记用于弹出栈项或者将栈展开为特殊项,并跳转到具有目标线程活动遮罩的目标指令地址。
- GPU硬件指令还有一些为不同车道设置的不同谓词(启/禁),
- PTX汇编程序通常将用PTX分支指令编码的简单外层IF/THEN/ELSE语句优化为设有谓词的GPU指令,不采用任何GPU分支指令。
- 更复杂控制流的优化通常会混合采用谓词与GPU分支指令,
- 这些分支指令带有一些特殊指令和标记,
- 当某些车道跳转到目标地址时,
- 这些GPU分支指令会使用分支同步栈压入一个栈项,而其他各项将会失败。
- 在这种情况下, NIDIA称为发生了分支分岔。
- 当SIMD车道执行同步标记或汇合时,也会使用这种混合方式,它会弹出一个栈项,并跳转到具有栈项线程活动遮罩的栈项地址。
- PTX汇编程序 识别循环分支,
- 并生成GPU分支指令,跳转到循环顶部,
- 用特殊栈指令来处理各个跳出循环的车道,
- 并在所有车道完成循环之后,使这些SIMD车道汇合。
- GPU索引跳转和索引调用指令
- 向栈中压项目,
- 以便所有车道完成开关语句或函数调用时,
- SIMD线程汇合。
- GPU设定谓词指令(setp)对IF的条件求值。
- PTX分支指令随后将根据该谓词来执行。
- 若PTX汇编程序生成了 无GPU分支,有谓词指令,
- 它会用各个车道的谓词寄存器来 启用或禁用 每条指令的每个SIMD车道。
- THEN部分线程中的SIMD指令向所有SIMD车道广播。
- 对ELSE,指令用谓词的补数(与THEN语句相对),
- 原来空闲的车道现在执行,
- 而它们前面的对应车道则不会执行。
- 在ELSE语句的结尾,会取消这些指令的谓词,使原始计算能继续。
- so对相同长度的路径,IF-THEN-ELSE效率为50%
- IF可嵌套,栈的使用也可,
- PTX汇编程序 混合用 设有谓词的指令 和 GPU分支与特殊分支指令,用于复杂控制流。
- 嵌套意味 大多数SIMD车道在执行嵌套条件语句时 是空闲。
- 等长路径的双重嵌套语句的执行效率为25%,三重嵌套12.5%
- 类似的情景:仅有少数几个遮罩位为1时向量处理器的运行。
- PTX在每个SIMD线程中的适当条件分支指令上设置“分支同步”标记,这个标记会在栈中压入当前活动遮罩。
- 如果条件分支分岔(有些车道进行跳转,有些失败)它会压人栈项,
- 并根据条件设置当前内容活动遮罩。
- 分支同步标记弹出分岔的分支项,并在ELSE部分之前翻转遮罩位。
- 在IF语句的末尾,PTX汇编程序添加了另一个分支同步标记,它会将先前的活动遮罩从栈中弹出,放入当前的活动遮罩中。
- 遮罩都1,THEN结束的分支指令将略过ELSE。
- 若都零,条件分支将跳过THEN指令。
- 并行的if语句和PTX分支经常使用没有异议的分支条件(所有车道都同意遵循同一路径),
- PTX汇编程序对此类分支进行了优化,跳过SIMD线程中所有车道都不会执行的指令块。
- 此优化在错误条件检查时是有用的,在这种情况下,必须进行测试,但很少会被选中。
- 编译为以下PTX
- (R8已有经过调整的线程ID),
- puSh、comp、pop由PTX汇编程序插入的分支同步标记,
- IF-THIEN-ELSET语句中的所有指令通常都由SIMD处理器执行。
- 一些SIMD车道为THEN启用,另一些ELSE启用的。
- 常见的情况,各个车道都一致选择设定谓词的分支,
- 如,根据参数值选分支,而所有车道的此参数都同,
- 所有活动遮罩位或者都0,或都1,so分支会跳过THEN或ELSE。
- 这一灵活性表明元素有其自己的程序计数器,但最缓慢的情况下,只有一个SIMD车道可以每两个时钟周期存储其结果,别车道闲置。
- 在向量体系结构中有种与之类似的最缓慢情景,那就是仅有一个遮罩位被设置为1时进行操作的情况。
- 这一灵活性会导致新手无法获得较佳性能,但在早期编程开发阶段可能是有帮助的。
- 在一个时钟周期内,SIMD车道的唯一选择就是执行在PTX指令中指定的操作或者处于空闲;
- 这一灵活性有助于解释为SIMD指令线程中每个元素指定的名称——CUDA线程,
- 它给人以独立运行的错觉。
- 新手认为这一线程抽象意味GPU能出色地处理条件分支。
- 一些线程沿一条路径执行,其他线程则沿另一路,只要你不着急,似乎就如此。
- 每个CUDA线程要么与线程块中的所有其他线程执行相同指令,要么空闲。
- 利用这一同步可处理有条件分支的循环,因为遮罩功能可关闭SIMD车道,自动检测循环的结束点。
- 最终得到的性能结果与这种简单的抽象不符。
- 如果写一些程序,以这种高度独立的MIMD模式来操作SIMD车道,
- 就好像编写了一些程序,
- 在物理存储器很小的计算机上用大量虚拟地址空间。
- 两程序都正确
- 但速度非常慢,程序员可能会对结果感到不快。
- 向量编译器可用遮罩寄存器做到GPU用硬件完成的小技巧,
- 但可能用标量指令来保存、求补和恢复遮罩寄存器。
- 条件执行就是这样例子:
- GPU在运行时用硬件完成向量体系结构在编译时完成的工作。
- 有种优化法,可在运行时针对GPU应用,但不能在编译时对向量体系结构应用,
- 就是在遮罩位全0或全1时略过THEN或ELSE部分。
- GPU执行条件分支的效率决定了分支的分岔频率。
- 某个特征值计算具有深度条件嵌套,
- 但代码测试表明,
- 82%的时钟周期发射将32个遮罩位中的29至32位设置为1,
- 所以GPU执行这一代码的效率可能要超出人们的预期。
- 同一机制处理向量循环的条带挖掘————当元素数与硬件不完全匹配时。
- 本节开始的例子表明,用一个语句检查SIMD车道元素数(上例该数目存储在RB)
4.4.5 NVIDA GPU存储器结构
图4-12是NVIDIA GPU的存储器结构。
- 多线程SIMD处理器中的每个SIMD车道获得片外DRAM的一个专用部分,
- 称之为专用存储器,
- 用于栈桢、溢出寄存器和不能放在寄存器中的私有变量。
- SIMD车道不共享专用存储器。
- 最近的GPU将这一专用存储器缓存在L1和L2缓存中,用于辅助寄存器溢出并加速函数调用。
每个多线程SIMD处理器本地的片上存储器称为本地存储器。
- 这一存储器由多线程SIMD处理器内的SIMD车道共享,但这一存储器不会在多线程SIMD处理器之间共享。
- 多线程SIMD处理器在创线程块时,
- 将部分本地存储器动态分配给此线程块,
- 当线程块中的所有线程都退出时,释放此存储器。
- 这一本地存储器部分由该线程块专用。
整个GPU和所有线程块共享的片外DRAM称为GPU存储器。
主机的系统处理器可读或写GPU存储器。
- 本地存储器不能供主机,它是每个多线程SMD专用的。
- 专用存储器也不可。
GPU不依赖大型缓存来包含应用程序的整个工作集,
- 是用较少的流式缓存,依靠大量的SIMD指令多线程来隐藏DRAM的长延迟,
- 原因是它们的工作集可能达到数百MB。
- 在用多线程隐藏DRAM延迟的情况下,系统处理器中供缓存使用的芯片面积用于计算资源和大量的寄存器,
- 如前文所述,向量的载入和存储与之相对,
- 是将这些延迟分散在许多元素之间,
- 因为它只需一次延迟,随后即可实现其余访问的流水化。
尽管隐藏存储器延迟是一种优选方法,但注意,最新的GPU和向量处理器都已经添加缓存。
- 最近Fermi体系结构加了缓存,
- 但它们要么被看作带宽滤选器,以减少对GPU存储器的要求,
- 要么被看作有限几种变量的加速器,这些变量的修改不能通过多线程隐藏。
- 因此,用于栈帧、函数调用和寄存器溢出的本地存储器与缓存是绝配,这是因为延迟对于函数调用是有影响的。
- 由于片上缓存访问所需要的能量要远远小于对多个外部DRAM芯片的访问,所以使用缓存还可以节省能量。
为提高存储器带宽、降低开销,
- 如上所述,当地址属于相同块时,PTX数据传送指令会将来自同一SIMD线程的各个并行线程请求接合在一起,变成单个存储器块请求。
- 对GPU程序设置的这些限制,多少类似于系统处理器程序在硬件预取方面的一些准则(见第2章)。
- GPU存储器控制器还会保留请求,将一些请求一同发送给同一个打开的页面,以提高存储器带宽(见4.6节)。
- 第2章介绍DRAM,可帮你理解对相关地址进行分组所带来的潜在好处。
参考链接