笔记内容及图片整理自XJTUSE “计算机组成原理与系统结构” 课程ppt,仅供学习交流使用,谢谢。
精简指令集计算机RISC体系结构与CPU体系结构的传统趋势大相径庭,RISC体系结构中具有以下关键要素:
1)大量通用寄存器和使用编译器技术来优化寄存器的用法
2)有限且简单的指令集
3)强调优化指令流水线
指令执行的特点
计算机的进化形式是随着硬件成本下降,软件相对成本上升;随着程序员的长期短缺,软件绝对成本上升。因此需要开发更强大、更复杂的高级编程语言HLL,它们具有以下特点:
1)允许程序员更精确地表示算法
2)允许编译器处理在程序员算法表达式中不重要的细节
3)自然地支持结构化编程或面向对象设计的使用
然而,开发更复杂的高级编程语言会带来较大的语义差距,即高级语言提供的操作与计算机体系结构中提供的操作之间的较大差异,这会导致执行效率低、机器程序过大和编译器复杂。因此需要设计缩小语义差距的体系结构,其关键特性是大型指令集、多种寻址模式、在硬件中实现各种高级语言语句。为了实现以上特性,需要从以下三个指令执行特点入手:
1)操作——决定CPU要执行的功能以及CPU与内存的交互
2)操作数——决定存储操作数的内存组织以及访问它们的寻址模式
3)执行序列——决定控制与流水线结构
操作
对于高级编程语言,语言和程序混合的结果具有较好的一致性。赋值语句占主导地位,表明数据移动的重要性;用比较和分支指令实现的条件语句(IF、LOOP)也占多数,表明指令集的顺序控制机制的重要性。
操作数
大多数引用都针对简单的标量变量,过程中超过80%的标量是局部变量,每次对数组或结构的引用都需要的对变址或指针的引用通常也是局部标量。因此关于访问操作数的体系结构,需要优化存储和访问高度局部化标量变量的机制。
过程调用
过程调用和返回是编译后的高级编程语言程序中最耗时的操作,因此要从过程处理时参数和变量的数量以及嵌套深度的角度考虑有效执行这些操作的方法。事实上,大量操作数引用都指向了局部标量变量且局限于相对较少的变量。
启示
通过优化最常用和最耗时的功能来为高级编程语言提供最佳支持。
RISC体系结构三要素:
1)大量寄存器——优化操作数的引用
2)精心设计指令流水线——预取的指令大部分都从未执行
3)高性能原语组成的精简指令集——指令具有可预测的成本并与高性能实现保持一致
使用大型寄存器文件
寄存器特点是比内存和高速缓存更快、地址更短、位置更靠近CPU。所以需要一种策略来让访问频率最高的操作数保存到寄存器中,以最小化寄存器-内存操作。
基于软件的基本方法:
1)要求编译器分配寄存器
2)根据给定时间内使用最多的变量进行分配
3)需要复杂的程序分析
基于硬件的基本方法:
1)拥有更多注册信息
2)允许寄存器中更多的变量
局部变量寄存器将局部标量变量存储在寄存器中,能减少内存访问。每个过程函数调用都会改变局部性,即调用程序的变量必须存储到寄存器中,且必须传递参数和返回结果。
寄存器窗口
关于指令执行有两个结论:
1)典型过程只使用少数的传递参数和局部变量
2)过程调用深度在一个相对较窄的范围内变化
为利用这两个结论,可以使用多组小寄存器,每组分配给不同的程序。过程调用会自动切换CPU以使用另一个固定大小的寄存器窗口,而不是将寄存器内容保存到内存中。相邻过程的寄存器窗口相互重叠以传递参数。
寄存器窗口被分成三个固定大小的区域:
1)参数寄存器——保存由调用当前过程的过程传递下来的参数,以及要向上传递的结果
2)本地寄存器——保存局部变量,由编译器分配
3)暂存寄存器——与相邻的下一级交换参数和结果。一级暂存寄存器与下一级暂存寄存器在物理上是相同的。这种重叠允许在不移动数据的情况下传递参数。
下图是重叠寄存器窗口示例:
循环缓冲区组织:
当进行调用时,移动当前窗口指针以显示当前活动的寄存器窗口;若所有窗口都在使用中,则会生成一个中断,并将最旧的窗口,即调用嵌套中最靠后的窗口,保存到内存中,保存的窗口指针指示下一个保存的窗口应还原到的位置。可以看出,一个N个窗口的寄存器文件只能容纳N-1个过程调用。通常采用8个窗口时,仅1%的调用或返回需要保存或恢复。
对于RISC计算机,使用8个窗口,每个窗口有16个寄存器。
全局变量
寄存器窗口模式是在寄存器中存储局部标量变量提供了一种有效的组织方式。若在高级编程语言中将一些变量声明为全局变量,并由编译器分配内存位置,则所有引用这些变量的机器指令将使用内存引用操作数,这种方法实现简单,但是对频繁访问的全局变量效率低下。
另一种方法是在CPU中为全局变量合并一组寄存器,这些寄存器数量固定,可以用于所有过程,也可以使用统一编号方案来简化指令格式。需要增加硬件负担,以适应分开的寄存器寻址,此外链接器必须决定将哪些全局变量分配给寄存器。
大型寄存器文件与高速缓存
寄存器被组织成窗口,充当一个小型的快速缓冲区,用于保存使用最频繁的所有变量的子集。从这个角度来看,寄存器文件的作用很像高速缓存,尽管它的速度要快得多。高速缓存方法能节省内存引用开销且在空间使用方面效率高,寄存器方法速度快。
以下是基于窗口的寄存器文件的引用标量:
以下是高速缓存的引用标量:
基于编译器的寄存器优化
大型寄存器组可以提高计算机的性能,而少假设RISC具有少量寄存器(16-32),我们可以通过使用优化寄存器来获得高性能,这种优化由RISC编译器完成,由于高级语言程序没有对寄存器的明确引用,它为每个候选变量分配符号或虚拟寄存器,将无限符号寄存器映射到真实寄存器,将不重叠的符号寄存器与真实寄存器共享。如果实际寄存器用完,某些变量会占用内存,因此加载和存储DRAM操作被最小化。
精简指令集架构
为什么不是复杂指令集架构CISC
复杂机器指令常常难以被使用,因为编译器必须找到完全适合该构造的情况。对于CISC,为了最小化代码量、减少指令执行数、增强流水线而生成的代码进行优化的任务要难得多。
预期CISC将产生更小、更快的程序,它提升计算机性能通过以下三种方式:1)更少的指令意味着要取的指令字节更少2)分页环境下更小的程序占用更少的页3)高速缓存包含更多的指令。
精简指令集架构RISC的特点
1)每个机器周期一条机器指令
2)大多数操作是寄存器对寄存器的
3)使用简单的寻址模式
4)使用简单的指令格式
因此可以对这些特点进行评估来确定RISC方法的潜在性能优势,包括硬连线设计、有效的指令流水线、对中断反应更灵敏和更多的编译时间。
CISC与RISC特点比较
RISC设计可以从包含一些CISC特性中受益,CISC设计也可以从包含一些RISC特性中受益。因此最近的RISC设计和CISC设计,随着芯片密度和原始硬件速度的提升,RISC系统变得更加复杂以期最大限度地提高性能,CISC系统把重点放在了传统上与RISC相关的问题上,例如增加通用寄存器数量、更加重视对指令流水线的设计等。
RISC流水线
规整指令的流水线
指令流水线常被用于提高性能,大多数指令都是寄存器对寄存器的,一个指令周期具有如下两个阶段:
I:取指——提取指令
E:执行——使用寄存器输入和输出执行一个ALU操作
对于加载和存储操作则具有如下三个阶段:
I:——取指——提取指令
E:执行——计算内存地址
D:访存——寄存器对内存或内存对寄存器的操作
下图为串行执行、两段流水时序、三段流水时序、四段流水时序的流水线效果图:
流水线优化
由于RISC指令简单规整,实现简单快速的流水线比较容易。指令执行过程的变化很少,但数据和分支相关性会拉低总体执行速度。为了弥补相关性,有延迟分支和延迟加载两种技术。
延迟分支——分支直到下一条指令执行之后才会生效,因此称为延迟。紧随分支的指令位置是延迟槽。为了让流水线更加规整,通常在分支后面插入一个NOOP。
下图是正常分支、延迟分支和优化延迟分支的示例:
下图是使用延迟分支针对传统流水线、插入NOOP的RISC流水线、交换指令的示例:
延迟加载——常用于LOAD指令,作为加载目标的寄存器被CPU锁定。然后CPU继续执行指令流,直到它到达需要给寄存器的指令,此时CPU处于空闲状态,直到加载完成。如果编译器可以重新排列指令,以便在加载处于流水线中时进行有用的工作,那么能提高效率。
循环展开——定义一个展开因子u,循环的展开将重复循环u次,迭代的步长是u。展开将从降低循环开销、通过改进流水线性能来增加指令并行性、增加寄存器、数据高速缓存或TLB的局部性三方面来提高性能。
下图是循环展开两次的程序代码: