Files
2026-05-16 17:16:51 +08:00

67 KiB
Raw Permalink Blame History

第4章 数据级并行 - 章节关键原文

曲冠南老师qgn

Slide 4-3: 数据级并行——任务场景

科学计算:矩阵运算
三维渲染
图像处理

Slide 4-4: 数据级并行与SPMD

无论是矩阵运算还是图形图像处理其共性是对大量的数据施加同种变换——数据级并行DLP
从软件的角度在编程模型上我们期待SPMD
SPMDSingle Program Multiple Data

Slide 4-5: 数据级并行与SPMD

思考一个SPMD程序如何运行在SISD或MIMD上?

Slide 4-6: 数据级并行与SPMD

单核,单线程,串行化执行:
把MD拆成多次SD变为SISD
多核,多线程,并行化执行:
把SP的每条I多次重复变为MIMD
排队、耗时、耗电

Slide 4-8: 数据级并行——传统器件的问题

分析传统的标量CPU流水线可知取址、译码等操作逻辑复杂且开销不低
对于SPMD任务无论是在SISD还是MIMD多核器件上运行其取址、译码操作都是有冗余的

对于SPMD场景如何设计一种新型的器件

Slide 4-9: 数据级并行——SIMD

SIMD器件更多的ALU (Execute)更少的Fetch和Decode

Slide 4-10: 数据级并行——SIMD

更少的Fetch和Decode甚至其他流水部件意味着什么——更少的器件更低的能耗和时间开销
更多的ALU意味着什么——一次流水能处理更多数据速度更快
增加数据寄存器的数量来一次存储更多数据,以减少存储器访问延迟

Slide 4-12: 数据级并行——向量体系结构和GPU

向量体系结构
"窄而深"
指令流水线深ALU宽度窄
单次指令流水后能处理更多数据,掩盖不必要的流水线时间

GPU
"宽而浅"
指令流水线浅ALU宽度宽
流水本身比较简单,直接对更多的数据进行并行计算,同一时刻处理更多数据

Slide 4-15: 向量 vector

向量这种数据结构以及向量的运算和我们对SIMD的期待不谋而合

Slide 4-17: 向量的计算方式 1. 横向计算

向量计算是按行的方式从左到右横向地进行。
先计算:   d1←a1×(b1c1)
再计算:   d2←a2×(b2c2)
                ……
最后计算: dN←aN×(bNcN)
组成循环程序进行处理。
      ki←bici
      di←ki×ai
数据相关N 次   功能切换2N 次
这种计算方式是在标量处理器上对向量的一般计算方式,不适合于向量处理机的并行处理。

Slide 4-18: 向量的计算方式 2. 纵向计算

刚才的计算方式中,数据相关和功能切换随着向量长度增长而增长,硬件开销过大,并不是最优的向量计算方式。
下面介绍的纵向的计算方式更适合于进行向量计算。

Slide 4-19: 向量的计算方式 2. 纵向计算

向量计算是按列的方式从上到下纵向地进行。
         k1←b1c1             d1←k1×a1
先计算   ……        再计算    ……
         kN←bNcN             dN←kN×aN

表示成向量指令:
   KBC
   DK×A
两条向量指令之间:
   数据相关1次   功能切换1次

Slide 4-20 ~ 4-22: 向量的计算方式 3. 纵横(分组)计算

刚刚的纵向计算方式优化了向量计算的硬件开销,但是每次计算都需要访问到向量中的全部元素;
考虑到当前计算机体系结构的存储结构往往是层次化的指令操作数一般都会加载到寄存器中而寄存器的数量一般不会太多相比于可以无限增长的向量长度N来说
结合前面两种计算方式,我们可以使用分组计算的方法。

把向量分成若干组,组内按纵向方式处理,依次处理各组。
对于上述的例子,设:
       N=S×nr
其中N为向量长度S为组数n为每组的长度r为余数。
若余下的r个数也作为一组处理则共有S+1组。

先算第1组
      k1n←b1nc1n
      d1n←k1n×a1n
再算第2组
      k(n+1)2n←b(n+1)2nc(n+1)2n
      d(n+1)2n←k(n+1)2n×a(n+1)2n
依次进行下去直到最后一组第S+1组。
每组内各用两条向量指令。
     数据相关1次   功能切换2次

Slide 4-23: 向量体系结构 Vector Architecture

根据刚刚探讨的串行化向量计算优化方式,我们得到一种更适用于向量处理的体系结构
向量体系结构应当具有很大的顺序寄存器堆 (Register File),可加载更多向量元素以支持纵向计算
向量体系结构从内存中收集散落的数据将其放入寄存器堆中并对寄存器堆中的数据们进行操作然后将这些结果放回内存一次传输一组数据LD/ST流水化
一条指令能够对一个向量的数据进行操作,也就对向量中诸多独立数据元素进行了操作(纵向计算,功能单元流水化)

Slide 4-24: 向量体系结构的一些优势

由于向量的Load与Store是深度流水线化的大型寄存器堆充当了Buffer的作用因此其能够掩盖访存延迟并充分利用内存带宽
乱序的超标量处理器往往具有复杂的设计且乱序程度越高其复杂性和功耗也会越高在此方向发展很容易触及Power Wall
将顺序的标量处理器扩展为向量处理器则不会带来复杂度和功耗的大幅升高,且开发者也能很容易适应和转换到向量指令。

Slide 4-25: 实例VMIPS=标量MIPS+逻辑向量扩展

向量寄存器:
64bit*64元素*8个VR足够多读写口
向量功能单元:
全流水化每周期1操作
需要一个控制单元检测结构冒险和数据冒险
向量load/store单元
全流水化每周期1个字可操作标量
标量寄存器集合:
32通用32浮点可存地址和数据
可以为向量功能单元提供数据也可以为向量load/store单元提供地址

Slide 4-26: 向量专用特殊寄存器

向量长度寄存器VL
64位每一位对应于向量寄存器的一个单元。
VL控制所有向量运算的长度包括ld/st
作用将软件层程序中实际向量长度N与硬件层向量寄存器中的元素数目64相适配
向量屏蔽寄存器VM
当向量长度小于64时或者条件语句控制下对向量某些元素进行单独运算时使用
即使maskcode中有大量的0使用VM的向量指令速度依然远远快于标量计算模式。

Slide 4-27: Vmips向量指令格式

VMIPS指令=MIPS指令+Op1类型+Op2类型.精度
mispadd.d
vmipsaddvv.d(V1,V2,V3)addvs.d(V1,V2,F0)

Slide 4-28: 代码VMIPS vs mips

MIPS代码
L.D  F0,a     ;载入标量
  DADDIU  R4,Rx,#512   ;载入最后地址
LOOP: L.D  F2,0(Rx)   载入X[i]
  MUL.D  F2,F2,F0   a x X[i]
  L.D  F4,0(Ry)   载入Y[i]  
  ADD.D  F4,F4,F2   载入a x X[i] + Y[i]
  S.D  F4,0(Ry)   存入Y[i]
  DADDIU  Rx,Rx,#8   递增X
  DADDIU  Ry,Ry,#8   递增Y
  DSUBU  R20,R4,Rx   ;计算范围
  BNEZ  R20,LOOP   ;检查是否完成

VMIPS代码
L.D   F0,a     载入a
LV   V1,Rx     载入X
MULVS.D  V2,V1,F0   a x X
LV   V3,Ry     载入Y
ADDVV.D  V4,V2,V2   +Y
SV   V4,Ry     存入Y

减少的仅仅是代码的行数么?

Slide 4-29: 向量体系结构的相关概念

循环间相关:对一个循环来说,如果各轮迭代之间存在相关性,则称为循环间相关,否则为循环间无关
可向量化针对一组Mips指令描述的循环如果满足循环间无关则循环称为可向量化的编译器可为其生成向量指令。
指令编队convoy由一组不包含结构冒险的向量指令组成一个编队中的所有向量指令在硬件条件允许时可以并行执行。

Slide 4-30: 向量体系结构的性能优化

多车道技术
链接技术
编队技术
分段开采技术

Slide 4-31: 多车道技术

刚刚讨论的是向量计算在串行化中尽可能进行优化的结果
从并行的角度去考虑增加功能单元ALU的数量也能大大提升向量的计算速度多车道
这就好像,将一条单车道的窄巷扩宽成四车道的公路,吞吐量自然会提升

Slide 4-32: 链接技术

链接技术:当两条指令出现"写后读"相关时,若它们不存在功能部件冲突和向量寄存器(源或目的) 冲突,就有可能把它们所用的功能部件头尾相接,形成一个链接(长)流水线,进行流水处理。
链接过程无链接情况下后面的功能需要等到前一个功能的n个结果都产生才能开始而链接情况下后面的功能只需要等到前一个功能的第一个结果产生就可以开始即向量数据的生产与向量数据的消费进行延迟的重叠。
链接实质:把流水线定向的思想引入到向量执行过程,对两条流水线进行联合控制,没有改变寄存器和运算电路。
以下的讨论中假设各个部件之间传递一个结果需要一拍时间。

Slide 4-33: 链接技术例题

假设功能单元的时间开销为:浮点加减(6 cycle),浮点乘法(7 cycle),浮点存储操作(6 cycle)。为了同步要求,将向量元素送往功能部件,以及把结果存入向量寄存器需要一拍时间,从存储器中把数据送入访存功能部件也需要一拍时间。为以下程序画出链接示意图,并分析非链接执行和链接执行两种情况下的执行时间。
 	V3 ← 存储器	// 访存取向量A
	V2 ← V0  V1  // 向量B和向量C进行浮点加
	V4 ← V2 × V3	// 浮点乘结果存入V4

Slide 4-34: 链接技术例题解答

1. 三条指令全部用串行方法执行,则执行时间为:
           [161N1][161N1]
                   [171N1] = 3N 22 (拍)

2. 前两条指令并行执行然后再串行执行第3条指令则执行时间为
	      [161N1][171N1] = 2N 15 (拍)

3. 前两条指令并行执行并与第3条指令链接执行。
从访存开始到把第一个结果元素存入V4所需的拍数
     (亦称为链接流水线的建立时间)为:
		      [161] [171] = 17 (拍)
生成所有结果的执行时间为:
        17 N1 = N16 (拍)  解毕。

Slide 4-35: 进行向量链接的要求

保证:无向量寄存器使用冲突和无功能部件使用冲突
只有在前一条指令的第一个结果元素送入结果向量寄存器的那一个时钟周期才可以进行链接。
当一条向量指令的两个源操作数分别是两条先行指令的结果寄存器时,要求先行的两条指令产生运算结果的时间必须相等,即要求有关功能部件的通过时间相等。
要进行链接执行的向量指令的向量长度必须相等,否则无法进行链接。
一次链接行为通常仅发生在分组内部即不对整个N进行链接而对个分组内的n个向量元素的计算过程进行链接

Slide 4-36: 编队技术

几条能在同一个时钟周期内一起开始执行的向量指令集合称为一个编队;
同一个编队中的向量指令之间
不存在结构冲突;
不存在数据冲突;
存在数据冲突,但是可以链接。

Slide 4-37: 编队技术例题

假设每种向量功能部件只有一个,下面一组向量指令,在不使用链接技术和使用链接技术的情况下如何编队?

使用链接技术分为3个编队
第一编队LVMULTSV
第二编队LVADDV
第三编队SV

解不使用链接技术分为4个编队
第一编队LV
第二编队MULTSV LV
第三编队ADDV
第四编队SV

Slide 4-38: 分段开采技术

当向量的长度N大于向量寄存器的长度n时必须把长向量N分成长度固定为n的段然后循环分段处理每一次循环只处理一个向量段。这种技术称为分段开采技术。
由系统硬件和编译软件合作完成控制,对程序员是透明的。

Slide 4-39: 向量体系结构的性能影响因素

操作数向量的长度
向量启动时间
操作之间的数据相关,是否采用链接
操作之间的结构性相关,发射限制,车道数量,是否采用编队

Slide 4-41: GPU: 为并行而生

pixel parallelism: accelerate the building of images
Computing: Graphics oriented -> General Purpose
Programmable: Graphic API (e.g. OpenGL)-> Computing API (e.g. OpenCL)
Problems are not needed to be expressed in terms of vertex coordinates, textures and shader programs, greatly decreasing program complexity.

Slide 4-42: Transistors Dedication

Divergent Design Choices
control logics  v.s. arithmetic logics

Slide 4-43: Using CPU+GPU Architecture (Heterogeneous)

Use the right processor and memory for each task
CPU excels at executing a few serial threads
Fast sequential execution
Low latency cached memory access
GPU excels at executing many parallel threads
Scalable parallel execution
High bandwidth parallel memory access

Slide 4-44: GPGPU's benefits

It frees the programmer from possessing intimate knowledge of graphics APIs and GPU architecture.
Problems are not needed to be expressed in terms of vertex coordinates, textures and shader programs, greatly decreasing program complexity. 
Basic programming features such as random reads and writes to memory are supported, greatly develop the programming model. 
The import of double precision support scientific applications on the GPU with high performance.

Slide 4-46: Structure of GPGPU Chips

Slide 4-52: Scheduling Hierarchy

Slide 4-55: Code Pattern

Host Code Pattern
Malloc Host Memory
Malloc Device Memory
CUDAMemoryCopy(H2D)
Invoke Kernel Function
CUDAMemoryCopy(D2H)

Device Code Pattern
Locate Thread Index (TIDX)
Compute(TIDX)

CPU端
GPU端

Slide 4-60: Computing Hierarchy

Thread
Block
Grid
Core
Software
Hardware
SM
GPU
…

Slide 4-61: SIMD/SIMTMicro Parallelism

Slide 4-62: SIMT Implementation

为每个warp维护一个PC
取指令
指令进入不同指令队列
进入不同的车道并行执行

Slide 4-64: Warps/Wavefront

Slide 4-72: 笔记

Grid是扔到GPU上的单元。
Grid中有若干block 每个block扔到一个SM上
Block在扔到SM上的时候被分成warp。
比如Block中是64个thread这64个thread是 一个程序100条指令的实例64个thread分成两组每组32个放入两个warp。
为了节省资源因为32个thread的代码是相同的所以在warp中使用的是100条instruction, 放在warp的指令队列中每个instruction在执行的时候按照32个thread操纵32个数据用threadID对应各自的数据。

李宏图老师lht

以下内容合并自20220526和20230331两个版本的PPT

Slide两版本共有: 数据级并行——任务场景

科学计算:矩阵运算
三维渲染
图像处理

Slide: 数据级并行与SPMD

无论是矩阵运算还是图形图像处理其共性是对大量的数据施加同种变换——数据级并行DLP
从软件的角度在编程模型上我们期待SPMD
SPMDSingle Program Multiple Data

Slide: 数据级并行

对大量的数据施加同种变换——数据级并行DLP
迭代1
迭代2
串行执行:
for (i=0; i < N; i++)
    C[i] = A[i] + B[i];

Slide: 数据级并行

对大量的数据施加同种变换——数据级并行DLP
for (i=0; i < N; i++)
    C[i] = A[i] + B[i];
迭代1
迭代2
并行执行:

Slide: ARM A53 架构

ARM A53 架构:观察并找出前端(取址、译码)部分
参考https://zhuanlan.zhihu.com/p/469582393

Slide: 数据级并行——传统器件的问题

分析传统的标量CPU流水线可知取址、译码等操作逻辑复杂且开销不低
对于数据级并行任务无论是在SISD还是MIMD多核器件上运行其取址、译码操作都是有冗余的

对于数据级并行场景,如何设计一种新型的器件?

Slide: 数据级并行——SIMD

更多的ALU (Execute)更少的Fetch和Decode
一种设计
更少的Fetch和Decode甚至其他流水部件意味着什么——更少的器件更低的能耗和时间开销
更多的ALU意味着什么——一次流水能处理更多数据速度更快
增加数据寄存器的数量,一次存储更多数据,以减少存储器访问延迟

Slide20230331版本特有: 单指令多数据SIMD处理

一条指令处理多个不同的数据
有多个处理单元PEProcessing Elements

阵列处理机:同一指令在同一时间处理不同空间上的不同数据元素
向量处理机:同一指令在连续的时间内在同一空间上处理不同的数据元素

Slide: 数据级并行两种执行方式

阵列处理机vs向量处理机
ARRAY PROCESSOR
VECTOR PROCESSOR

LD0 LD1 LD2 LD3
AD0 AD1 AD2 AD3
MU0 MU1 MU2 MU3
ST0 ST1 ST2 ST3

LD0 LD1
AD0 AD1
MU0 MU1
LD2 LD3
AD2 AD3
ST0 ST1
MU2 MU3
ST2 ST3

Space
Space
Same op @ same time
Different ops @ same space
Different ops @ time
Same op @ space

LD     VR  A[3:0]
ADD  VR  VR, 1 
MUL  VR  VR, 2
ST     A[3:0]  VR
Instruction Stream

Slide: SIMD vs. VLIW

阵列处理机:对不同数据进行同一操作

Slide: SIMD vs. VLIW

VLIW多个独立操作由编译器捆绑在一起

Slide: 数据级并行——向量体系结构和GPU

向量体系结构
"窄而深"
指令流水线深ALU宽度窄
单次指令流水后能处理更多数据,掩盖不必要的流水线时间

GPU
"宽而浅"
指令流水线浅ALU宽度宽
流水本身比较简单,直接对更多的数据进行并行计算,同一时刻处理更多数据

Slide: 向量 vector

向量这种数据结构以及向量的运算和我们对SIMD的期待不谋而合

Slide: 向量的串行化计算方式

Slide: 向量的计算方式 1. 横向计算

这种计算方式是在标量处理器上对向量的一般计算方式

Slide: 向量的计算方式 1. 横向计算

向量计算是按行的方式从左到右横向地进行。
先计算:   d1←a1×(b1c1)
再计算:   d2←a2×(b2c2)
                ……
最后计算: dN←aN×(bNcN)

组成循环程序进行处理。
      ki←bici
      di←ki×ai
数据相关N次   功能切换2N次
不适合于向量处理机的并行处理。

Slide: 向量的计算方式 2. 纵向计算

刚才的计算方式中,数据相关和功能切换随着向量长度增长而增长,硬件开销过大,并不是最优的向量计算方式
下面介绍的纵向的计算方式更适合于进行向量计算

Slide: 向量的计算方式 2. 纵向计算

向量计算是按列的方式从上到下纵向地进行。
         k1←b1c1             d1←k1×a1
先计算   ……        再计算    ……
         kN←bNcN             dN←kN×aN

表示成向量指令:
   KBC
   DK×A
两条向量指令之间:
   数据相关1次   功能切换1次

Slide: 对处理机结构的要求:存储器-存储器结构

向量指令的源向量和目的向量都存放在存储器中,运算的中间结果需要送回存储器。 
存储器-存储器型操作的运算流水线
例如STAR-100、CYBER-205

Slide: 向量的计算方式 3. 纵横(分组)计算

刚刚的纵向计算方式优化了向量计算的硬件开销,但是每次计算都需要访问到向量中的全部元素
考虑到当前计算机体系结构的存储结构往往是层次化的指令操作数一般都会加载到寄存器中而寄存器的数量一般不会太多相比于可以无限增长的向量长度N来说
结合前面两种计算方式,我们可以使用分组计算的方法

Slide: 向量的计算方式 3. 纵横(分组)计算

把向量分成若干组,组内按纵向方式处理,依次处理各组。
对于上述的例子,设:
       N=S×nr
其中N为向量长度S为组数n为每组的长度r为余数。
若余下的r个数也作为一组处理则共有S+1组。

Slide: 向量的计算方式 3. 纵横(分组)计算

先算第1组
      k1n←b1nc1n
      d1n←k1n×a1n
再算第2组
      k(n+1)2n←b(n+1)2nc(n+1)2n
      d(n+1)2n←k(n+1)2n×a(n+1)2n
依次进行下去直到最后一组第S+1组。
每组内各用两条向量指令。
     数据相关1次   功能切换2次

Slide: 对处理机结构的要求:寄存器-寄存器结构

设置能快速访问的向量寄存器,用于存放源向量、目的向量及中间结果,让运算部件的输入、输出端都与向量寄存器相联,构成寄存器-寄存器型操作的运算流水线。
典型的寄存器-寄存器结构的向量处理机 
    美国的CRAY-1、我国的YH-1巨型机

Slide: 向量体系结构 Vector Architecture

根据刚刚探讨的串行化向量计算优化方式,我们得到一种更适用于向量处理的体系结构
向量体系结构应当具有很大的顺序寄存器堆 (Register File),可加载更多向量元素以支持纵向计算
向量体系结构从内存中收集散落的数据将其放入寄存器堆中并对寄存器堆中的数据们进行操作然后将这些结果放回内存一次传输一组数据LD/ST流水化
一条指令能够对一个向量的数据进行操作,也就对向量中诸多独立数据元素进行了操作(纵向计算,功能单元流水化)

Slide: 向量体系结构的一些优势

由于向量的Load与Store是深度流水线化的大型寄存器堆充当了Buffer的作用因此其能够掩盖访存延迟并充分利用内存带宽
乱序的超标量处理器往往具有复杂的设计且乱序程度越高其复杂性和功耗也会越高在此方向发展很容易触及Power Wall
将顺序的标量处理器扩展为向量处理器则不会带来复杂度和功耗的大幅升高,且开发者也能很容易适应和转换到向量指令

Slide: 实例VMIPS=标量MIPS+逻辑向量扩展

向量寄存器:
64bit64元素*8个VR足够多读写口
向量功能单元
全流水化每周期1操作
需要一个控制单元检测结构冒险和数据冒险
向量load/store单元
全流水化每周期1个字可操作标量
标量寄存器集合
32通用32浮点可存地址和数据
可以为向量功能单元提供数据也可以为向量load/store单元提供地址

Slide: 向量Vi冲突和功能部件冲突

只要不出现Vi冲突和功能部件冲突各Vi之间和各功能部件之间都能并行工作大大加快了向量指令的处理。
Vi冲突并行工作的各向量指令的源向量或结果向量使用了相同的Vi。
例如:源向量相同
     V3←V1V2
     V5←V4∧V1
功能部件冲突:并行工作的各向量指令要使用同一个功能部件。
例如:都需使用乘法功能部件
     V3←V1×V2
     V5←V4×V6

Slide: 向量专用特殊寄存器

向量长度寄存器VL
64位每一位对应于向量寄存器的一个单元。
VL控制所有向量运算的长度包括ld/st
作用将软件层程序中实际向量长度N与硬件层向量寄存器中的元素数目64相适配
向量屏蔽寄存器VM
当向量长度小于64时或者条件语句控制下对向量某些元素进行单独运算时使用
即使maskcode中有大量的0使用VM的向量指令速度依然远远快于标量计算模式。

Slide: 向量屏蔽寄存器VM

Slide20230331版本: CRAY-1的基本结构

共有12条可并行工作的单功能流水线可分别流水地进行地址、向量、标量的各种运算。

其中6个单功能流水部件进行向量运算 
整数加3
逻辑运算2
移位4
浮点加6
浮点乘7
浮点迭代求倒数14
括号中的数字为其流水经过的时钟周期。

Slide: Vmips向量指令格式

VMIPS指令=MIPS指令+Op1类型+Op2类型.精度
mispadd.d
vmipsaddvv.d(V1,V2,V3)addvs.d(V1,V2,F0)

Slide: 代码VMIPS vs mips

MIPS代码
L.D FO,a      ;载入标量
  DADDIU R4,Rx,#512   ;载入最后地址
LOOP: L.D F2,0(Rx)    载入X[i]
  MUL.D F2,F2,F0      a × X[i]
  L.D F4,0(Ry)    载入Y[i]  
  ADD.D F4,F4,F2      载入a x X[i] + Y[i]
  S.D F4,9(Ry)    存入Y[i]
  DADDIURx,Rx,#8      递增X
  DADDIU Ry,Ry,#8     递增Y
  DSUBU R20,R4,Rx     ;计算范围
  BNEZ R20,LOOP   ;检查是否完成

VMIPS代码
L.D   F0,a     载入a
LV   V1,Rx     载入X
MULVS.D V2,V1,F0   a × X
LV   V3,Ry     载入Y
ADDVV.D V4,V2,V2   +Y
SV   V4,Ry     存入Y

减少的仅仅是代码的行数么?

Slide: 向量体系结构的相关概念

循环间相关:对一个循环来说,如果各轮迭代之间存在相关性,则称为循环间相关,否则为循环间无关
可向量化针对一组Mips指令描述的循环如果满足循环间无关则循环称为可向量化的编译器可为其生成向量指令。
指令编队convoy由一组不包含结构冒险的向量指令组成一个编队中的所有向量指令在硬件条件允许时可以并行执行。

Slide: 向量体系结构的性能优化

多车道技术
链接技术
编队技术
分段开采技术

Slide: 多车道技术

刚刚讨论的是向量计算在串行化中尽可能进行优化的结果
从并行的角度去考虑增加功能单元ALU的数量也能大大提升向量的计算速度多车道
这就好像,将一条单车道的窄巷扩宽成四车道的公路,吞吐量自然会提升

Slide: 链接技术

链接技术:当两条指令出现"写后读"相关时,若它们不存在功能部件冲突和向量寄存器(源或目的) 冲突,就有可能把它们所用的功能部件头尾相接,形成一个链接(长)流水线,进行流水处理。
链接过程无链接情况下后面的功能需要等到前一个功能的n个结果都产生才能开始而链接情况下后面的功能只需要等到前一个功能的第一个结果产生就可以开始即向量数据的生产与向量数据的消费进行延迟的重叠。
链接实质:把流水线定向的思想引入到向量执行过程,对两条流水线进行联合控制,没有改变寄存器和运算电路。
以下的讨论中假设各个部件之间传递一个结果需要一拍时间。

Slide: 链接技术例题

假设功能单元的时间开销为:浮点加减(6 cycle),浮点乘法(7 cycle),浮点存储操作(6 cycle)。为以下程序画出链接示意图,并分析非链接执行和链接执行两种情况下的执行时间。
 	V3 ← 存储器	// 访存取向量A
	V2 ← V0  V1  // 向量B和向量C进行浮点加
	V4 ← V2 × V3	// 浮点乘结果存入V4

Slide: 链接技术例题解答

1. 三条指令全部用串行方法执行,则执行时间为:
           [161N1][161N1]
                   [171N1] = 3N 22 (拍)

2. 前两条指令并行执行然后再串行执行第3条指令则执行时间为
	      [161N1][171N1] = 2N 15 (拍)

3. 前两条指令并行执行并与第3条指令链接执行。
从访存开始到把第一个结果元素存入V4所需的拍数
     (亦称为链接流水线的建立时间)为:
		      [161] [171] = 17 (拍)
生成所有结果的执行时间为:
        17 N1 = N16 (拍)  解毕。

Slide: 进行向量链接的要求

保证:无向量寄存器使用冲突和无功能部件使用冲突
只有在前一条指令的第一个结果元素送入结果向量寄存器的那一个时钟周期才可以进行链接。
当一条向量指令的两个源操作数分别是两条先行指令的结果寄存器时,要求先行的两条指令产生运算结果的时间必须相等,即要求有关功能部件的通过时间相等。
要进行链接执行的向量指令的向量长度必须相等,否则无法进行链接。
一次链接行为通常仅发生在分组内部即不对整个N进行链接而对个分组内的n个向量元素的计算过程进行链接

Slide: 编队技术

几条能在同一个时钟周期内一起开始执行的向量指令集合称为一个编队;
同一个编队中的向量指令之间
不存在结构冲突;
不存在数据冲突;
存在数据冲突,但是可以链接。

Slide: 编队技术例题

假设每种向量功能部件只有一个,下面一组向量指令,在不使用链接技术和使用链接技术的情况下如何编队?
  LV        V1Rx    
  MULTSV    V2F0V1  
  LV        V3Ry
  ADDV      V4V2V3   
  SV        RyV4

使用链接技术分为3个编队
第一编队LVMULTSV
第二编队LVADDV
第三编队SV

解不使用链接技术分为4个编队
第一编队LV
第二编队MULTSV LV
第三编队ADDV
第四编队SV

Slide: 分段开采技术

当向量的长度N大于向量寄存器的长度n时必须把长向量N分成长度固定为n的段然后循环分段处理每一次循环只处理一个向量段。这种技术称为分段开采技术。
由系统硬件和编译软件合作完成控制,对程序员是透明的。

Slide: 例3.4

设A和B是长度为N的向量考虑在Cray-1向量处理器上实现以下的循环操作
            			DO I = 1:N
            				AI= 5.0 * BI + C       ;c=1.0

当N ≤64时可以用以下指令序列

S1 ← 5.0       	将常数5.0送入标量寄存器S1
S2 ← 1.0  	    	将常数1.0送入标量寄存器S2	
VL ← N  	    	在向量长度寄存器VL中设置向量长度N
V0 ← B    	    	从存储器中将向量B读入向量寄存器V0
V1 ← S1 × V0      	向量B中的每个元素分别和常数S1相乘
V2 ← S2 + V1	 	向量V1中的每个元素分别和常数S2相加
A ← V2	    		 将计算结果从向量寄存器V2存入存储器的向量A

Slide: 当N64时的分段开采

当N 64时就需要进行分段开采。
循环次数K 
余数L

S1 ← 5.0		将常数5.0送入标量寄存器S1
S2 ← 1.0		将常数1.0送入标量寄存器S2
VL ← L   		在向量长度寄存器VL中设置向量长度L
V0 ← B    		从存储器中将向量B[0..L-1]读入向量
         		寄存器V0
V1 ← S1 * V0  	向量B中的每个元素分别和常数S1相乘
V2 ← S2 + V1  	向量V1中的每个元素分别和常数S2相加
A ← V2       	将计算结果从向量寄存器V2存入存储器
             	的向量A[0..L-1]

处理余
数部分,
计算L
个元素

For (I=0 to K-1)  {
V0 ← B     	从存储器中将向量B[L+I*64..L+I*64+63]
            	读入向量寄存器V0
V1 ← S1 * V0   	向量B中的每个元素分别和常数S1相乘
V2 ← S2 + V1   	向量V1中的每个元素分别和常数S2相加
A ← V2         将计算结果V2存入存储器的向量
                 A[L+I*64… L+I*64+63]
}

循环
K次,
分段
处理

Slide: 向量体系结构的性能影响因素

操作数向量的长度
向量启动时间
操作之间的数据相关,是否采用链接
操作之间的结构性相关,发射限制,车道数量,是否采用编队

Slide: GPU: 为并行而生

pixel parallelism: accelerate the building of images
Computing: Graphics oriented -> General Purpose
Programmable: Graphic API (e.g. OpenGL)-> Computing API (e.g. OpenCL)
Problems are not needed to be expressed in terms of vertex coordinates, textures and shader programs, greatly decreasing program complexity.

Slide: Transistors Dedication

Divergent Design Choices
control logics  v.s. arithmetic logics

Slide: Using CPU+GPU Architecture (Heterogeneous)

Use the right processor and memory for each task
CPU excels at executing a few serial threads
Fast sequential execution
Low latency cached memory access
GPU excels at executing many parallel threads
Scalable parallel execution
High bandwidth parallel memory access

Slide: GPGPU's benefits

It frees the programmer from possessing intimate knowledge of graphics APIs and GPU architecture.
Problems are not needed to be expressed in terms of vertex coordinates, textures and shader programs, greatly decreasing program complexity. 
Basic programming features such as random reads and writes to memory are supported, greatly develop the programming model. 
The import of double precision support scientific applications on the GPU with high performance.

Slide: GPU

GPU=Graphics Processing Unit图形处理器
具有极高的计算吞吐率和内存带宽
专用图形图像处理器
适合纹理图像处理

GPGPU=General-purpose computing on GPUs通用图形处理器
可进行通用计算编程例如CUDA, OpenCL
适合处理SIMD程序

Slide: CPU vs GPU

多核 vs 众核:几个核 vs 上千个核
GPU将芯片中更多的晶体管用于计算

Slide: GPU处理模式

流水线 vs 并发执行

Slide: SPMD编程模型

Single Program Multiple Data (SPMD)单程序多数据
程序采用多线程的模型,而非向量指令
与SIMD编程模型不同
不同的进程/线程运行同一个程序源代码SP但是分别使用不同的输入数据进行各自的计算MD
不同进程/线程相互独立,没有执行顺序的要求

常用的并行编程模型多数采用SPMD模式
例如CUDAMPIopenMP

Slide: GPU编程模型

CUDA=Compute Unified Device Architecture,统一计算设备架构
由NVIDIA提出的通用并行计算平台和编程模型
CUDA软件环境使用C++作为高级编程语言
采用SPMD编程方式

Slide: 异构编程

Host
主机(CPU)
运行C++程序
Device
物理上分离的协处理器(GPU)
运行kernel程序
运行thread
运行CUDA目标代码

Slide: CUDA程序示例

向量加法
// Kernel definition
__global__ void VecAdd(float* A, float* B, float* C)
{
int i = threadIdx.x;
C[i] = A[i] + B[i];
}
int main()
{
...
// Kernel invocation with N threads
VecAdd<<<1, N>>>(A, B, C);
...
}

Slide: Host-Device数据传输

Host代码
… Allocate h_A, h_B, h_C …

void vecAdd(float *h_A, float *h_B, float *h_C, int n)
{
    int size = n * sizeof(float); float *d_A, *d_B, *d_C;

    cudaMalloc((void **) &d_A, size);    
    cudaMalloc((void **) &d_B, size);
    cudaMalloc((void **) &d_C, size);

    cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);
    cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);
     
     // Kernel invocation code  to be shown later

     cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);
     cudaFree(d_A); cudaFree(d_B); cudaFree (d_C);
}

… Free h_A, h_B, h_C …

Slide: GPU执行方式

Single Instruction Multiple Thread单指令多线程

SIMD与多线程相结合
线程按照固定的方式运行与传统SPMD不同
固定个数的线程一起执行SIMD
使用标量指令与传统SIMD不同

Slide: SPMD, SIMT, 和SIMD

SPMD
GPU的编程模型
SIMT
GPU的执行方式
SIMD
GPU计算单元的处理方式

Slide: SIMT的优点

编程灵活
任意大小的工作量vs任意硬件SIMD宽度
每个线程可以被单独对待
分支控制
Warp细节对程序员透明
Warp大小
哪些线程由同一个warp执行

Slide: NVIDIA H100 GPU

16896 FP32 CUDA cores
66.9 TFLOPS (FP64 Tensor core)
33.5 TFLOPS (FP64)
3352 GB/s memory bandwidth
700W TDP
TSMC's 4N customized for NVIDIA

Slide: GPGPU微体系结构

线程以warp为单位进行调度
同一warp中的不同线程按照SIMT方式执行
大容量寄存器组用于存放所有线程的数据
SPStreaming Processor进行数据的并行计算i.e., ALU

标量指令处理
SIMD数据访问及计算

Slide: Warp

在Nvidia GPU中warp为32个标量线程构成的SIMT执行单位
GPU SM中以warp为最小单位进行调度和执行
GPU无法控制单独执行某一个thread
Warp中不同的Thread在执行时保持同步
所有Thread永远执行相同PC所对应的指令

Slide: 通过细粒度多线程并行掩盖延迟

细粒度多线程执行

Slide: Warp调度

LD R1, 0x00027    PC=1
LD R2, 0x00034    PC=2
ADD R3, R1, R2    PC=3
ST R3, 0x00045    PC=4

Cycle 100: 调度 warp0 PC1
Cycle 101: 调度 warp1 PC2
Cycle 102: 调度 warp2 PC1
…

如果访存时间长则PC3对应的指令需要等待GPU将大量warp交替调度执行可以一定程度上掩盖长时间访存

Slide: Warp执行

32-thread warp executing ADD A[tid],B[tid] → C[tid]

Slide: Streaming Processor

CUDA core
SIMDSingle Instruction Multiple Data执行
大量SP提供极高并行算力

Slide: Warp指令级的并行执行

可以将不同指令交替执行
假设机器中有8个通道1个warp中有32个线程
每周期完成24个操作但是每周期流出1个warp

Slide: SIMT的访存模式

执行同一指令的不同线程使用线程ID来访问不同的数据
假设N=16, 每个warp中有4个线程 → 共有4个warp

Slide: Coalescing

将一个warp中不同thread的内存访问合并成更少的访问次数
如果一个warp中的32个thread访问内存中连续的4B大小的位置
合并成一个128B的访存请求(coalescing)
而不是发送32个4B的访存请求 
有效降低SM和DRAM之间的访存次数
减少片上网络、片上存储划分、DRAM的工作量

Slide: GPU程序层次模型

一个程序kernel grid由多个block组成
例如256个block
一个block由多个thread组成
例如1024个thread
Thread为标量线程执行完整的程序源代码

Slide: GPU程序层次模型

一个kernel以block为单位分配到硬件中
多个block可以分配到同一个SM中只要有足够的硬件资源

Slide: GPU程序层次模型

在SM内部来自同一个block中的不同线程编号相邻组成一个warp
例如1 warp = 32 thread
Block和thread对程序员可见warp对程序员不可见由硬件进行控制
同一kernel中的所有线程都共享相同的代码
不同线程执行进度可能不同
但是同一warp中的线程一同执行
可以在Block级别进行同步

Slide: GPU程序层次例子

要处理所有元素的代码在一个grid中
将一个grid分割成可处理的大小Block
例如一个block中有512个线程
一次以SIMD方式处理32个元素

Block与向量处理循环中的向量长度为32的分段开采类似

Slide: GPU与传统SIMD的区别

传统SIMD采用单线程
向量指令之间锁步执行:一个向量指令完成后后续指令才能开始执行
编程方式为SIMD软件需要知道向量长度
ISA中有向量/SIMD指令

GPU以SIMD方式执行大量标量线程
可以非锁步执行
每个线程可以被独立看待,由硬件进行线程组合(Warp)
编程方式为SPMD
ISA是标量的

Slide: Nvidia ISA

"Parallel Thread Execution (PTX)"
NVIDIA编译器的指令集目标时硬件指令集的一种抽象可实现各代GPU的兼容性
opcode.type d,a,b,c;
使用虚拟寄存器
由软件将其翻译成机器语言

shl.s32	R8, blockIdx, 9	; Thread Block ID * Block size (512 or 2^9)
add.s32	R8, R8, threadIdx	; R8 = i = my CUDA thread ID
ld.global.f64	RD0, [X+R8]	; RD0 = X[i]
ld.global.f64	RD2, [Y+R8]	; RD2 = Y[i]
mul.f64 R0D, RD0, RD4	; Product in RD0 = RD0 * RD4 (scalar a)
add.f64 R0D, RD0, RD2	; Sum in RD0 = RD0 + RD2 (Y[i])
st.global.f64 [Y+R8], RD0	; Y[i] = sum (X[i]*a + Y[i])

Slide: Nvidia机器指令

PTX在运行时转换成SASS
SASS与GPGPU计算能力对应一般一种计算架构有一组对应的SASS指令
不同架构也可能采用相同的SASS

/*0030*/ MOV R6, 0x4;
/*0040*/ S2R R3, SR_TID.X

Slide: 分支处理

GPU分支硬件也使用掩码类似向量处理机中的向量屏蔽寄存器
此外,还使用硬件栈处理分支
分歧后push
汇聚后pop

Slide: 例子(分支处理)

if (X[i] != 0)	
		X[i] = X[i]  Y[i];
	else X[i] = Z[i];

	ld.global.f64	RD0, [X+R8]		; RD0 = X[i]
	setp.neq.s32	P1, RD0, #0		; P1 is predicate register 1
	@!P1, bra	ELSE1, *Push		; Push old mask, set new mask bits
					; if P1 false, go to ELSE1
	ld.global.f64	RD2, [Y+R8]		; RD2 = Y[i]
	sub.f64	RD0, RD0, RD2		; Difference in RD0
	st.global.f64	[X+R8], RD0		; X[i] = RD0
	@P1, bra	ENDIF1, *Comp		; complement mask bits
					; if P1 true, go to ENDIF1
ELSE1:		ld.global.f64 RD0, [Z+R8]	; RD0 = Z[i]
			st.global.f64 [X+R8], RD0	; X[i] = RD0
ENDIF1: 	<next instruction>, *Pop		; pop to restore old mask

*Push *Comp *Pop为由PTX汇编程序插入的分支同步标记

Slide: 分支分歧(branch divergence)

同一warp中的不同thread在遇到分支指令时可能会有分歧
例如if-else

Slide: 分支分歧(branch divergence)

分支分歧当同一warp中的不同线程在遇到分支指令时执行不同的路径

GPU使用简化的控制逻辑来减少控制部分所占的面积
thread无法单独控制和调度
并行的处理单元同一个时钟周期只能处理相同的操作

Slide: 分支分歧(branch divergence)

每个warp用一个栈来处理分支分歧
分支指令中可以加入一个汇聚指令域

A: v = foo[tid.x];
B: if (v < 10) 
C:    v = 0;
   else
D:    v = 10;
E: w = bar[tid.x]+v;

foo[] = {4,8,12,16};
One stack per warp

Slide: 分支分歧处理

每个warp使用一个栈来处理分支分歧的执行

Slide: 存储层次

Local memory
一个thread
Shared memory
一个block中的所有thread
Global memory
所有thread

Texture memory
只读所有thread
适合特定地址访问模型
Constant memory
只读所有thread

Slide: AMD GPU

AMD TeraScale Architecture: VLIW
CU = SM, Work group = block (CTA), work item = thread, wavefront = warp (64)

Slide: AMD GPU

AMD GCN Architecture
Wavefront size = 64
每个SIMD Unit 中有 16 ALU

Slide: AMD GPU

AMD RDNA Architecture
Wavefront size = 32
每个SIMD unit中有32个ALU

Slide: GPGPU Virtualization虚拟化

企业级做云计算时常常采用虚拟化技术:
一个GPU的SM很多但是对于单用户只能用很少的SM,这样利用率很低;
硬件上把多个SM进行分割成多个切片
软件上为每个用户分配一个SM切片
使得每个用户都能满负荷使用SM;
每个用户感觉自己拥有了一个GPU。

Slide: 笔记

Grid是扔到GPU上的单元。
Grid中有若干block 每个block扔到一个SM上
Block在扔到SM上的时候被分成warp。
比如Block中是64个thread这64个thread是 一个程序100条指令的实例64个thread分成两组每组32个放入两个warp。
为了节省资源因为32个thread的代码是相同的所以在warp中使用的是100条instruction, 放在warp的指令队列中每个instruction在执行的时候按照32个thread操纵32个数据用threadID对应各自的数据。

谭婧炜佳老师tjwj

Slide 4-1: 计算机系统结构第4章 数据级并行

授课教师:谭婧炜佳
jtan@jlu.edu.cn

Slide 4-2 ~ 4-3: 本章内容

数据级并行的概念
向量体系结构
GPU体系结构

Slide 4-4: 数据级并行——任务场景

对大量不同数据执行相同操作
科学计算:矩阵运算
三维渲染
图像处理

Slide 4-5: 数据级并行——计算场景

对大量不同数据执行相同操作
例如:矩阵/向量加法

串行执行:
for (i=0; i < N; i++)
    C[i] = A[i] + B[i];
迭代1
迭代2

Slide 4-6: 数据级并行——计算场景

对大量不同数据执行相同操作
例如:矩阵/向量加法

并行执行:
for (i=0; i < N; i++)
    C[i] = A[i] + B[i];
迭代1
迭代2

Slide 4-7: 数据级并行程序执行

Slide 4-8: 存在的问题

单核,单线程,串行化执行:
把MD拆成多次SD变为SISD
多核,多线程,并行化执行:
把SI的每条I多次重复变为MIMD
排队、耗时、耗电

Slide 4-9: 存在的问题

在标量CPU流水线中取址、译码等操作逻辑复杂且开销不低
对于数据级并行任务无论是在SISD还是MIMD多核器件上运行其取址、译码操作都是有冗余的

对于数据级并行场景,如何设计一种新型的处理器?

Slide 4-10: SIMD处理器

更多的ALU (Execute)更少的Fetch和Decode

更少的Fetch和Decode甚至其他流水部件意味着什么——更少的器件更低的能耗和时间开销
更多的ALU意味着什么——同时能处理更多数据速度更快
增加数据寄存器的数量来一次存储更多数据,以减少存储器访问延迟

Slide 4-11: 单指令多数据SIMD处理

一条指令处理多个不同的数据
有多个处理单元PEProcessing Elements

阵列处理机:同一指令在同一时间处理不同空间上的不同数据元素
向量处理机:同一指令在连续的时间内在同一空间上处理不同的数据元素

Slide 4-12: 数据级并行两种执行方式

阵列处理机vs向量处理机
ARRAY PROCESSOR
VECTOR PROCESSOR

LD0 LD1 LD2 LD3
AD0 AD1 AD2 AD3
MU0 MU1 MU2 MU3
ST0 ST1 ST2 ST3

LD0 LD1
AD0 AD1
MU0 MU1
LD2 LD3
AD2 AD3
ST0 ST1
MU2 MU3
ST2 ST3

Space
Space
Same op @ same time
Different ops @ same space
Different ops @ time
Same op @ space

LD     VR  A[3:0]
ADD  VR  VR, 1 
MUL  VR  VR, 2
ST     A[3:0]  VR
Instruction Stream

Slide 4-13: 向量体系结构和GPU

向量体系结构
"窄而深"
指令流水线深ALU宽度窄
单次指令流水后能处理更多数据,掩盖不必要的流水线时间

GPU
"宽而浅"
指令流水线浅ALU宽度宽
流水本身比较简单,直接对更多的数据进行并行计算,同一时刻处理更多数据

Slide 4-14: SISD、MIMD与SIMD

SISD、MIMD与SIMD阵列处理机、SIMD向量处理机的时间对比图

Slide 4-16: 向量

科学计算领域的很多问题需要处理大批量操作重复且互相没有关联的计算
向量:一组由一维数组所组成的数据
标量:单个数据
在流水线处理机中,设置向量数据表示及相应的向量指令,称为向量处理机。
不具有向量数据表示和相应的向量指令的流水线处理机,称为标量处理机。

Slide 4-17: 向量处理方式

Slide 4-18: 横向(水平)计算

向量计算是按行的方式从左到右横向地进行。
先计算:   d1←a1×(b1c1)
再计算:   d2←a2×(b2c2)
                ……
最后计算: dN←aN×(bNcN)
组成循环程序进行处理。
      ki←bici
      di←ki×ai
数据相关N次   功能切换2N次
不适合于向量处理机的并行处理。

Slide 4-19: 纵向(垂直)处理方式

向量计算是按列的方式从上到下纵向地进行。
         k1←b1c1             d1←k1×a1
先计算   ……        再计算    ……
         kN←bNcN             dN←kN×aN
表示成向量指令:
   KBC
   DK×A
两条向量指令之间:
   数据相关1次   功能切换1次
适合于向量处理机的并行处理。

Slide 4-20: 纵向处理方式

纵向处理方式N的大小不受限制
每次计算都需要访问到向量中的全部元素
纵向处理方式对处理机结构的要求:存储器-存储器结构
向量指令的源向量和目的向量都存放在存储器中,运算的中间结果需要送回存储器。

Slide 4-21: 纵横 (分组)处理方式

当前计算机体系结构的存储结构往往是层次化的指令操作数一般都会加载到寄存器中而寄存器的数量一般不会太多相比于可以无限增长的向量长度N来说
分组处理方式:把向量分成若干组,组内按纵向方式处理,依次处理各组。
对于上述的例子,设:
       N=S×nr
其中N为向量长度S为组数n为每组的长度r为余数。
若余下的r个数也作为一组处理则共有S+1组。

Slide 4-22: 纵横 (分组)处理方式

运算过程为:
先算第1组
      k1n←b1nc1n
      d1n←k1n×a1n
再算第2组
          k(n+1)2n←b(n+1)2nc(n+1)2n
      d(n+1)2n←k(n+1)2n×a(n+1)2n
依次进行下去直到最后一组第S+1组。
每组内各用两条向量指令。
     数据相关1次   功能切换2次
适合于向量处理机的并行处理。

Slide 4-23: 纵横处理方式

纵横处理方式N的大小不受限制但n值固定
对处理机结构的要求:寄存器-寄存器结构
设置能快速访问的向量寄存器,用于存放源向量、目的向量及中间结果,让运算部件的输入、输出端都与向量寄存器相联,构成寄存器-寄存器型操作的运算流水线。
典型的寄存器-寄存器结构的向量处理机 
      美国的CRAY-1、我国的YH-1巨型机

Slide 4-24: 向量体系结构Vector Architecture

向量体系结构应当具有很大的顺序寄存器堆 (Register File),可加载更多向量元素以支持纵向计算
向量体系结构从内存中收集散落的数据将其放入寄存器堆中并对寄存器堆中的数据们进行操作然后将这些结果放回内存一次传输一组数据LD/ST流水化
一条指令能够对一个向量的数据进行操作,也就对向量中诸多独立数据元素进行了操作(纵向计算,功能单元流水化)

Slide 4-25: 功能部件流水化

把流水线技术应用于运算的执行过程,就形成了运算操作流水线,也称为部件级流水线。
例如:浮点加法流水线
把浮点加法的全过程分解为求阶差、对阶、尾数相加、规格化4个子过程。

Slide 4-26: 向量体系结构的一些优势

由于向量的Load与Store是深度流水线化的大型寄存器堆充当了缓冲的作用因此其能够掩盖访存延迟并充分利用内存带宽
乱序的超标量处理器往往具有复杂的设计,且乱序程度越高,其复杂性和功耗也会越高,在此方向发展很容易触及功耗墙;
将顺序的标量处理器扩展为向量处理器则不会带来复杂度和功耗的大幅升高,且开发者也能很容易适应和转换到向量指令。

Slide 4-27: 实例VMIPS=标量MIPS+逻辑向量扩展

向量寄存器:
64bit*64元素*8个VR足够多读写口
向量功能单元:
全流水化每周期1操作
需要一个控制单元检测结构冒险和数据冒险
向量load/store单元
全流水化每周期1个字可操作标量
标量寄存器集合:
32通用32浮点可存地址和数据
可以为向量功能单元提供数据也可以为向量load/store单元提供地址

Slide 4-28: 向量专用特殊寄存器

向量长度寄存器VL
表示向量的长度值≤64。
VL控制所有向量运算的长度包括ld/st
作用将软件层程序中实际向量长度N与硬件层向量寄存器中的元素数目64相适配
向量屏蔽寄存器VM
当向量长度小于64时或者条件语句控制下对向量某些元素进行单独运算时使用
即使maskcode中有大量的0使用VM的向量指令速度依然远远快于标量计算模式。

Slide 4-29: VMIPS向量指令格式

VMIPS指令=MIPS指令+Op1类型+Op2类型.精度
mipsadd.d
vmipsaddvv.d(V1,V2,V3)addvs.d(V1,V2,F0)

Slide 4-30: 代码VMIPS vs MIPS

a*X[i]+Y[i]

MIPS代码
L.D  F0,a     ;载入标量
  DADDIU  R4,Rx,#512   ;载入最后地址
LOOP: L.D  F2,0(Rx)    载入X[i]
  MUL.D  F2,F2,F0    a * X[i]
  L.D  F4,0(Ry)    载入Y[i]  
  ADD.D  F4,F4,F2    载入a * X[i] + Y[i]
  S.D  F4,0(Ry)    存入Y[i]
  DADDIU     Rx,Rx,#8    递增X
  DADDIU  Ry,Ry,#8    递增Y
  DSUBU  R20,R4,Rx   ;计算范围
  BNEZ  R20,LOOP   ;检查是否完成

VMIPS代码
L.D   F0,a     载入a
LV   V1,Rx     载入X
MULVS.D V2,V1,F0     a x X
LV   V3,Ry     载入Y
ADDVV.D V4,V2,V3     +Y
SV   Ry, V4    存入Y

减少的仅仅是代码的行数么?
不只是行数,减少了指令条数,大量节约取指令和指令译码所消耗的时间

Slide 4-31: 向量体系结构的相关概念

循环间相关:对一个循环来说,如果各轮迭代之间存在相关性,则称为循环间相关,否则为循环间无关
可向量化针对一组MIPS指令描述的循环如果满足循环间无关则循环称为可向量化的编译器可为其生成向量指令。
指令编队convoy由一组不包含结构冒险的向量指令组成一个编队中的所有向量指令在硬件条件允许时可以并行执行。

Slide 4-32: 向量体系结构的性能优化

多车道技术
链接技术
编队技术
分段开采技术

Slide 4-33: 多车道技术

刚刚讨论的是向量计算在串行化中尽可能进行优化的结果
从并行的角度去考虑增加功能单元ALU的数量也能大大提升向量的计算速度多车道
这就好像,将一条单车道的窄巷扩宽成四车道的公路,吞吐量自然会提升

Slide 4-34: 链接技术

链接技术:当两条指令出现"写后读"相关时,若它们不存在功能部件冲突和向量寄存器(源或目的) 冲突,就有可能把它们所用的功能部件头尾相接,形成一个链接(长)流水线,进行流水处理。
链接过程无链接情况下后面的功能需要等到前一个功能的n个结果都产生才能开始而链接情况下后面的功能只需要等到前一个功能的第一个结果产生就可以开始即向量数据的生产与向量数据的消费进行延迟的重叠。
链接实质:把流水线定向的思想引入到向量执行过程,对两条流水线进行联合控制,没有改变寄存器和运算电路。
以下的讨论中假设各个部件之间传递一个结果需要一拍时间。

Slide 4-35: 冲突例子

Vi冲突并行工作的各向量指令的源向量或结果向量使用了相同的Vi。
例如:源向量相同
     V3←V1V2
     V5←V4∧V1
功能部件冲突:并行工作的各向量指令要使用同一个功能部件。
例如:都需使用乘法功能部件
     V3←V1×V2
     V5←V4×V6

Slide 4-36: 链接技术例题

假设功能单元的时间开销为:浮点加减(6 cycle),浮点乘法(7 cycle),浮点存储操作(6 cycle)。为了同步要求将向量元素送往功能部件以及把结果存入向量寄存器需要一拍时间从存储器中把数据送入访存功能部件也需要一拍时间。为以下程序画出链接示意图并分析非链接执行和链接执行两种情况下的执行时间。假设向量长度为NN≤64。
 	V3 ← 存储器	// 访存取向量A
	V2 ← V0  V1  // 向量B和向量C进行浮点加
	V4 ← V2 × V3	// 浮点乘结果存入V4

Slide 4-37: 链接技术例题解答

1. 三条指令全部用串行方法执行,则执行时间为:
           [161N1][161N1]
                   [171N1] = 3N 22 (拍)

2. 前两条指令并行执行然后再串行执行第3条指令则执行时间为
	      [161N1][171N1] = 2N 15 (拍)

3. 前两条指令并行执行并与第3条指令链接执行。
从访存开始到把第一个结果元素存入V4所需的拍数
     (亦称为链接流水线的建立时间)为:
		      [161] [171] = 17 (拍)
生成所有结果的执行时间为:
        17 N1 = N16 (拍)  解毕。

Slide 4-38: 进行向量链接的要求

保证:无向量寄存器使用冲突和无功能部件使用冲突
只有在前一条指令的第一个结果元素送入结果向量寄存器的那一个时钟周期才可以进行链接。
当一条向量指令的两个源操作数分别是两条先行指令的结果寄存器时,要求先行的两条指令产生运算结果的时间必须相等,即要求有关功能部件的通过时间相等。
要进行链接执行的向量指令的向量长度必须相等,否则无法进行链接。
一次链接行为通常仅发生在分组内部即不对整个N进行链接而对个分组内的n个向量元素的计算过程进行链接

Slide 4-39: 编队技术

几条能在同一个时钟周期内一起开始执行的向量指令集合称为一个编队;
同一个编队中的向量指令之间
不存在结构冲突;
不存在数据冲突;
存在数据冲突,但是可以链接。

Slide 4-40 ~ 4-41: 编队技术例题

LV		V1,Rx		从存储器中载入向量Rx
MULTSV		V2,R0,V1	向量V1和标量寄存器R0中的内容相乘
LV		V3,Ry		从存储器中载入向量Ry
ADDV		V4,V2,V3	向量V2和V3相加结果保存在向量寄存器V4中
SV		Ry, V4		将结果存入Ry

假设每种向量功能部件只有一个,下面一组向量指令,在不使用链接技术和使用链接技术的情况下如何编队?

写后读,通过链接可编入同一编队
结构冒险,
需要放入新编队
写后读,通过链接可编入同一编队
结构冒险,
需要放入新编队
编队
Ry=R0*Rx+Ry

使用链接技术分为3个编队
第一编队LVMULTSV
第二编队LVADDV
第三编队SV

解不使用链接技术分为4个编队
第一编队LV
第二编队MULTSV LV
第三编队ADDV
第四编队SV

Slide 4-42: 分段开采技术

当向量的长度N大于向量寄存器的长度n时必须把长向量N分成长度固定为n的段然后循环分段处理每一次循环只处理一个向量段。这种技术称为分段开采技术。
由系统硬件和编译软件合作完成控制,对程序员是透明的。

Slide 4-43: 向量体系结构的性能影响因素

操作数向量的长度
向量启动时间
操作之间的数据相关,是否采用链接
操作之间的结构性相关,发射限制,车道数量,是否采用编队

Slide 4-45: GPU

GPU=Graphics Processing Unit图形处理器
具有极高的计算吞吐率和内存带宽
专用图形图像处理器
适合纹理图像处理

GPGPU=General-purpose computing on GPUs通用图形处理器
可进行通用计算编程例如CUDA, OpenCL
适合处理SIMD程序

Slide 4-46: CPU vs GPU

多核 vs 众核:几个核 vs 上千个核
GPU将芯片中更多的晶体管用于计算

Slide 4-47: GPU处理模式

流水线 vs 并发执行

Slide 4-48: SPMD编程模型

Single Program Multiple Data (SPMD)单程序多数据
程序采用多线程的模型,而非向量指令
与SIMD编程模型不同
不同的进程/线程运行同一个程序源代码SP但是分别使用不同的输入数据进行各自的计算MD
不同进程/线程相互独立,没有执行顺序的要求

常用的并行编程模型多数采用SPMD模式
例如CUDAMPIopenMP

Slide 4-49: GPU编程模型

CUDA=Compute Unified Device Architecture, 统一计算设备架构
由NVIDIA提出的通用并行计算平台和编程模型
CUDA软件环境使用C++作为高级编程语言
采用SPMD编程方式

Slide 4-50: 异构编程

Host
主机(CPU)
运行C++程序
Device
物理上分离的协处理器(GPU)
运行kernel程序
运行thread
运行CUDA目标代码

Slide 4-51: CUDA程序示例

向量加法
// Kernel definition
__global__ void VecAdd(float* A, float* B, float* C)
{
int i = threadIdx.x;
C[i] = A[i] + B[i];
}
int main()
{
...
// Kernel invocation with N threads
VecAdd<<<1, N>>>(A, B, C);
...
}

Slide 4-52: Host-Device数据传输

Host代码
… Allocate h_A, h_B, h_C …

void vecAdd(float *h_A, float *h_B, float *h_C, int n)
{
    int size = n * sizeof(float); float *d_A, *d_B, *d_C;

    cudaMalloc((void **) &d_A, size);    
    cudaMalloc((void **) &d_B, size);
    cudaMalloc((void **) &d_C, size);

    cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);
    cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice);
     
     // Kernel invocation code  to be shown later

     cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost);
     cudaFree(d_A); cudaFree(d_B); cudaFree (d_C);
}

… Free h_A, h_B, h_C …

Slide 4-53: GPU执行方式

Single Instruction Multiple Thread单指令多线程

SIMD与多线程相结合
线程按照固定的方式运行与传统SPMD不同
固定个数的线程一起执行SIMD
使用标量指令与传统SIMD不同

Slide 4-54: GPU中处理单元的执行方式

Single Instruction Multiple Data (SIMD)

Slide 4-55: SPMD, SIMT, 和SIMD

SPMD
GPU的编程模型
SIMT
GPU的执行方式
SIMD
GPU计算单元的处理方式

Slide 4-56: SIMT的优点

编程灵活
任意大小的工作量vs任意硬件SIMD宽度
每个线程可以被单独对待
分支控制
Warp细节对程序员透明
Warp大小
哪些线程由同一个warp执行

Slide 4-57: NVIDIA H100 GPU

16896 FP32 CUDA cores
66.9 TFLOPS (FP64 Tensor core)
33.5 TFLOPS (FP64)
3352 GB/s memory bandwidth
700W TDP
TSMC's 4N customized for NVIDIA

Slide 4-64: GPGPU微体系结构

线程以warp为单位进行调度
同一warp中的不同线程按照SIMT方式执行
大容量寄存器组用于存放所有线程的数据
SPStreaming Processor进行数据的并行计算i.e., ALU

标量指令处理
SIMD数据访问及计算

Slide 4-67: Warp

在Nvidia GPU中warp为32个标量线程构成的SIMT执行单位
GPU SM中以warp为最小单位进行调度和执行
GPU无法控制单独执行某一个thread
Warp中不同的Thread在执行时保持同步
即所有Thread永远执行相同PC所对应的指令

Slide 4-68: 通过细粒度多线程并行掩盖延迟

细粒度多线程执行

Slide 4-69: Warp调度

LD R1, 0x00027    PC=1
LD R2, 0x00034    PC=2
ADD R3, R1, R2    PC=3
ST R3, 0x00045    PC=4

Cycle 100: 调度 warp0 PC1
Cycle 101: 调度 warp1 PC2
Cycle 102: 调度 warp2 PC1
…

如果访存时间长则PC3对应的指令需要等待GPU将大量warp交替调度执行可以一定程度上掩盖长时间访存

Slide 4-70: Warp执行

32-thread warp executing ADD A[tid],B[tid] → C[tid]

Slide 4-72: Streaming Processor

CUDA core
SIMDSingle Instruction Multiple Data执行
大量SP提供极高并行算力

Slide 4-73: Warp指令级的并行执行

可以将不同指令交替执行
假设机器中有8个通道1个warp中有32个线程
每周期完成24个操作但是每周期流出1个warp

Slide 4-74: SIMT的访存模式

执行同一指令的不同线程使用线程ID来访问不同的数据
假设N=16, 每个warp中有4个线程 → 共有4个warp

Slide 4-75: Coalescing

将一个warp中不同thread的内存访问合并成更少的访问次数
如果一个warp中的32个thread访问内存中连续的4B大小的位置
合并成一个128B的访存请求(coalescing)
而不是发送32个4B的访存请求 
有效降低SM和DRAM之间的访存次数
减少片上网络、片上存储划分、DRAM的工作量

Slide 4-77 ~ 4-79: GPU程序层次模型

一个程序kernel grid由多个block组成
例如256个block
一个block由多个thread组成
例如1024个thread
Thread为标量线程执行完整的程序源代码

一个kernel以block为单位分配到硬件中
多个block可以分配到同一个SM中只要有足够的硬件资源

在SM内部来自同一个block中的不同线程编号相邻组成一个warp
例如1 warp = 32 thread
Block和thread对程序员可见warp对程序员不可见由硬件进行控制
同一kernel中的所有线程都共享相同的代码
不同线程执行进度可能不同
但是同一warp中的线程一同执行
可以在Block级别进行同步

Slide 4-80: GPU程序层次例子

要处理所有元素的代码在一个grid中
将一个grid分割成可处理的大小Block
例如一个block中有512个线程
一次以SIMD方式处理32个元素

Block与向量处理循环中的向量长度为32的分段开采类似

Slide 4-82: GPU与传统SIMD的区别

传统SIMD采用单线程
向量指令之间锁步执行:一个向量指令完成后后续指令才能开始执行
编程方式为SIMD软件需要知道向量长度
ISA中有向量/SIMD指令

GPU以SIMD方式执行大量标量线程
可以非锁步执行
每个线程可以被独立看待,由硬件进行线程组合(Warp)
编程方式为SPMD
ISA是标量的

Slide 4-84: Nvidia ISA

"Parallel Thread Execution (PTX)"
NVIDIA编译器的指令集目标时硬件指令集的一种抽象可实现各代GPU的兼容性
opcode.type d,a,b,c;
使用虚拟寄存器
由软件将其翻译成机器语言

Slide 4-86: 分支处理

GPU分支硬件也使用掩码类似向量处理机中的向量屏蔽寄存器
此外,还使用硬件栈处理分支
分歧后push
汇聚后pop

Slide 4-87: 例子(分支处理)

if (X[i] != 0)	
		X[i] = X[i]  Y[i];
	else X[i] = Z[i];

	ld.global.f64	RD0, [X+R8]		; RD0 = X[i]
	setp.neq.s32	P1, RD0, #0		; P1 is predicate register 1
	@!P1, bra	ELSE1, *Push		; Push old mask, set new mask bits
					; if P1 false, go to ELSE1
	ld.global.f64	RD2, [Y+R8]		; RD2 = Y[i]
	sub.f64	RD0, RD0, RD2		; Difference in RD0
	st.global.f64	[X+R8], RD0		; X[i] = RD0
	@P1, bra	ENDIF1, *Comp		; complement mask bits
					; if P1 true, go to ENDIF1
ELSE1:		ld.global.f64 RD0, [Z+R8]	; RD0 = Z[i]
			st.global.f64 [X+R8], RD0	; X[i] = RD0
ENDIF1: 	<next instruction>, *Pop		; pop to restore old mask

*Push *Comp *Pop为由PTX汇编程序插入的分支同步标记

Slide 4-88 ~ 4-89: 分支分歧(branch divergence)

同一warp中的不同thread在遇到分支指令时可能会有分歧
例如if-else
分支分歧当同一warp中的不同线程在遇到分支指令时执行不同的路径

GPU使用简化的控制逻辑来减少控制部分所占的面积
thread无法单独控制和调度
并行的处理单元同一个时钟周期只能处理相同的操作

Slide 4-90 ~ 4-92: 分支分歧处理

每个warp用一个栈来处理分支分歧
分支指令中可以加入一个汇聚指令域

A: v = foo[tid.x];
B: if (v < 10) 
C:    v = 0;
   else
D:    v = 10;
E: w = bar[tid.x]+v;

foo[] = {4,8,12,16};
One stack per warp

每个warp使用一个栈来处理分支分歧的执行

Slide 4-93: 存储层次

Local memory
一个thread
Shared memory
一个block中的所有thread
Global memory
所有thread

Texture memory
只读所有thread
适合特定地址访问模型
Constant memory
只读所有thread

Slide 4-94: GPGPU Virtualization虚拟化

企业级做云计算时常常采用虚拟化技术:
一个GPU的SM很多但是对于单用户只能用很少的SM,这样利用率很低;
硬件上把多个SM进行分割成多个切片
软件上为每个用户分配一个SM切片
使得每个用户都能满负荷使用SM;
每个用户感觉自己拥有了一个GPU。