CPU流水线技术全面解读

本文详细介绍了CPU流水线的工作原理,超流水线技术的应用,以及流水线冒险(包括结构冒险、数据冒险和控制冒险)的解决方法。此外,还探讨了分支预测、乱序执行、SIMD和NEON在提升性能中的作用,以及单发射和多发射处理器的区别和优缺点。
摘要由CSDN通过智能技术生成

0. 引言

流水线是工业社会化大生产背景下的产物,可以极大地提高生产效率。

流水线上的每个工人只需要专注自己所涉及的环节,便可以使整个流水线高效运行。

举个例子,假如生产一步手机需要三个步骤,组装,质检和贴膜包装,如果每个人独立负责各个环节。
在这里插入图片描述
这样看似没什么问题,实际上需要很大的学习成本,每个人因为要学习整个手机的生产技能,分身乏术,且一旦产品出了问题,也不方便找到具体的人。


如果是每个人只负责一个环节,就会好很多。
在这里插入图片描述
A只负责组装,同理,B负责质检,C负责贴膜和包装,每个人责任明确,工作起来也很高效。CPU流水线与其原理类似。


本篇内容提要:
在这里插入图片描述

1.流水线工作原理

一条指令的执行需要经过取指令翻译指令执行指令三个基本流程。CPU内部的电路也分为不同的单元:取指单元译码单元执行单元等,指令的执行也是按照流水线的工序一步一步执行的。

若不采用流水线技术,则每个时钟周期内只有一个单元在工作,其余两个单元在“观望”,如下图所示:
(假设每个单元的工作周期都为1,且不算流程转换的时间)
在这里插入图片描述
引入流水线技术之后,就大不一样了,每个电路单元都状态拉满,基本不会有空闲:
在这里插入图片描述
从上图中可以看到,从第三个时钟周期开始,每个单元都会“满状态”工作,CPU的指令执行效率大幅提升。

流水线的本质就是拿空间换时间。将每条指令的步骤分解到不同的电路单元,从而使得多个指令并行执行。

2.超流水线技术

上述流水线确实可以提升CPU的工作效率,但我们假设了每个小环节的执行周期是相同的,但若不同呢?现在我们假设取指单元的工作需要两个时钟周期来完成,就会出现以下情况:
在这里插入图片描述
可以发现,又会出现部分工作单元出现空闲的情况,为了进一步提升效率,我们可以这样做:
将取指的工作分配给两个物理单元去完成,这时又会像开始那样,非常高效地运行,如下图所示:
在这里插入图片描述
这样设计,我们便通过增加流水线的深度提升了CPU处理指令的效率。

优化前:执行一个指令平均需要2个指令周期;
优化后:执行一个指令平均仅需要1个指令周期。

实际的工程中,会根据实际情况来设计流水线的深度,通常我们把5级以上的流水线称为超流水线结构。高性能的处理器一般都会采用这种超流水线结构。Inter的i7处理器有16级流水线,AMD的速龙64系列CPU有20级流水线。

既然每条指令的执行都需要N个时钟周期,那我们便可以通过缩短一个时钟周期的时间来提升效率。影响时钟周期的关键制约因素就是CPU内每个单元消耗的时间。

提升CPU主频的办法有三个:

  • 优化流水线中各级的性能。
  • 提升搬半导体制造工艺,制程越先进,相对发热越小,越容易提升主频。
  • 不断增加流水线深度,流水线越深,各级延迟越小,更容易提升主频。

第一点:受限于目前集成电路的设计水准,比较难实现;
第二点:依靠半导体的制造工艺,工艺制程越先进,越容易提升主频。这也就是为什么荷兰的ASML公司可以卡其他国家脖子,包括我国;
第三点:增加流水线深度,从上面的例子中我们便可以看出来,提升流水线深度确实是个看似比较好的方法,但事实果真如此吗?

弊端一:流水线的的本质是空间换时间,流水线越深,芯片面积越大,功耗越高,这对类似手机类的移动设备来说非常致命,这将意味着更加频繁的充电。

弊端二:如果指令执行是顺序结构的,没有中断或跳转,流水线一般确实越深效率越高。但当程序中存在跳转。分支结构时,下面预取的指令可能要全部丢掉了,需要到跳转的地方重新取指令执行。
比方下面这段代码(汇编):

BEQ R1, R2, here
ADD R2, R1, R0
ADD R5, R4, R3
...
here:
SUB R2, R1, R0
SUB R5, R4, R3
...

虽然是汇编语言,但相对来说不是很难理解,BEQ是个跳转指令,当R1==R2时,跳转到here处执行,相当于C语言的if和goto语句。正常情况下,当执行BEQ指令时,下面的ADD已经被预取和译码了,如果程序没有跳转,将会继续往下执行,但当跳转到here处执行时,流水线预取的ADD指令就无效了,需要全部弃掉,然后回到here处取SUB指令。如下图所示:
在这里插入图片描述
流水线越深,一旦预取失败,浪费就会越严重,此时流水线就发生了停顿,无法按照预期继续执行,这种情况一般称为流水线冒险。

3.流水线冒险

很多因素都可以导致流水线冒险,一般分成三种情况。

  1. 结构冒险:所需的硬件正在为前面的指令工作。
  2. 数据冒险:当前的指令需要前面指令的运算数据才能执行。
  3. 控制冒险:需根据之前指令的执行结果决定下一步的行为。

待补充:三种流水线冒险的比较

3.1 结构冒险

如果多条指令都用相同的硬件资源,如内存单元,寄存器等,就会发生冲突。

ADD R2, R1, R0
SUB R1, R4, R3

上面这两条指令执行时都需要访问寄存器R1,但两条指令没有依赖关系,仅仅在使用的硬件资源上发生了冲突,这种冲突我们称之为结构冒险。如下图所示:
在这里插入图片描述
解决结构冒险的方法很简单,直接对冲突的寄存器重命名即可。这种操作可以通过编译器静态实现,也可以通过硬件动态完成。若采用硬件来完成,流水线就会增加一级,如下图所示:
在这里插入图片描述
通过硬件电流对寄存器进行重命名之后,将减法指令执行的结果写回R5寄存器,结构冒险解决。

ADD R2, R1, R0
SUB R5, R4, R3

在这里插入图片描述

3.2 数据冒险

数据冒险是指当前的执行需要上一条指令的运算结构,上一条指令没有运行结束,当前指令无法运行,只能暂停执行。如下面的程序代码。

ADD R2, R1, R0
SUB R4, R2, R3

第二条SUB指令,要等待第一条ADD指令运行结束,将结果协会寄存器R2后才可以执行。现在的经典流水线一般分为5级:取指、译码、执行、访问内存、写回。也就是说要等到指令执行结束后将运算结果写回寄存器,下一条指令才可以到整个寄存器写数据。如下图所示:
在这里插入图片描述
要解决数据冒险有很多方法,下面列举两种:

  1. 使用“operand forwarding”技术,当ADD指令运行结束后,不再执行后续操作,而是直接使用运算结果。
  2. 在两个运算中间插入空指令,也就是pipeline bubble,让流水线暂时停顿。

3.3 控制冒险

控制冒险也是如此,当执行BEQ这样的条件判断指令,我们无法立即确定接下来要执行什么,流水线就会发生停滞。如我们上文中刚刚提到的例子。

BEQ R1, R2, here
ADD R2, R1, R0
ADD R5, R4, R3
...
here:
SUB R2, R1, R0
SUB R5, R4, R3
...

解决控制冒险,也可以通过插入几条空指令来解决。

4.分支预测

条件跳转引起的控制冒险虽然也可以通过在流水线中插入空泡来避免,但是这毕竟不是最优解,因为还是较大程度降低了效率,流水线越深,就需要插入越多的空泡。以一个20级的流水线为例,如果一条指令需要上一条指令执行结束才去执行,则需要在这两条指令之间插入19个空泡,相当于流水线要暂停19个时钟周期,这是非常不理想的一种情况。

为了避免以上情况的发生,现在的CPU流水线在取指和译码时,都要对跳转指令进行分析,预测可能执行的分支和路径,防止预取错误的分支路径给流水线带来停顿。

根据工作方式的不同,分支预测可以分为静态预测动态预测

静态预测在程序编译的时候通过编译器进行预测,这种预测方式对于循环程序最有效,可以根据循环边界反复取指令。而对于跳转指令,则要简单粗暴很多,一般都是默认不跳转。

动态预测则在程序运行时进行预测。不同的软件,不同的程序分支行为,可以采取不同的算法提高预测的准确率,如我们可以根据程序的历史执行路径信息来预测本次跳转的行为,常见的动态预测方式有1-bit预测,n-bit预测,下一行预测、双模态预测、局部分支预测、全局分支预测、融合分支预测、循环预测等。

随着大量新的应用软件出现,为了应对新的程序逻辑行为,分支预测器也做得越来越复杂,占用的芯片面积也越来越大。在CPU内部,除了Cache,就数分支预测器的电路版图最大。

5.乱序执行

CPU按照顺序一条一条地执行RAM中的指令,这种执行方式称为顺序执行。顺序执行不可避免地会产生数据冒险,当产生数据冒险时,不仅可以在指令之间添加空指令来解决,也可以通过乱序执行来解决。

造成流水线成图的根源在于指令之间存在较强的相关性解决:前后指令要么产生数据冒险,要么产生结构冒险。这时候我们就可以通过重排指令的执行顺序来优雅地解决这个问题。

ADD R2, R1, R0
SUB R4, R3, R2
ADD R7, R6, R5
SUB R10, R9, R8

很明显,上述的程序存在数据冒险,这时我们可以通过在流水线中插入2个空指令来避免。
不过这时候我们的流水线暂停了2个周期,其效果并不理想。

为了避免这种情况,我们可以将指令顺序重排,使其乱序执行。从而使问题顺利解决。

ADD R2, R1, R0
ADD R7, R6, R5
SUB R10, R9, R8
SUB R4, R3, R2

支持乱序执行的CPU处理器,其内部一般都会有专门的乱序执行逻辑电路,该控制电路会对当前指令的执行顺序进行分析,看能否提前执行。CPU分析这些不相关的指令,并结合各电路单元的空闲状态综合判断,将能提前执行的指令 进行重排,发送到相应的电路单元执行。

6.SIMD和NEON

一条指令一般由操作数和操作码构成,不同的指令,其操作数的数量可能不一样。加法指令有2个操作数:加数1加数2。CPU的控制单元会先到内存中取数据,然后将其送到算术逻辑单元中,取数据的方法有两种:一种是逐个取,然后才可以进行求和计算。这种数据操作类型一般称为单指令单数据(Single Instruction Single Data, SISD);第二种方法是几个执行部件同时访问内存,一次性读取所有操作数,这种数据操作类型成为单指令多数据(Single Instruction Multiple Data, SIMD)。很明显,SIMD帮助CPU实现了数据并行访问,其执行效率进一步提高。

SIMD特别适合数据密集型计算,为了满足这种需求,从1996年起,X86架构的处理器就不断地扩展这种指令集。

多媒体扩展(MultiMedia eXtensions, MMX)指令集是X86处理器为音视频、图像处理专门设计的SIMD多媒体指令集。MMX将64位寄存器当作2个32位或8个8位寄存器来用,用来处理整型计算。这些计算借用了浮点运算的寄存器,因此MMX指令和浮点运算不能同时工作。

SSE(Internet Streaming SIMD Externsions)指令集是Inter在奔腾三处理器中对MMX中进行扩展的指令集。该指令集不再占用浮点单元的寄存器,拥有自己的128位寄存器,一次可处理128位数据。后来AVX(Advanced Vector Extensions)指令集将128位的指令集扩展到256位,支持矢量计算,并全面兼容SSE后续的扩展指令集SSE2/SSE3/SSE4。几年后AMD发布了3DNow!和SSE5指令集。3DNow!指令集给予Intel的MMX指令集进行扩展,不仅支持并行整型计算、并行浮点型计算,还可以混合操作整型和浮点型计算,不需要上下文来回切换,执行效率更高。
FMA(Fused-Multiply-Add)指令集,基于AVX指令集进行扩展,融合加法和和乘法,称为积和熔加计算,以适应绘图、渲染、立体音效等一些更复杂的多媒体运算。现在Intel和AMD新版微架构都开始支持FMA指令集。

随着多媒体需求在移动设备上大面积爆发,ARM也开始慢慢支持和扩展SIMD指令集。NEON是适用于Cortex-A和Cortex-R52系列处理器的一种128位SIMD扩展指令集。ARM从ARM V7开始引入了NEON多媒体SIMD指令,通过向量化学习,更好地支持音视频编解码、计算机视觉、游戏渲染、机器学习等需要大量复杂计算的新应用场景。

7.单发射和多发射

流水线通过前面的各种优化手段来提高吞吐率,其实就是通过提升处理器主频来提高运行效率。但每个时钟周期只能从存储器取一条指令,每个时钟周期也只能执行一条指令,这种处理器一般叫做单发射处理器。

多发射处理器在一个时钟周期内可以执行多条指令。处理器内部一般有多个执行单元,每个时钟周期内仅有一个执行单元在工作,其他执行单元都闲着,也会造成资源浪费!双发射处理器可以在一个时钟周期内同时分发多条指令到不同的执行单元运行,让CPU同时执行不同的计算(加法、乘法、浮点运算等),从而达到指令级的并行。一个双发射处理器每个时钟周期理论上最多可执行2条指令,一个4发射处理器每个时钟周期理论上最多可执行4条指令。双发射处理器的流水线如下图所示:
在这里插入图片描述
根据实现方式的不同,多发射处理器可分为静态发射动态发射静态发射是指在编译阶段将可以并行的指令打包,合并到一个64位长的指令中。在打包过程中,若找不到可以并行的指令集,则用空指令NOP补充。这种实现方式称为超长指令集架构(Very Long Instruction Word, VLIW)。如下面的汇编指令,带有||的指令表示这两条指令要在一个时钟周期内同时执行。

ADD R1, R1 R0 || ADD R3, R2, R2

动态发射:随着处理器不断地迭代更新,为了保证指令集的兼容性,现在的处理器,如X86、ARM等都采用SuperScalar结构。采用SuperScalar结构的处理器又叫超标量处理器。这种动态发射的处理器的实现过程中会增加额外的取指单元、译码单元、逻辑控制单元等硬件电路。在指令运行时,将串行的执行序列转换为并行的执行序列,分发到不同的执行单元取执行,通过执行的动态并行化来提升CPU的性能。SuperScalar大致如下图所示:
在这里插入图片描述
VLIWSuperScalar分别从编译器和硬件上实现了指令的并行化,各有优势和局限性:VLIW虽然简单,到那时由于兼容性问题,不支持目前主流的X86和ARM处理器;而采用SuperScalar结构的处理器,完全依赖硬件去识别可并行的指令,大大增加了硬件电路的复杂性,而且也存在极限。学者和工业界普遍认为,同时执行8条指令是SuperScalar结构的极限。

现在新架构的处理器没有指令集兼容的历史包袱,一般会采用显式并行指令计算(Explicitly Patallel Instruction Computing, EPIC)的指令集结构。EPIC结合了VLIWSuperScalar的优点,允许处理器根据编译器的的调度并行执行指令而不增加硬件的复杂性。EPIC的实现原理也很简单,在指令中使用三个bit位来表示相邻两个指令有没有相关性、当前指令要不要等上一条指令执行完毕才开始运行。EPIC大大简化了CPU硬件逻辑电路设计,1997年,Intel和HP联合开发的纯64位的(Itanium)处理器就采用了EPIC结构。

  • 24
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值