大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
1.我们知道,CPU是电脑的中央处理单元,CPU到底是怎么连续的执行指令的。我们以MIPS为例,探究一下。
2.基础的知识我们需要知道,CPU执行一条指令时分为五个阶段的:(1)在内存取指令(2)根据指令读寄存器(3)利用寄存器中的数据ALU(4)访问内存(5)写寄存器。一般是这五个阶段,但是很多指令并不是说这五个阶段全部都在做事情。比如add,它只有四个阶段,其中不涉及到内存的访问。但是,又有指令五个阶段都要做事情,比如lw。既然是通用的CPU,我们尽可能的支持夺得指令,或者说是一种短板效应。
3.知道了这些,我们分析一下,CPU如果是一条一条的执行指令,那么就会出现这种情况,比如add,在执行它的时候,他被执行到第二阶段,第一个取指令的操作就空了下来,同理,越来越多的操作被空闲。这显示是不行的,对于追求效率的CPU是不能容忍的,于是在基于工厂流水线的启发:提出了基于流水线形式工作的CPU。大概就是这个样子。
这是五条指令在一起工作,上一条使用完资源以后,下一条紧接着继续。十分的紧凑,没错,这样就让CPU连续不空闲的工作,看上去似乎很不错。
4.流水线的困境。虽然上边基于流水线的设计,使得CPU得到了很高的效率,但是也面临这一些困境,或者说冒险。这些词是根据英文 Hazards翻译而来。
5.结构困境:
我们观察这样一种执行的指令的顺序:我们注意到,第一条指令和第四条指令在同一个阶段都是在访问内存,其中第一条是在写内存,第二条是在读内存。这两种操作在某种意义下是相反的,你不可能又读又写吧,这显然是不合理的。为了解决这个问题,人们发明了缓存技术。使用两个一级缓存,一个读一个写。即保证了效率,又解决了冲突。
同样的问题也发生在了寄存器上:
我们发现,存在两条指令一条写寄存器,一条读寄存器。这也是不合理的。但是由于寄存器的读写速度很快,并且,肯定是要先写后读的,因为后边的指令可能要用到上面指令得到的数据,所以规定先写后读。这样,结构困境被完美解决。
6.控制困境:我们知道,程序时不可避免要出现分支的,就像这样:
但是我们很快就可以发现问题,因为分支的结果是在ALU之后才能知道的,所以你当前分支后边的两条指令到底执行什么,这是不可预料的。有可能跳转,也有可能不跳转,所以把那些指令装载进来,这是一个很大的问题。针对这个问题,人们提出了三种解决方案:
(1)空指令等待:既然beq这种分支指令只能再ALU之后才能得到结果,那好吧,我在你出结果之前啥也不干,给你两条空指令,比如再a0寄存器加0什么的。那么其实结构就像是这样:
这虽然解决了问题,但是引入了两条空指令,这对CPU这种追求效率的部件来说时不能忍的。然后,人们又提出了第二种解决方案。
(2)人们仔细分析发现,问题的根本原因是在ALU这个位置再整个执行程序流程的后边,如果把它提前,问题不久容易了一些嘛,所以人们提出再Reg中加一个分支比较器,然后空出一个单个的时间段,这样似乎又好了很多。
这样虽然好了很多,但是,还是又空闲,你说人们怎么就这么贪心呢?CPU一直工作,插个空闲让人家休息会不行吗,计算机科学家说:不行!它得一直给我干活!行吧,那就干呗。
(3)新的概念引入,人们发现,这样虽然不时一整个空指令进去了,但是还是不是很满意,于是就继续反思自己。然后发现可能是自己的思想有问题:为什么分支会产生这样的问题,因为分支只是做了干了一个活。我现在重新定义分支,我让你依次就是执行两条指令,什么意思呢?就是说,计算机科学家发现,如果我让分支一次执行两条指令,其中一条是分支,另一条是与它无关的指令。这样再执行完这两条指令之后,分支的结果也产生了,还顺便执行了一条其他的指令。但是这里面有很多问题:什么叫做与分支无关的指令,你怎么找到这样的指令,这样的指令一定存在吗?对于前两个问题,汇编器会帮我们做,至于最后一个问题,答案是这样的指令还就真的不一定存在。注意,时不一定,不是一定。但是这种设计使得CPU的效率再很多时候都能达到100%,即使找不到这样的指令,也是50%。这种设计的方式叫做分支延迟:Delay-Branch
我来解释一下这个图,主要是解释Delay-Branch。看都右边,我们发现再beq之前,其实ori用到的数据和beq用到的没关系,所以就可以调整一下位置,把or拿到beq的后边,这样再beq完成的时候,or也完成,效率达到100%。
7.我们花了很大的力气解决了控制困境,但是问题总是解决不完的。毕竟是CPU,怎么能随随便便成功。我们来看一下这个程序执行的过程:
放在流水线上就是这样的结果:
我们可以看到:由于t0使用的很频繁,导致再还没有结果算出来被写回来的时候,就已经开始用了。数据的即时依赖产生了数据困境。当然了,这些小问题可是难度到我们的计算机科学家。针对这个问题,计算机科学家马上提出了前馈机制来解决。
Forwarding:
其实我们知道,ALU之后数据的结果已经出来了,那么你先别写了,先拿来给我用用。先把数据的结果反馈出去,这就叫做前馈。嗯,似乎很不错,直到遇见了这张图:
这张图中,结果再内存访问之后才出来,实在是太慢了,前馈都没办法。这可咋办?没事,我们的计算机科学家总是有办法的,他们还是根据刚才的想法,插入一些空指令,使得前馈可以继续下去。大概就是这样:
这样就成功的解决了问题。这样我们CPU的流水线控制也基本结束了,其实CPU也就是这样吧,哈哈。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170390.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...