计算机体系结构技术杂谈(上)
2.1 计算机的层次结构
2.1.1基本概念介绍
1. 计算机基本概念
1) 机器数:用0和1编码的计算机内部的0/1序列。
2) 真值:机器数真正的值,即:现实中带正负号的数(通常指带符号二进制数对应的真正的数值)。
3) 定点数:将一个实数表示为带有固定小数点位置的整数,这个小数点位置是在数
值中的固定位置上的一个约定。它不像浮点数那样具有动态的小数点位置。
4) 原码:最高位为符号位,0表示正1表示负,剩余位表示数值。
5) 补码:最高位为符号位,0正1负,正数时和原码相同,负数为原码最高位不变,其余按位取反加一;对于小数,对整数部分按原规则取补,小数部分仍然保持小数的二进制(比如-8.25中-8表示为1000,0.25表示为0.01,拼接以后为1000.01,注意小数直接用原码表示即可,正负值不用转换,具体看例1);补码转原码时不需管符号位,直接所有位取反然后加一即可。
6) 无符号数:unsigned int ( short / long)
7) 带符号整数: int ( short / long)
8) 定点数:表示实数,其中小数点的位置在数值中是固定的,小数点的位置不会改变。
9) 浮点数:点数采用科学计数法,将实数表示为指数和尾数的形式,通过移动小数点来表示不同数量级的数值。
2. 逻辑电路基本概念介绍
1) CMOS管:互补金属-氧化物半导体,包含有N管和P管,两者互补保证时刻都有导通。
2) N管:门电压高时打开(导通输入端S和输出端D),低时关闭(图形表示上为两个平行线段)。
3) P管:门电压低时打开(导通输入端S和输出端D),高时关闭,PN性质相反(一
般图形表示上有小圆圈前置连接)。
4) 反相器:P管接电源,N管接地,他们的输入同为一个输入,输出也为同一,可以
以此构输入高时输出0,输入接地时输出电源电压(视为输出1)。
3. 浮点数的表示(IEEE 754标准)
1)定点数的不足:表示范围有限,太大或太小的数都不能表示、除法不精确。浮点数在统一之前表示方法各异,Rahan教授设计了 IEEE 754标准,并因此获得了诺贝尔奖。
2)浮点数由3部分组成:符号位、阶码、尾数。
3)浮点数表示形式: 符号尾数阶码偏移值一个固定值符号×尾数𝑓×2(阶码𝑒−偏移值(一个固定值)),其中尾数的形式为1.*,为了节省空间,把小数点前那一位统一表示为1(如果前边是0确实不用表示,在二进制中只有第一个1需要表示,而小数点的位置用阶码约束,这样就能表示很大的范围,只需要考虑去掉原码中的小数点以后非0且在第一个1后边的数字的表示),这也是为什么尾数可以表示的范围会更大。浮点数的表示(IEEE 754标准),见表2-1。
表2-1 浮点数的表示(IEEE 754标准)
4. IEEE 754 浮点格式参数
IEEE 754 浮点格式参数,见表2-2。
表2-2 IEEE 754 浮点格式参数
S
|
指数Exp(8)
|
小数部分(23)
|
|
|
D
|
指数Exp(11)
|
小数部分(52)
|
|
|
|
|
|
|
32位与64位浮点数表示中各位数含义(S符号、Exp阶码、Fra尾数)
浮点数的三种情况:阶码 e 的范围为 0<𝑒<255 并且当 e 为0或255时为特殊情况 当 e 处于 1~254 时表示正常的数
1)当 e 处于 1~254 时,表示规格化数,因为偏移量为127,所以阶码的移码 (e-127) 的范围为 -126~127,尾数 f 是一个非 0 的数,经过规格化后,最高位的 1 被省略,所以如果符号位为0,那么浮点数为: 如果符号位为1,那么浮点数为: 。浮点格式参数(IEEE 754标准),见表2-3。
表2-3 浮点格式参数(IEEE 754标准)
IEEE 754 浮点格式参数,说明如下:
1)把 Singaling NaN 当作操作数引起例外,把 Quiet NaN 当作操作数结果也为 Quiet NaN,可以用尾数部分区分它们。
2)非规格化数用于阶码下溢的情况,填补最小数和 0 之间的空隙。
5. C语言中的浮点数类型
1)C语言中有float和double类型,分别对应IEEE 754单精度 浮点数格式和双精度浮点数格式。
2)long double类型的长度和格式随编译器和处理器类型的 不同而有所不同,IA-32中是80位扩展精度格式。
3)从int转换为float时,不会发生溢出,但可能有数据被舍入。
4)从int或 float转换为double时,因为double的有效位数更多,故能保留精确值
5)从double转换为float和int时,可能发生溢出,此外,由于有效位数变少,故可能被舍入。
6)从float 或double转换为int时,因为int没有小数部分,所以数据可能会向0方向被截断。
2.1.2 数的表示
1. 补码运算
加法溢出判断:A和B的最高位一样,且结果的最高位与A和B的最高位不一样(就两个都是1加了以后变成0了呗),下述例子中的符号位是最高位,显然
1)1001+0101(-7+5)=1110, 1100+0100(-4+4)=0000
2)0011+0100(3+4)=0111, 1100+1111(-4-1)=1011(未溢出)
3)0101+0100(5+4)=1001, 1001+1010(-7-6)=0011(溢出)
4)位原码位补码位原码位补码18−188位原码00010010100100108位补码000100101110111016位原码0000000000010010100000000001001016位补码00000000000100101111111111101110
补码运算示例,见表2-4。
表2-4 补码运算示例
|
18
|
-18
|
8位原码
|
00010010
|
10010010
|
8位补码
|
00010010
|
11101110
|
16位原码
|
0000000000010010
|
0000000000010010
|
16位补码
|
0000000000010010
|
1111111111101110
|
2. 浮点数转换与比较
1)8.0,-8.25 在计算机中怎么表示和参与运算的?
定点数表示:由于小数在定点数中的表示并没有一个统一的约束,所以定点数表示暂且不说。
2) 单精度表示 (32位)
8.0:转化为二进制为 1000.0= = 因为尾数首位1可以省略,所以尾数f=0,阶码e=130,所以二进制表示为首位0,接着8位为 1000 0010 (128+2),最后23位为 0,整体表示为 0{100 0001 0}{000 0000 ~ 0000 0000 0000 0000}
-8.25:转化为二进制为 −1000.01=1.00001×23=1.00001×2130−127 尾数首位1省略,尾数f=0,阶码e=130,二进制表示为首位1,接着8位为 1000 0010 (128+2),最后23位为0000 1...,整体表示为 1{100 0001 0}{000 0100 ~ 0000 0000 0000 0000}
浮点数的加运算为如下流程,以8.0-8.25为例,这是因为:
①第一步,找出指数较小的数。
②第二部,使两个数的指数相同。
③第三步,尾数相加。
④第四步,如果有必要,将结果规格化。
3) 双精度表示(64位)
与单精度类似,只是各个类型码的尾数有变化。
3. 补码比较(是否带符号)
若同时有无符号和带符号整数,则C编译器将带符号整数强制转换为无符号数。所以下属的例外基本是本问题产生的,总结就是当无符号和有符号的数进行比较时,如果两个数的正负号不同,就会出现错误。
有无符号和带符号整数补码比较,见表2-5。
表2-5 有无符号和带符号整数补码比较
4. CPU从存储器中读出一个4字节信息D=BF400000H
1) 若D是一个32位补码表示的带符号定点纯小数,则其真值是多少?
H表示此数为16进制(以0x开头或以H结尾都代表此数为16进制),将其转换为二进制后为 1011 1111 0100 0000 ~ 0000 0000 0000 0000,32 位带符号定点纯小数,即符号位后面的位数代表小数部分,本数为负数,表示为-0.011 1111 0100 0000 ~ 0000 0000 0000 0000,小数点后取反加一得 -0.100 0000 11,计算得
。
(2) 若D是一个IEEE 754单精度浮点数,则其值是多少?
二进制为 1011 1111 0100 0000 ~ 0000 0000 0000 0000,前1位表示符号位,8位表示阶码,剩下23位表示尾数,移码固定为127,所以阶码为:0111 1110 (126),尾数为 100 0000 ~ 0000 0000 0000 0000(1.1000...),符号为负所以值为
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812182903307-1267939471.png)
。
5. 定点数的转换
1) 分别给出64位定点原码和补码表示的数的范围
定点原码和补码都表示整数,首位为符号位,其余为数值,但是补码比原码范围更大一点,因为原码有+0和-0,补码没有,比如 0000 和 1000,对 -0 取补码为 1000,和符号位相加后为 0000,同时对于补码中最小的负数 1000,转换为原码以后得到 - 1000,即 2^3,所以对于64位定点原码其表示范围为
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183025090-764411837.png)
,补码范围为
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183000101-1889747242.png)
2) 在32位定点补码表示中,0x8000 0000表示什么数
0x表示16进制数,所以其转换为二进制后为 1000 0000 0000 0000 ~ 0000 0000 0000 0000,负数取其补码为 -1000 0000 0000 0000 ~ 0000 0000 0000 0000,所以其值为
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183109237-1120001506.png)
6. 浮点数的转换
1) 把单精度数转化为十进制数:0x7ff 0000,0xbe40 0000,0xff80 0000
0x 7ff 0000 = 0000 0111 1111 1111 ~ 0000 0000 0000 0000 => 阶码e=0000 1111,尾数f=111 1111 0000 0000 0000 0000 =>
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183127343-1433009280.png)
2) 把双精度数转化为十进制数: 0x4035 0000 0000 0000,0x8008 0000 0000 0000
3) 把十进制数转化为单精度数: -100.0,0.25
-100.0:其中100.0转化为二进制得到 110 0100.0 = 1.1001 * = 1.1001 * => f=1001 ...,e = 133 = 1000 0101 => 所以单精度数为 0b1{100 0010 1}{100 1000 ~ 0000 0000 0000 0000} = 0xc2c8 0000
0.25:转化为二进制得到 0.01 = 1.0 * = 1.0 * => f=0(省略第一个1),e=125 = 0111 1101 => 所以单精度为 0b0{011 1110 1}{000 0000 ~ 0000 0000 0000 0000} = 0x3e80 0000
4) 把十进制数转化为双精度数: 1024.0,0.25
1024.0 = 1 0000 0000.0 = 1.0 *
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183257268-1470443486.png)
=1.0 *
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183411048-1369034652.png)
=> f=0,e=1033=100 0000 1001 => 0b0{100 0000 1001}{ 0000 ~ 0000 0000 0000 0000 ~ 0000 0000 0000 0000 ~ 0000 0000 0000 0000} = 0x4090 0000 0000 0000
0.25 = 0.01 = 1.0 *
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183435441-355023089.png)
=1.0 *
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183507580-1776735686.png)
=> f=0, e=1021=011 1111 1101 => 0b0{011 1111 1101}{0000 ~ 0000 0000 0000 0000 ~ 0000 0000 0000 0000 ~ 0000 0000 0000 0000} = 0x3fd0 0000 0000 0000
7. 浮点数范围
1) 单精度浮点数(float型)的表示范围多大
最大的数据: 重复次
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183649565-906982379.png)
但是阶码的最大值是非标准情况,所以要减去1得到
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183726616-929085804.png)
最小的数据:加个负号即可
最小精度:
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183747218-509653494.png)
双精度最大数据为 重复次
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812183826907-333311102.png)
8. 表达式真假
两个表达式示例,如图2-1所示。
图2-1 两个表达式示例
两个表达式解析如下:
(1) 对于第一个表达式,若有一个32位 int i 数值很大,这时候将其转换为二进制以后其有效位数超过了单浮点型尾数的23位限制,就会将最后的几个位数舍入,产生误差。但是如果使用Double时便不会,因为double的尾数为52,比32位更大(int一般是32位)
(2) 第二个表达式特例更多,当有小数位时必然会发生截断,产生不准确,因为截断先发生,所以就算用double也不准确。
9. 浮点数加法结合律是否正确
浮点数加法结合律的特例,如图2-2所示。
图2-2 浮点数加法结合律的特例
第二个结合律因为1和 大小差异过大,所以较小的数会被忽略,导致运算结果错误。
注意事项如下:
1)因为IEEE754标准的32位单精度浮点数的指数与尾数位于位于同一个字中,所以在加法过程开始之前必须将它们分离开。
2)如果两个指数的差大于p+1,p为尾数的位数,这里p=23,较小的数由于太小而无法影响较大的数,结果实际就等于较大的数。
3)结果规格化时检查指数范围,以分别检测指数下溢或上溢。指数下溢会导致结果为0,而指数上溢出会造成错误。
10. 浮点数比较
对于给定的关系表达式,判断是否永真,如图2-3所示。
图2-3 对于给定的关系表达式,判断是否永真
11. 精度问题
1)数据类型的表示范围
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184038716-170566955.png)
(2-1)
2)数据类型的表示精度
int:32位时精度为
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184116712-1524048134.png)
long:64位时精度为
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184136855-882573349.png)
float:32位中精度由尾数决定(阶码控制二进制表示中的小数点位置,只对范围有影响,对精度无影响),尾数有23位,加上省略后的一个位,精度为
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184211074-1802090334.png)
double:64位,尾数有52位,所以精度为
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184222883-1911898157.png)
所以总的精度排行为 long > double > int > float
12. 三输入与非门
已知三输入与非门,可写出真值表和逻辑表达式,当A=1,B=0,C=1 时,连线或非连线,如图2-4所示。
图2-4 三输入门与非门示意图
三输入与非门真值表为A B C F 11101101101110010111010100110001,见表2-6。
表2-6 三输入与非门真值表
A
|
B
|
C
|
F
|
1
|
1
|
1
|
0
|
1
|
1
|
0
|
1
|
1
|
0
|
1
|
1
|
1
|
0
|
0
|
1
|
0
|
1
|
1
|
1
|
0
|
1
|
0
|
1
|
0
|
0
|
1
|
1
|
0
|
0
|
0
|
1
|
即除了ABC全是1时输出为0,其他情况输出都为1。
逻辑表达式为: ~( A & B & C )
当ABC为101时,上边三个从左到右第二个,即中间的P管导通,下边的三个,上下两个导通。
2.2 量化设计与分析(CPI、MIPS计算)
2.2.1基本概念
1) 功率:指单位时间的功耗
2)能耗:指平均功率乘以工作负载的执行时间。能耗是处理器完成某个任务能源效率更好的度量。
3)CPI:每条指令的时钟周期数(平均的)
4)IPC:每时钟周期指令数
5)IC:指令数(指令路径长度)
6)程序的CPU时间:程序的CPU时钟周期数 * 时钟周期时间
2.2.2 CPI计算
程序的时钟周期数指令数CPI=程序的CPU时钟周期数/指令数(IC) 。
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184314694-1737947831.png)
(2-2)
1. MIPS比较
某程序在两台计算机上的性能测量结果,见表2-7。
表2-7 某程序在两台计算机上的性能测量结果
测量内容
|
计算机 A
|
计算机 B
|
指令数
|
100 亿
|
80 亿
|
时钟频率
|
4GHx
|
4GHx
|
CPI
|
1.0
|
1.1
|
2. 计算与比较示例
1) 哪台计算机的MIPS (每秒执行百万条指令数) 值更高?
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184353590-1285327876.png)
(2-3)
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184434960-761283807.png)
(2-4)
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184453223-1135220174.png)
(2-5)
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184518907-1477776356.png)
(2-6)
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184531201-1584598743.png)
(2-7)
![](https://img2024.cnblogs.com/blog/1251718/202408/1251718-20240812184554848-1834809658.png)
(2-8)
因此,具体计算一下。
执行时间秒执行时间𝐴=100×108×1.04×109=2.5秒
MIPS𝐴=100×1082.5×106=4000MIPS
执行时间秒执行时间𝐵=80×108×1.14×109=2.2秒
MIPS𝐵=80×1082.2×106≈3640MIPS
所以A计算机的MIPS更高。
(2) 哪台计算机更快?
尽管A的MIPS更高,但是因为计算机B的执行时间更少,所以计算机B更快。
2.3 存储器层次结构
2.3.1 存储器层次结构概述
1. Cache的作用
Cache结构与作用,如图2-5所示。
图2-5 Cache结构与作用
介绍一下Cache具有特征。Cache没有程序上的意义,只是为了降低访存延迟;处理器访问Cache和访问存储器使用相同的地址。
Tag存储cache块在主存中的首地址(cache每个字节都给一个地址太浪费,所以cache以块为单位存储数据)。
2. Cache的结构特点
同时存储数据和地址,通过地址的比较判断相应数据是否在Cache中;需要考虑所需要的数据不在Cache中的情况,替换机制,写策略等。例如处理器发送地址Add的高位和cache的地址标记进行比对,如果Add在cache中即为命中,从cache中返回数据,否则从主存读入数据块,返回数据给处理器的同时更新cache,更新cache时要考虑替换策略。Cache结构,如图2-6所示。
图2-6 Cache结构
cache有三种主要类型,直接相联、全相联、组相联,这代表了三种不同的内存和cache的映射方式,假设内存和cache都按照相同大小分块,其中内存一共分了32块,cache分了8块。接下来看看内存的每一项内容是如何放到cache中的。
Cache结构三种主要类型,如图2-7所示。
图2-7 Cache结构三种主要类型
1)直接相联:每项内存单元只能映射到cache的一个位置,假设要把内存的12号单元放到 cache中,因为 cache只有8个单元,12除以8余数为4,12号单元就只能放在4 号单元,别的地方都不能放。4,12,20,28 号都映射到这个单元,如果冲突了怎么办?那只有替换。这就是直接相联,硬件简单但效率低。
2)全相联:每个内存块都可以放到任何一个 cache行中,4号12号20号28号同时进来都放得下。全相联硬件复杂但效率高。
3)组相联:是直接相联和全相联的折中。以二路组相联为例,0、2、4、6号一路,1、3、5、7号一路,每路4个cache块;12除以4余数为0,既可以把12号单元放在0路的0号单元,也可以放在1路的0号单元(即1号单元)(注意路和组概念不同,组是将两路拆开成单个的,然后将两个两个组合组成了四组)。
3. Cache的优化技术统计
Cache的优化技术统计,见表2-8。
表2-8 Cache的优化技术统计
优化技术
|
时效延迟
|
命中率
|
命中时间
|
复杂度
|
多级cache
|
+
|
|
|
2
|
关键字优先
|
+
|
|
|
2
|
读失效优先
|
+
|
|
|
1
|
合并写缓存
|
+
|
|
|
1
|
牺牲缓存
|
+
|
+
|
|
2
|
增加块大小
|
-
|
+
|
-
|
0
|
增加cache大小
|
|
+
|
-
|
1
|
增加相联度
|
|
+
|
|
1
|
伪相连、路猜测
|
|
+
|
|
2
|
编译优化
|
|
+
|
|
0
|
非阻塞cache
|
+
|
|
|
3
|
硬件预取
|
+
|
+
|
|
2i, 3d
|
软件预取
|
+
|
+
|
|
3
|
小而简单的cache
|
|
-
|
+
|
0
|
Cache与TLB访问并行
|
|
|
+
|
2
|
流水cache访问
|
|
|
+
|
1
|
4. 替换策略
即在 cache 放不下时把谁替换出去给新cache 行腾位置。
直接相联 cache 不存在替换谁的问题,因为每个内存块对应到一个 cache行的位置
在全相联和组相联 cache 中,由于每个存储行可以放在 cache 中不同的位置,因此就有替换谁的问题。常见的替换算法有 随机替换、LRU(最近少使用替换) 和 FIFO(先进先出)。
5. 写策略
1) 写命中时:
①写穿透(Write Through):写cache的同时写回内存
②写回(Write Back):只写cache不写内存,cache块被替换时整体写入内存
2) 写失效时(发现写回的地址没在cache):
①写分配:把内存中对应的块读入cache然后更改再写回内存(常与写回配合,很自然的考虑)
②写不分配:直接写入内存(常与写穿透配合)
2.3.2 存储器层次结构示例
1. Cache相关问题
(a) 块的放置:在Cache中,根据不同的策略一个块能被放置在哪里?
①直接映射(Direct Mapping):每个块只能映射到Cache中的特定位置。通过块地址的一部分来决定放置位置。
②组相联映射(Set-Associative Mapping):Cache被划分为多个组,每个组可以容纳多个块。块可以映射到组内的任何位置。
(b) 块的标志:如果一个块在 Cache 中,如何找到它?
①在Cache中找到一个块通常需要使用地址的一部分作为标记(Tag)来识别。
②地址的一部分用于标记块的唯一性,另一部分用于选择Cache中的特定位置。
(c) 块的替换:如果没有命中,哪个块该被替换,列举三种策略?
①最近最少使用(Least Recently Used, LRU):替换最长时间没有被访问的块。
②先进先出(First-In-First-Out, FIFO):替换最早进入Cache的块。
③随机替换:随机选择一个块进行替换。
(d) 写时策略:通常有哪两种基本策略来写 Cache,写缺失时又有哪两种基本策略?
①写回(Write Back)和写通过(Write Through):写回只在块被替换时才将数据写回主存,而写通过则在写操作同时更新Cache和主存。
②写缺失策略:
写分配(Write Allocate):在写缺失时,先将整个块从主存读入Cache,然后 进行写操作。
非写分配(Write No-Allocate):在写缺失时,直接更新主存而不将整个块加
载到Cache中。
2. 数组合并
将Key和Value放在一起,而不是两个数组,如图2-8所示。
图2-8 数组合并示例
3. 循环交换
因为二维数组中一行中的数据是相邻的,一列上的数据距离更远,如图2-9所示。
图2-9 循环交换示例
4. 循环合并
增加一个循环体内值的重用次数,如图2-10所示。
图2-10 循环合并示例
5. 数组分块
数组分块示例,如图2-11所示。
图2-11 数组分块示例
6. 数组分块2
完成双精度浮点数矩阵乘法,X=Y*Z,代码如下。
for (i=0;i<N;i=i+l)
for(j=0;j<N;j=j+1){
r = 0;
for(k=0;k<N;k=k+1){
r=r+y[i][k]*z[k][j];};
x[i][j]=r;
}
假设系统 cache 大小为32KB,矩阵大小N足够大。写出将矩阵按 cache大小分块进行优化的代码,并计算分块前后Y和Z矩阵的 cache 失效次数。
双精度浮点数占用 8 个字节(64位)。
32 KB = 32 * 1024 = 32768 字节
若每个双精度浮点数占用 8 字节,则:32KB 能容纳 32 * 1024 / 8 = 4096 个双精度浮点数。
所以,可以容纳 64 * 64 大小的矩阵。
分块前 Y 和 Z 失效 次,X 失效 次,分块后 Y 和 Z 分别在小矩阵内部失效 1 次(都装进去了),在全局失效 /64 次,X 仍失效 次。
所以,分块前一共失效 2 + ,分块后失效 2 /64+ 次。
2.4 指令集并行与开发
2.4.1 Tomasulo算法
1. 概念
Tomasulo 方法是一种用于在超标量处理器中执行指令并处理数据相关(数据相关性)的方法。它主要通过对指令进行乱序执行和动态调度来提高指令级并行性。
可以通过寄存器重命名消除 WAR 和 WAW 相关(通过保留站号间接实现重命名)
也可以通过总线结构在写回寄存器之前将值送入需要的指令中,消除RAW相关
乱序的能力和有关队列大小紧密相关
2.各种阻塞
WAR 读后写(先读后写)冲突(Write After Read)是指一个指令在另一个指令读取相同寄存器之后试图写入该寄存器的情况。这种情况可能会导致结果不正确或者不确定,比如有两个指令,指令1将地址a中的数据写入寄存器b,指令2将寄存器b中的内容写回地址a中,此时如果指令2提前执行了,那么就会有不确定的内容被写入地址a,导致寄存器b的内容也会错误,但是如果必须顺序执行就会发生阻塞降低执行效率
WAW 写后写冲突(Write After Write )指的是两条指令都对一个寄存器进行了写,但是有可能第一条指令的结果被其他指令依赖,但是写入以后还没读的时候就被第二条指令的结果覆盖了,这样可能造成错误结果。如果必须顺序执行,可能会专门等待依赖第一条指令的所有指令执行完才能执行第二条指令,这样效率会下降。
RAW 写后读(先写后读)冲突(Read After Write)是数据相关性的一种类型,指的是在流水线处理器中,当前指令要读取一个在之前指令写入的寄存器的数值的情况。这种数据相关性可能会导致问题,因为如果当前指令在前一条指令写入数据之前就需要读取这个数据,那么可能会读取到错误的数据或者数据尚未更新。为了解决 RAW 相关性,流水线处理器通常采用数据前推(Data Forwarding)或者暂停(Stalling)的方式来处理;暂停就是等一会,阻塞一会,数据前推即在数据计算完成后,将数据直接传递给需要使用它的指令,而不用等到写入寄存器后再读取。这样可以避免等待写入寄存器的延迟。
WAR和WAW很明显可以通过寄存器重命名解决,RAW的解决依赖于总线, 因为总线直接将值广播到了保留站,这样就能直接完成数据前推操作,缓解RAW冲突。
3. 算法结构
整个算法的执行区域与所需结构,如图2-12所示,其中保留站内容:
1)Busy:忙位
2)Op:操作码
3)Vj, Vk:源操作数的值
4)Qj, Qk:保存没有准备好的源操作数保留站号( 0 表示操作数已经准备好)。
基础算法中的经典站位为 Op - Qj Vj , Qk Vk,如果Vj Vk都准备好了,Qj Qk就都为0。同时寄存器增加一个域(结果状态域)空表示寄存器可用,否则保存产生寄存器结果依赖的保留站号(这个域的意思就是,如果是空的,可以直接去寄存器取所需的操作数;否则,就要等某一条保留站内的指令执行完毕后采用其结果,这个结果后边也是要写到对应寄存器的)。
其中结果总线除了要送回结果外,还要送回对应操作的保留站号,毕竟还有其他操作等待此保留站的指令执行完毕,要通过此保留站号来检测是否是自己所等待的内容。
图2-12 整个算法执行结构
4. 算法流程
1)发射:把操作队列的指令根据操作类型送到保留站(如果保留站有空),发射过程中读寄存器的值和结果状态域
2)执行: 如果所需的操作数都准备好, 则执行, 否则侦听结果总线并接收结果总线的值。
3)写回: 把结果送到结果总线, 释放保留站
5. 例1:基础算法
第一个例子是基础算法,后边还有加上重排序缓存的流水线,如图2-13所示。
在指令队列中有四个指令,以指令 DIV F0 F1 F2 为例,这个指令的含义为,从浮点寄存器1 2取出两个数F1 F2,对他们执行除法操作,结果保存在F0中,即F0 = F1 / F2,其他指令类似此解释。
图2-13 基础重排序算法准备阶段
1)首先发射DIV指令(F1和F2的浮点寄存器标志位都为空,所以他们都可以访问,所在乘法保留站中两个1.0都对应0,表示他们无需等待保留站结果),将指令放入乘法保留站,保留站号为3,所以浮点寄存器0中标志位置为3,表示在等待乘法保留站3中的结果写入,如图2-14所示。
图2-14 DIV发射并计算
2) 接下来发射MUL1,即第一个乘法运算,第一个操作数是F0,需要等保留站3的结果,第二个操作数F2可以直接访问,无需等结果,所以第一个操作数保留站号置为3值置为空,第二个操作数保留站置为0值置为1(保留站号为0表示无需等待),如图2-15所示。
图2-15 发射乘法运算
3) 此时第三个加法指令进入,F1,F2均已准备就绪,所以加法保留站可以填入数值,两个操作数的保留站号都置为0,如图2-16所示。
图2-16 增加加法指令
4) 发射最后一个指令MUL2,他需要F0 F2其中F0需要等待上一条的ADD操作,即等待保留站号为6的结果,而F2不需要等待,所以在保留站中其F0操作数对应的保留站号为6值置为空,F2对应为0并将值置为1,如图2-17所示。
图2-17 增加第二个乘法运算
5) 接下来,开始依次执行保留站内的指令,首先执行加法保留站内的6号指令,并将执行结果与自身的保留站号通过总线广播出去,乘法保留站1号得到结果并将其写入,寄存器0也得到结果并写入(这两个操作均通过保留站号进行判断是否是自己所需的值),如图2-18所示。
图2-18 依次执行保留站内的指令
6) 执行DIV写回,并将结果写入保留站号2内(因为他在寄存器的写入操作被ADD覆盖了,所以就不用重新写回寄存器,此时只有他在原始队列中的下一条指令MUL1需要他的结果,这也是消除 写后写WAW 的方法,以往都是通过寄存器重命名解决的,这里用保留站号代替了重命名),如图2-19所示。
图2-19 执行DIV写回
7) 后边只需要执行保留号为1的指令,并将其结果放入浮点寄存器即可,对于2号保留站,因为其他保留站内的指令都对他没有依赖,并且浮点寄存器也无需其结果,所以是无效运行(这点看指令队列也能看出来)。