CPU的流水线,分支预测与乱序执行

流水线

转自:http://www.elecfans.com/emb/dsp/20180405657563.html

流水线的概念来源于工业制造领域,以汽车装配为例来解释流水线的工作方式,假设装配一辆汽车需要四个步骤:

第一步冲压:制作车身外壳和底盘等部件。

第二步焊接:将冲压成形后的各部件焊接成车身。

第三步涂装:将车身等主要部件清洗、化学处理、打磨、喷漆和烘干。

第四步总装:将各部件(包括发动机和向外采购的零部件)组装成车。

汽车装配则同时对应需要冲压、焊接、涂装和总装四个工人。最简单的方法是一辆汽车依次经过上述四个步骤装配完成之后,下一辆汽车才开始进行装配,最早期的工业制造就是采用的这种原始的方式,即同一时刻只有一辆汽车在装配。不久之后人们发现,某个时段中一辆汽车在进行装配时,其它三个工人都处于闲置状态,显然这是对资源的极大浪费,于是思考出能有效利用资源的新方法,即在第一辆汽车经过冲压进入焊接工序的时候,立刻开始进行第二辆汽车的冲压,而不是等到第一辆汽车经过全部四个工序后才开始,这样在后续生产中就能够保证四个工人一直处于运行状态,不会造成人员的闲置。这样的生产方式就好似流水川流不息,因此被称为流水线。

计算机体系结构教材中被提及最多的经典MIPS五级流水线如图1所示。在此流水线中一条指令的生命周期分为:

取指:

指令取指(InstrucTIon Fetch)是指将指令从存储器中读取出来的过程。

译码:

指令译码(InstrucTIon Decode)是指将存储器中取出的指令进行翻译的过程。经过译码之后得到指令需要的操作数寄存器索引,可以使用此索引从通用寄存器组(Register File,Regfile)中将操作数读出。

执行:

指令译码之后所需要进行的计算类型都已得知,并且已经从通用寄存器组中读取出了所需的操作数,那么接下来便进行指令执行(InstrucTIon Execute)。指令执行是指对指令进行真正运算的过程。譬如,如果指令是一条加法运算指令,则对操作数进行加法操作;如果是减法运算指令,则进行减法操作。

在“执行”阶段的最常见部件为算术逻辑部件运算器(ArithmeTIc Logical Unit,ALU),作为实施具体运算的硬件功能单元。

访存:

存储器访问指令往往是指令集中最重要的指令类型之一,访存(Memory Access)是指存储器访问指令将数据从存储器中读出,或者写入存储器的过程。

写回:

写回(Write-Back)是指将指令执行的结果写回通用寄存器组的过程。如果是普通运算指令,该结果值来自于“执行”阶段计算的结果;如果是存储器读指令,该结果来自于“访存”阶段从存储器中读取出来的数据。

在工业制造中采用流水线可以提高单位时间的生产量,同样在处理器中采用流水线设计也有助于提高处理器的性能。以上述的五级流水线为例,由于前一条指令在完成了“取指”进入“译码”阶段后,下一条指令马上就可以进入“取指”阶段,依次类推,如图2所示,如果流水线没有停顿,理论上可以取得每个时钟周期都完成一条指令的性能。
在这里插入图片描述

分支预测

转自:https://zhuanlan.zhihu.com/p/36543235

上述流水线架构对于顺序执行的命令,效果提高显著,但是遇到跳转命令时效率便会急剧下降,对于分支跳转指令,我们在执行完该指令之前是不知道是否发生跳转的,也就是说,我们在分支指令执行完之前,我们无法确定分支指令的下一条指令的地址,所以也就没法把分支指令的下一条命令放入流水线中,只能等待分支指令执行完毕才能开始下一条命令的取指步骤,所以流水线中就会出现气泡(Bubble),这会大大降低流水线的吞吐能力。
为了解决上述问题,分支预测器应运而生。当指令执行到分支跳转指令时,CPU不再是空等待分支跳转指令执行完毕给出下一条命令的地址,而是根据模型预测分支是否发生跳转以及跳转到哪里,CPU将预测到的指令直接放入流水线,去执行指令的取指、译码等工作。

当分支跳转指令完成执行阶段后,给出是否跳转的结果,CPU即可判断分支跳转预测是否正确,如果指令执行后的跳转结果与分支预测器预测结果相一致,则流水线继续往下执行,如果发现分支预测结果出现错误,则需要清空流水线,将前面不该进入流水线的指令清空,然后将正确的指令放入流水线重新执行。

乱序执行

由于在指令在一定逻辑上只能按照顺序执行,比如疫情期间在家办公,计划的顺序是起床、等快递来送菜、做早饭、吃早饭、回复邮件、开始工作。但是快递小哥迟迟不来,咱们是不是得一直等着他来送菜再进行下面的事情?显然不会,你一定会把等菜、做饭、吃饭等一系列步骤放着,先去执行另一系列的步骤(发邮件、工作),因为这两个系列的步骤是完全不相关的。放在CPU中的逻辑时,**某条指令需要访存,访存的时候多级cache不命中、内存缺页等原因导致访存时间延长,这个时候是不是也像快递小哥迟迟不来一样,这个时候咱们就要先放弃这一系列的事情,先去执行后面的与之无关指令。**等访存结束再去回去执行之前被耽搁的指令,这就是乱序执行(因为指令的顺序被打乱了)。这样的代价在哪里?为什么叫乱序执行,不叫乱序提交,那就是因为提交顺序(最后结果)必须正确,比如一个寄存器按照原本的顺序是 被改成3、3被访问、被改成5、5被访问,显然前两个跟后两个没有关系,如果出现了乱序执行,先执行了后两个,那么寄存器最后结果就是3了,后续跟这个寄存器打交道的指令都会出错(原本希望最后是5),所以提交顺序一定要正确,所以设计了保留站和ROB(重排序缓冲),那就是标记指令本来的执行顺序,在写回(最后一级流水线)的时候、如果比自己标记好小的指令不提交,自己就在ROB中呆着等待原本在自己前面的指令提交。这样就保证了既节省时间又能正确提交

不光CPU会进行乱序执行,编译器也会进行乱序执行,他们会打乱程序原本的执行顺序,若不希望CPU和编译器有如此乱序,需要自行在代码中加入“屏障”和“内存屏障”

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值