第六章 存储层次结构
CPU如何处理指令_寄存器
CPU主要由下图组成
- 一系列寄存器相关的寄存器,以及其他用于处理数据的寄存器
- ALU,算数逻辑运算单元
- CU,控制单元,控制多个寄存器传输之间的顺序等
- 一些寄存器之间的连接
保存需要访问的内存地址;
保存需要写入寄存器或从内存中读取的数据;
程序计数器(Program Counter,PC)在主存储器中指出下一个指令的地址。 程序执行前,必须先将程序的第一个地址发送到程序第一条指令所在主存单位的地址PC,因此PC内容是从主存提取的第一条指令的地址。 执行指令时,CPU能自动递增PC内容使其始终保存下一个指令的主要存储地址,并为下一个指令做好准备。如果是单词长指令,则(PC) 1àPC,若为双字长指令,则(PC) 2àPC,以此类推。 但是,当遇到转移指令时,下一个指令的地址将由转移指令的地址代码字段指定,而不是像往常一样按顺序增加PC获取内容。
读取内存的时候,首先会从MBR在寄存器中读取数据;如果读取的数据是指令,从IR里面读取;
1.IR(opcode):每一个指令opcode它们是唯一的,也是指令最重要的组成部分,CPU通过opcode了解当前指令到底在做什么
2.IR(address):另一个重要部分是address部分
指令寄存器(Instruction Register,IR)用于保存正在执行的指令。 执行指令时,首先将指令从主存读取到数据寄存器,然后传输到指令寄存器。 指令包括操作代码和地址代码。为了执行指令,必须测试操作代码,识别所需的操作,并指令翻译(Instruction Decoder,ID)完成这项工作。指令翻译器翻译指令寄存器的操作代码部分,以产生指令要求的控制电位,并将其发送到微操作控制线,在定时信号的作用下产生特定的操作控制信号。 操作码字段在指令寄存器中的输出是指令译码器的输入。操作码一旦翻译,就可以向操作控制器发出特定的操作信号。
:栈指针存储在当前程序执行过程中的栈地址
控制单元、控制顺序和指令执行后寄存器的变化
算术运算单元包括算术运算所需的逻辑、逻辑或其他模块
根据状态寄存器ALU状态寄存器中的一些状态标志包括一些溢出(Overflow)、零(Zero)、负(Negative)等标识位
累加寄存器通常简称累加器,是一种通用寄存器。 当运算器的算术逻辑单元时,累加器的功能是ALU执行算术或逻辑操作时,为ALU可以提供工作区ALU暂时保存操作数或操作结果。 显然,运算器中至少有一个累加寄存器。
2.
1.从内存中提取(Fetch)并把指令放进去IR中;
2.解码(Decode)当前指令
3.执行(Execute)当前指令
1.将PC的值存入MAR寄存器,
MAR<---PC
2.读取MAR将数据放入中值MBR寄存器中
MBR<---{MAR}
3.需要下一步MBR复制中值IR寄存器中,IR在解释执行当前指令后PC加1(本文介绍的指令是8个指令,所以PC对16位和32位指令,PC需要添加相应的字节数)
PC<----PC 1
将存储器装入累加器或变址X指定的存储器
以上图为例可分为以下步骤:
a.指令提取阶段,将PC值先赋值给MAR
b.地址的值为"2"
c.从内存为"2" 提取指令并存入指令的地方MBR中
d.MBR的值写入IR寄存器
e.PC需要加一
f.解码当前IR获取指令opcode部分的值为1
g.Address部分的值为"5"
h.内存地址为"5"提取出数据
i.由于LDA操作的是AC最后,将数据放入寄存器中AC中
CPU可以粗略地认为是。充电算1,不充电算0。每次计算都是这些小电容器的充放电。
各种模块连接,形成复杂的功能。也就是说,前小模块的输出将被后模块输入。
那就有问题了。一方面,后面的模块如何知道前面的模块是充放电还是充放电?另一方面,路径越长,从一开始输入到最终输出的时间越长,即路径长度的延迟不同,因此很难保证每根针脚上的数据同时严格到达。
因此引入了时钟机制。
用统一的时钟脉冲同步每个小模块。
指令周期通常有几个CPU周期,CPU周期也被称为机器周期,因为CPU访问一个内存需要很长时间,所以通常在内存中读取最短的指令来规定CPU周期。也就是说,指令取出阶段(通常是指令取出)需要一个CPU周期时间CPU周期时间包括几个时钟
扩展:
基本的数据运算单位,与或非门
0和1的艺术,以及,或,非基本逻辑门电路_码出钞能力的博客-CSDN博客
1. 存储技术
(RAM)分为两类:(SRAM)和(DRAM)。
SRAM:结构相对复杂,成本高;双稳态特性,只要有电,就会始终保持其值();容量小,速度快,一般用作CPU内存缓存。
DRAM:结构相对简单,成本低;存储单元对干扰非常敏感(),容量大,速度慢,一般用作系统内存。
RAM易失性存储器,断电数据丢失。
属于外部I/O设备,其特点是存储容量大,但读取速度更慢,价格也更加便宜。
磁盘的结构是一个磁盘,分布在磁盘上(不同半径有不同的磁道),每次访问都要找到相应的磁道,磁盘旋转到相应的位置,然后用读写头读取或修改值,因此磁盘风扇区域的访问时间=寻道时间 旋转时间 传输时间。
与机械磁盘相比,基于闪存的存储技术读写速度快,价格高,使用寿命短。
2. 局部性
在程序中,程序倾向于引用与最近引用的其他数据项相邻的数据项,或者最近引用的数据项本身,这被称为局部原理。一个好的程序倾向于显示和良好的局部性。
局部原理分为时间局部性(temporal locality) 和空间局部性 (spatial locality) 。
时间局部性是指未来多次引用被引用一次的存储位置(通常在循环中)。 如果引用存储器的位置被引用,他附近的位置将来也会被引用。
可以总结如下:
- 重复引用变量程序具有良好的时间局部性。
- 对于步长为k的引用模式,步长越长,空间局部性越好。相对得,在存储器中以大步长跳来跳去的程序空间局部性会很差。
- 对于取指令来说,循环有好的时间和空间局部性。循环体越小,循环迭代次数越多,局部性越好。
3. 存储器层次结构
基本缓存原理
当程序需要第k+1层数据块n的时候,程序会在当前存储的k层,寻找块n的数据,刚好n在k层的话,就是一个缓存命中,这比从k+1层读取的速度要快很多。
当程序需要访问到块n的时候,在k层没有该数据块,就是一个缓存不命中,这时候就会从k+1层中读取块n将其替换到k层的一个数据块(覆盖或驱逐一个已有的数据块)。程序还是从k层访问块n。
冷不命中:也称强制性不命中,一个空的缓存,被访问时不命中的情况。
冲突不命中:缓存足够大,理论上能够保持被引用的全部数据对象,但是因为这个对象映射到了同一个缓存块中,导致缓存一直不命中。
当缓存不命中时,我们需要从k+1层中获得的数据放置在k层缓存中,以什么样的策略放置,称为放置策略。LRU
:最近最少使用,把数据加入一个链表中,,发生淘汰的时候,把访问时间最旧的淘汰掉。MRU:与LRU
相比,发生淘汰的时候,把访问时间最新的淘汰掉。等(Cache replacement policies(缓存替换策略)/ LRU 和 LFU等算法_Jeff_的博客-CSDN博客_lru替换策略)
4. 高速缓存存储器
算术逻辑单元(arithmetic and logic unit) 是能实现多组算术运算和逻辑运算的组合逻辑电路,简称ALU。
高速缓存大小(容量)C = S*E*B。
三种
1.直接映射高速缓存
2,组相连高速缓存
3.全相连高速缓存
以直接映射高速缓存为例
1.组索引s位数据转换成无符号的组号,定位到高速缓存所在组。
2.由于直接映射高速缓存每组只有一行,只需检查有效位是否=1,标记号是否与地址的标记位一致。是则进行第三步;否则进行第四步。
3.此时高速缓存命中,由于直接映射高速缓存每个组只有一行,则直接根据块偏移来定位行中的起始地址(有多行的高速缓存需要根据标记的t位定位到具体行),从该地址中读取n个字节(n为字的大小)返回给上层。
4.此时高速缓存不命中,需要从下一层取出被访问的块,然后存放到对应的缓存组(行)中,再返回字给上一层,如果缓存组(行)中已有有效数据,则需要驱逐一个行数据,在组内有多行的缓存器中,驱逐哪一个会根据制定好的策略进行。
1.高速缓存大小的影响:较大的高速缓存可能会提高命中率,但是会增加命中时间。
2.块大小的影响:会提高空间局部性好的程序的命中率,但是会损害时间局部性好的程序的命中率;并且较大的块会使不命中的处罚更大。
3.相联度的影响:即高速缓存每组的行数,较高的相联度降低了由于冲突不命中出现的抖动的可能性,但是成本较高,并且由于复杂性的增加,会增加命中时间和不命中处罚。
4.写策略的影响:直写比较容易实现,使用独立与高速缓存的独立写缓冲区;写回减少传送次数,提高效率。
5. 编写高速缓存友好的代码
例子:假设高速缓存块大小位4个字,字大小为4个字节,一开始缓存为空。
二维数组以行优先求和
由于一开始缓存为空,所以每读取一个字,必然会出现一次不命中。C语言程序以行优先顺序存储数组,所以读出来一个字后,步长为1的引用,后三个字节都能命中。
二维数组以列优先求和
由于一开始缓存为空,所以每读取一个字,必然会出现一次不命中。以一列的大小为步长引用,当列大小大于字大小时,前面读取出来的字则不包含下一次需要引用的字节。
总之:
6. 高速缓存对程序性能的影响
习题:
解答:
A:由S=2^s,S=4得出s为2;由B=2^b,B=4得出b为2;那么t=12-s-b=8。按照
可得:0-1为CO,2-3为CI,4-12为CT。
B:1.地址0x834转换为二进制(1000 0011 0100)可得标记为0x83,组索引为1,块偏移为0;由表可得缓存中该租有效位为0,所以不命中,值未知。
2.地址0x836转换为二进制(1000 0011 0110)可得标记为0x83,组索引为1,块偏移为2;由表可得缓存中该租有效位为0,但是由于第一步对地址0x834的读取,会更新该组数据,且有效位为1,所以命中,值未知。
3.地址0xFFD转换为二进制(1111 1111 1101)可得标记为0xFF,组索引为3,块偏移为1;由表可得缓存中该租有效位为1,所以命中,值为0写C0。
解答:由块大小为16字节的b=4;由直接映射可得E=1;由总共32个字节可得S=2(C=S*E*B),s=1;
dst开始地址64= 100 0000b
src开始地址0 = 000 0000b
dst[0][0]:100 0000b src[0][0]:000 0000b
dst[1][0]:101 0000b src[0][1]:000 0100b
dst[2][0]:110 0000b src[0][2]:000 1000b
dst[3][0]:111 0000b src[0][3]:000 1100b
dst[0][1]:100 0100b src[1][0]:001 0000b
dst[1][1]:101 0100b src[1][1]:001 0100b
dst[2][1]:110 0100b src[1][2]:001 1000b
dst[3][1]:111 0100b src[1][3]:001 1100b
dst[0][2]:100 1000b src[2][0]:010 0000b
dst[1][2]:101 1000b src[2][1]:010 0100b
dst[2][2]:110 1000b src[2][2]:010 1000b
dst[3][2]:111 1000b src[2][3]:010 1100b
dst[0][3]:100 1100b src[3][0]:011 0000b
dst[1][3]:101 1100b src[3][1]:011 0100b
dst[2][3]:110 1100b src[3][2]:011 1000b
dst[3][3]:111 1100b src[3][3]:011 1100b
所以最终结果为:
m | m | m | m |
m | m | m | m |
m | m | m | m |
m | m | m | m |
m | m | h | m |
m | h | m | h |
m | m | h | m |
m | h | m | h |
解答:
A:E=1,B=16,C=512;b=4,S=32,s=5;i取值为0-127
x[0][i] : 000 0 0000 0000b + i*100b
x[1][i] : 001 0 0000 0000b + i*100b
由于依次读取x[0][i]和x[1][i]的i值一样,所以导致他们的组索引一直都是一样的,但标记不一样,造成了冲突不命中。所以不命中率为100%。
B:E=1,B=16,C=1024;S=64,s=6;i取值为0-127
x[0][i] : 00 00 0000 0000b + i*100b
x[1][i] : 00 10 0000 0000b + i*100b
由于s=6,所以x[0][i]和x[1][i]不管当i取何值,组索引都不一样,所以不存在冲突不命中,块大小为16字节,每次读4字节,所以得不命中率为25%。
C:E=2,B=16,C=512;S=16,s=4;i取值为0-127
x[0][i] : 0000 0000 0000b + i*100b
x[1][i] : 0010 0000 0000b + i*100b
当i取值为0-63,每四次就会有一次冷不命中,所以不命中率为25%。
当i取值为64-127,同样每四次就会有一次不命中,需要置换之前的缓存,所以不命中率为25%。
综合起来,不命中率为25%。
D:不会,因为此时缓存块大小是限制因素(每四次读取第一次都会不命中)。
E:会,更大的缓存块会降低不命中率,因为不命中只发生在第一次读入块的时候,增加块的大小,使得读取一个块后,该块的命中次数增加。所以更大的块会使得不命中占总读取的比例降低。