前面咱们用了三讲,用一个个的电路组合,制做出了一个完整功能的CPU。这里面一会儿给你引入了三个“周期”的概念,分别是指令周期、机器周期(或者CPU周期)以及时钟周期。程序员
你可能会有点摸不着头脑了,为何小小一个CPU,有那么多的周期(Cycle)呢?咱们在专栏一开始,不是把CPU的性能定义得很是清楚了吗?咱们说程序的性能,是由三个因素相乘来衡量的,
咱们还专门说过“指令数×CPI×时钟周期”这个公式。这里面和周期相关的只有一个时钟周期,也就是咱们CPU的主频倒数。当时讲的时候咱们说,一个CPU的时钟周期能够认为是能够完成一条最简单的计算机指令的时间。后端
那么,为何咱们在构造CPU的时候,一会儿出来了那么多个周期呢?这一讲,我就来为你说道说道,带你更深刻地看看现代CPU是怎么一回事儿。性能
学过前面三讲,你如今应该知道,一条CPU指令的执行,是由“取得指令(Fetch)-指令译码(Decode)-执行指令(Execute) ”这样三个步骤组成的。这个执行过程,至少须要花费一个时钟周期。
由于在取指令的时候,咱们须要经过时钟周期的信号,来决定计数器的自增。spa
那么,很天然地,咱们但愿能确保让这样一整条指令的执行,在一个时钟周期内完成。这样,咱们一个时钟周期能够执行一条指令,CPI也就是1,看起来就比执行一条指令须要多个时钟周期性能要好。采用这种设计思路的处理器,就叫做单指令周期处理器(Single Cycle Processor),也就是在一个时钟周期内,处理器正好能处理一条指令。设计
不过,咱们的时钟周期是固定的,可是指令的电路复杂程度是不一样的,因此实际一条指令执行的时间是不一样的。在第13讲和第14讲讲加法器和乘法器电路的时候,我给你看过,
随着门电路层数的增长,因为门延迟的存在,位数多、计算复杂的指令须要的执行时间会更长。code
不一样指令的执行时间不一样,可是咱们须要让全部指令都在一个时钟周期内完成,那就只好把时钟周期和执行时间最长的那个指令设成同样。这就比如学校体育课1000米考试,
咱们要给这场考试预留的时间,确定得和跑得最慢的那个同窗同样。由于就算其余同窗先跑完,也要等最慢的同窗跑完间,咱们才能进行下一项活动。blog
因此,在单指令周期处理器里面,不管是执行一条用不到ALU的无条件跳转指令,仍是一条计算起来电路特别复杂的浮点数乘法运算,咱们都等要等满一个时钟周期。
在这个状况下,虽然CPI可以保持在1,可是咱们的时钟频率却无法过高。由于过高的话,有些复杂指令没有办法在一个时钟周期内运行完成。那么在下一个时钟周期到来,开始执行下一条指令的时候,前一条指令的执行结果可能尚未写入到寄存器里面。那下一条指令读取的数据就是不许确的,就会出现错误。ip
到这里你会发现,这和咱们以前第3讲和第4讲讲时钟频率时候的说法不太同样。当时咱们说,一个CPU时钟周期,能够认为是完成一条简单指令的时间。为何到了这里,
单指令周期处理器,反而变成了执行一条最复杂的指令的时间呢?内存
这是由于,不管是PC上使用的Intel CPU,仍是手机上使用的ARM CPU,都不是单指令周期处理器,而是采用了一种叫做 指令流水线(Instruction Pipeline)的技术。开发
若是咱们把一个指令拆分红“取指令-指令译码-执行指令”这样三个部分,那这就是一个三级的流水线。若是咱们进一步把“执行指令”拆分红“ALU计算(指令执行)-内存访问-数据写回”,
那么它就会变成一个五级的流水线。
五级的流水线,就表示咱们在同一个时钟周期里面,同时运行五条指令的不一样阶段。这个时候,虽然执行一条指令的时钟周期变成了5,可是咱们能够把CPU的主频提得更高了。
咱们不须要确保最复杂的那条指令在时钟周期里面执行完成,而只要保障一个最复杂的流水线级的操做,在一个时钟周期内完成就行了。
若是某一个操做步骤的时间太长,咱们就能够考虑把这个步骤,拆分红更多的步骤,让全部步骤须要执行的时间尽可能都差很少长。这样,也就能够解决咱们在单指令周期处理器中遇到的,
性能瓶颈来自于最复杂的指令的问题。像咱们现代的ARM或者Intel的CPU,流水线级数都已经到了14级。
虽然咱们不能经过流水线,来减小单条指令执行的“延时”这个性能指标,可是,经过同时在执行多条指令的不一样阶段,咱们提高了CPU的“吞吐率”。在外部看来,咱们的CPU好像是“一心多用”,
在同一时间,
同时执行5条不一样指令的不一样阶段。在CPU内部,其实它就像生产线同样,不一样分工的组件不断处理上游传递下来的内容,而不须要等待单件商品生产完成以后,再启动下一件商品的生产过程。
一、增长流水线深度,实际上是有性能成本的
既然流水线能够增长咱们的吞吐率,你可能要问了,为何咱们不把流水线级数作得更深呢?为何不作成20级,乃至40级呢?这个其实有不少缘由,我在以后几讲里面会详细讲解。
这里,我先讲一个最基本的缘由,就是增长流水线深度,实际上是有性能成本的。
咱们用来同步时钟周期的,再也不是指令级别的,而是流水线阶段级别的。每一级流水线对应的输出,都要放到流水线寄存器(Pipeline Register)里面,而后在下一个时钟周期,交给下一个流水线级去处理。因此,每增长一级的流水线,就要多一级写入到流水线寄存器的操做。虽然流水线寄存器很是快,好比只有20皮秒
可是,若是咱们不断加深流水线,这些操做占整个指令的执行时间的比例就会不断增长。最后,咱们的性能瓶颈就会出如今这些overhead上。若是咱们指令的执行有3纳秒,也就是3000皮秒。
咱们须要20级的流水线,那流水线寄存器的写入就须要花费400皮秒,占了超过10%。若是咱们须要50级流水线,就要多花费1纳秒在流水线寄存器上,占到25%。这也就意味着,单纯地增长流水线级数,不只不能提高性能,反而会有更多的overhead的开销。因此,设计合理的流水线级数也是现代CPU中很是重要的一点。
讲到这里,相信你已经可以理解,为何咱们的CPU须要流水线设计了,也能把每个流水线阶段在干什么,和上一讲的整个CPU的数据通路的链接过程对上了。
能够看到,为了可以不浪费CPU的性能,咱们经过把指令的执行过程,切分红一个一个流水线级,来提高CPU的吞吐率。而咱们自己的CPU的设计,又是由一个个独立的组合逻辑电路串接起来造成的,自然可以适合这样采用流水线“专业分工”的工做方式。
由于每一级的overhead,一味地增长流水线深度,并不能无限地提升性能。一样地,由于指令的执行再也不是顺序地一条条执行,而是在上一条执行到一半的时候,下一条就已经启动了,因此也给咱们的程序带来了不少挑战。这些挑战和对应的解决方案,就要请你坚持关注后面的几讲,咱们一块儿来揭开答案了