体系结构复习3——数据级并行

体系结构复习 CH6 数据级并行


6.1 数据级并行DLP和SIMD

数据级并行(Data Level Parallel,DLP)是指处理器能够同时处理多条数据,属于SIMD模型,即单指令流多数据流模型

继续挖掘传统ILP的缺陷:

  • 提高流水线时钟频率可能导致CPI增加
  • 每个时钟周期很难预取和译码多条指令
  • 大型科学计算、媒体流处理局部性较差,Cache命中率低

并且SIMD模型有以下优点:

  • SIMD可有效挖掘DLP,如矩阵运算、图像声音等多媒体数据处理
  • SIMD比MIMD更节能,对于一组数据相同操作只需要取一次指令
  • (重要)SIMD允许程序员串行思维(串行算法应用到并行体系结构)

因此近年来DLP发展迅速,特别是在超级计算机领域


6.2 向量体系结构

DLP思想:一条指令处理长度为N的一组向量元素的计算

6.2.1 VMIPS

书上引入VMIPS向量体系结构作为MIPS体系结构的向量扩展

(1)VMIPS主要组件

  • 向量寄存器:8个向量寄存器,每个向量寄存器保存64个元素,每个元素的宽度是64位
  • 向量功能单元:包括浮点加/减、浮点乘、浮点除、整数运算、逻辑运算等向量功能单元(流水化)
  • 向量载入/存储单元:从存储器中载入向量或将向量存储到存储器中(流水化)
  • 标量寄存器:即MIPS基础的32个通用寄存器和32个浮点寄存器

(2)VMIPS主要指令

向量指令后缀VV表示两个向量寄存器操作、VS表示一个向量寄存器和一个标量寄存器操作(交换顺序是SV),.D仍然是双精度浮点的后缀,常用指令用:

ADDVV.D     V1,V2,V3
ADDSV.D     V1,F0,V2
MULVS.D     V1,V2,F0

LV          V1,R1
SV          R1,V1

(3)VMIPS程序示例

DaXPY是Linpack的某项标准测试,表示双精度a乘以向量X加上向量Y这一计算过程,用VMIPS表示为:

L.D         F0,a
LV          V1,Rx
MULVS.D     V2,V1,F0
LV          V3,Ry
ADDVV.D     V4,V2,V3
SV          Ry,V4

如果没有循环间相关,编译器可以为一段程序序列生成其VMIPS代码,称这段程序是可向量化的(可以用类似之前提到的代码调度消除循环间相关再向量化)

6.2.2 一些术语

  • 护航指令组Convoy:不包含任何结构冒险的可以在同一周期开始执行的向量指令集合
  • 链接Chain:允许向量操作在其向量源操作数的各个元素可用时立即启动,链接中第一个功能单元的结果被转发给第二个功能单元
  • 钟鸣Chime:执行护航指令组所话费的时间单位;执行m个护航指令组需要m次钟鸣,向量长度为n时需要花费m x n个时钟周期
  • 多车道Multi-Lane:多个功能单元来提高性能;单车道1条向量指令需要64周期完成,若变成4车道则只需16周期完成

6.2.3 条带挖掘——处理长度不为64的循环

若处理下面DaXPY循环:

for (i = 0; i < n; i++) {
    Y[i] = a * X[i] + Y[i];
}

其中n可以不等于向量寄存器最大长度MVL(VMIPS中MVL=64)也可以是根据前面程序动态确定的,此时需要引入一个向量长度寄存器VLR来实现条带挖掘版本的循环来适应MVL

VLR控制所有向量操作(包括运算和载入存储)的向量长度,当然VLR不能超过MVL

此时可以根据MVL把数据划分为条状,一条一条的处理,称之为条带挖掘

low = 0;
VL = n % MVL;

for (j = 0; j < n / MVL; j++) {
    for (i = low; i < low + VL; i++) {
        Y[i] = a * X[i] + Y[i];
    }
    low += VL;
    VL = MVL;
}

上述划分策略为先处理长度不足MVL的单个条带,再处理n / MVL - 1条长度为MVL的整齐条带

6.2.4 遮罩寄存器——处理向量循环中的if语句

如果可向量化的循环中存在if语句,那么有些向量元素执行操作而有些不执行,难以直接向量化:

for (i = 0; i < n; i++) {
    if (X[i] != 0) {
        X[i] -= Y[i];
    }
}

VMIPS中引入向量遮罩寄存器VM来处理if语句,VM用测试语句来测试每位向量元素,元素X[i]满足条件时置VM[i] = 1,否则置0(遮罩寄存器默认全1)

在遮罩寄存器的遮罩下,后续的向量指令只有VM[i] == 1的对应元素才执行

基本块的代码为:

LV          V1,Rx
Lv          V2,Ry
L.D         F0,#0
SNEVS.D     V1,F0       ; 若V1[i] != F0,则置VM[i]为1否则为0
SUBVV.D     V1,V1,V2
SV          V1,Rx

6.3 GPU和CUDA

见另外两篇博文:

  • CUDA编程入门:向量加法和矩阵乘法: http://blog.csdn.net/u014030117/article/details/45952971
  • 并行编程:http://blog.csdn.net/u014030117/article/details/46444247

6.4 循环级并行

第五章中介绍过循环依赖(循环间相关)和消除循环依赖的办法,循环级并行不仅要消除循环间相关还需要消除名称相关(输出相关、反相关)

6.4.1 GCD测试检测循环间相关

GCD(最大公约数)能够检测一个循环中,对数组的两次访问是否存在相关

假设一个循环中,以索引a * i + b存储一个数组元素,并以索引c * i + d载入同一个数组中的元素,GCD测试表示为:

若GCD(a,c)能够整除d-b,则存在循环间相关

如循环为X[2 * i + 3] = X[2 * i] * 5.0;GCD(a,c) == 2d-b == -3不能整除即不存在循环间相关

GCD测试最大公约数计算就是为了检测某一次循环中load的元素会不会是其他某次循环中store的元素

注:由于GCD测试没有考虑循环范围,因此某些时候GCD测试表明存在相关但实际却没有(比如刚好重合的时候已经不在数组界限范围内了),但可以证明:GCD测试能够确保不存在相关

6.4.2 消除名称相关

消除名称相关的主要方法还是之前介绍的重命名方法,如下面的循环存在输出相关和反相关:

for (i = 0; i < 100; i++) {
    Y[i] = X[i] / c;
    X[i] = X[i] + c;
    Z[i] = Y[i] + c;
    Y[i] = c - Y[i];
}

重命名为:

for (i = 0; i < 100; i++) {
    T[i] = X[i] / c;    // Y -> T,消除输出相关
    X1[i] = X[i] + c;   // X -> X1,消除反相关
    Z[i] = T[i] + c;    // Y -> T,消除反相关
    Y[i] = c - T[i];
}

你可能感兴趣的:(体系结构,体系结构,数据级并行,VMIPS,循环间相关,GCD测试)