指令流水线总结

5.5 CPU指令流水线

一. 流水线

  1. 流水线
    (1)流水线:
    指令从取值到真正执行的过程划分成多个小步骤,cpu真正开始执行指令序列时,一步压一步的执行,减少其等待时间。
    (2)流水线级数越多,工作效率越高。intel处理器的流水线级数远超过嵌入式cpu的流水线级数
    (3)流水线的效率:
        a. 并非指令每一步的操作时间都是等长的。长节拍的步骤会导致流水线效率下降(短节拍的步骤要等待长节拍的步骤执行完毕)
        b. 解决办法:
            i. 拆分长节拍
            
            ii. 增加长节拍的处理单元:使得同时间产生多个长节拍的执行结果,供短节拍的动作使用。
            
            eg:长节拍是短节拍执行时间的3倍,则可以增加为3个长节拍执行单元,1个短节拍执行单元

  2. ARM7的三级流水线:
    ARM7把知道指令执行的时间分为3个步骤:取指IF,译码ID,执行EX
         

  3. 经典的MIPS5级流水线
    (1)处理器内部有很多程序员可见的 通用寄存 器,这些通用寄存器组成了寄存器堆register file
    (2)为了让不同流水线之间的数据不会互相打扰,cpu在每个阶段会把通用寄存器中的值拷贝到流水线寄存器中,使得各个节拍的工作不会乱序。
    (3)汇编语言中,ALU执行单元直接访问通用寄存器,而在硬件实现中,通用寄存器的值先拷贝到ALU输入寄存器中(一个流水线寄存器),然后再送回通用寄存器
    (4)MIPS在3级流水线的基础上,增加了2个阶段,1个是从内存获取数据的mem阶段,另一个是把ALU计算后的数据导出到通用寄存器堆的WB阶段
    IF -> ID -> EX -> MEM -> WB
         

  4. DSP对三级流水线的深层次扩展
    (1)DSP处理器把取指,译码,执行的三大步骤细分为多个小步骤
    (2)MIPS处理器把MEM和ALU计算放在不同阶段,写回通用寄存器也单独形成一个阶段。而DSP把MEM和WB阶段都放在了指令执行阶段,更符合程序员的视角。
         

三. 流水线思想的冒险

  1. 结构冒险:不同阶段的执行步骤由于硬件资源冲突不能同时进行
    MIPS处理器的流水线中,IF阶段需要访问存储器拿指令,MEM阶段需要访问存储器拿数据,这两个动作都要访问存储器,造成存储器和寄存器之间的总线冲突,而不能同时进行。但是,现代处理器,程序被存储在L1P Cache中,数据被存在L1D Cache中,所以不会冲突。
         

  2. 数据冒险
    (1)流水线使得原先有先后顺序的指令同时处理,当出现某些指令组合时,可能会导致使用了错误的数据。
         
    (2)因此,cpu采用直通(forwarding)来解决:如果当前指令的源操作数在EX/MEM的流水线寄存器中,就直接将流水线寄存器中的值传递给ALU输入,不去通用寄存器堆取值
    (3)但不是所有数据冲突都能采用直通解决,要配置cycle等待
    sub指令的R1,最早也要在cycle4中才能到MEM/EX流水线寄存器,所以仍要掩饰一个周期

  3. 控制冒险
    (1)当一条流水线中的指令出现跳转操作时,其他流水线提前做出的操作是根据pc+1进行取指的,跳转操作使得这些流水线上的操作全部无效
    (2)流水线等级越深,跳转指令造成的效率下降约严重

四. 流水线的分支预测

  1. 1-bit预测算法:
    如果该指令上次发生跳转,则预测这一次也会发生跳转
  2. 2-bit预测算法:
    每个指令的预测状态信息从1bit加到2bit,如果这个跳转执行了,就+1,加到3就不加了,如果跳转信息不执行就-1,减到0就不减了。如果计数器值为0/1,就预测不执行,计数器为2/3,就预测执行。

  3. Intel的分支预测实现
    (1)前面2个是算法思想,Intel在此基础上进行了一系列的设计。Intel分支预测包含3个单元:Branch Target Buffer(BTB),The Static Predictor,Return Stack

五. 指令的乱序执行

  1. 乱序执行
    指令在执行时,常常因为一些限制而等待。例如,MEM阶段访问的数据不在cache中,需要从外部存储器获取,这个动作需要几十个cycle,如果顺序执行,后面的指令MEM都要等待这个指令操作完成。乱序执行是说,先执行后面不依赖该数据的指令

  2. 指令相关性
    (1)寄存器相关:当2条指令公用寄存器时,他们就有可能相关。
         a. 先读后读

    ADD BX,AX   
    ADD CX,AX   #2条指令先后读取AX寄存器

         b. 先写后读 (RAW:Read after Write)

    ADD BX,AX
    ADD CX,BX   #后面的指令依赖前面的指令。先写BX,后读BX,指令间存在数据流动

         c. 先读后写 (WAR:Write after read)

    ADD BX,AX
    MOV AX,CX   #指令1读AX,指令2写AX,逻辑上本没有相关性,但X86cpu的寄存器太少,指令要公用寄存器,导致指令相关

         d. 先写后写(WAW)

    MOV AX,BX
    MOV AX,CX   #输出到同一个寄存器

    【注】:WAR和WAW在2条指令间没有数据流动,被称为伪相关。

  3. 控制相关
    前一条指令是跳转指令,而后一条指令的执行需要跳转指令的结果,这就是控制相关

三. 去除指令相关

  1. 去除数据相关
    去除数据相关的动作不是由cpu进行的,而是由编译器和程序员进行处理的

    x = a + b
    y = x + c
    z = y + d   // x,y,z产生深度相关
    
    // 改造后
    x = a + b
    y = b + c
    z = x + y   // 此时去除了x,y间的相关性
  2. 去除控制相关
    投机执行:cpu会根据跳转预测的结果,可能会提前把跳转后的指令放到跳转指令前面执行,是一种预测的投机行为。现代分支预测的准确性能达到98%以上,所以可以一定程度上去除控制相关

  3. 去除伪相关:WAW,WAR
    (1)处理器的ISA寄存器数目通常较少(暴露给程序员的寄存器),因此会导致多个变量映射到同一个寄存器中,这样即使指令逻辑上是不相关的,也会因为使用了同一个寄存器而产生相关。
    (2)通过把相同的ISA寄存器,映射到不同的物理寄存器来解决伪相关(名字相关)
    (3)映射策略:

    1. 将每条指令的目的寄存器映射到新的物理寄存器
    2. 将指令的源寄存器映射到ISA寄存器最近映射到的那个物理寄存器上
    3. 本条指令执行后,该目的寄存器映射的更早的物理寄存器就可以释放了
       
    4. 一开始R1,R2,R3,R4分别映射到F1,F2,F3,F4寄存器
    5. 第一条指令R3是目的寄存器,映射到新物理寄存器F5
    6. 这种方式会把物理寄存器用光,所以每次映射完毕都要实时释放:R4寄存器开始映射到F4寄存器,指令2结束后,R4映射到F6寄存器,此时,原先的F4寄存器就可以释放了

四.CPU如何进行乱序执行

  1. Buffer
    CPU内部要提供能够缓存多条指令的Buffer,才能达到乱序执行的效果

  2. 指令调度
    (1)指令有操作数和操作码,操作码描述指令做什么(cpu分配什么样的执行单元);而寄存器重命名后,目的寄存器总是新的,所以指令能否执行,和目的操作数已经无关,和操作码和源操作数有关
    (2)指令可以被执行的2个条件,除了这2个条件,指令不用再等待前面的指令执行完毕

    1. cpu中是否有空闲的执行单元执行这条指令(操作码)
    2. 该指令的源操作数是否已经准备好
  3. 指令结果顺序提交
    (1)指令执行顺序虽然是乱序的,但是指令结果的提交顺序一定要是顺序的。因为“精确中断”的存在
    (2)精确中断:指令执行过程中,来了一个中断,此时cpu要将ISA寄存器压栈,执行中断服务程序,然后执行中断后面的指令。而精确中断要求终端钱的指令全部执行,中断后的指令一个都不执行。而在乱序执行内核中,终端后面的指令可能放在中断前面的指令执行
    (3)所以,cpu引入重排序缓冲区,用来缓冲指令的执行结果,这些结果会被顺序的提交到寄存器中,来实现精确中断。

五. 处理器并行架构

  1. Flynn分类
    1966年,Flynn将处理器系统结构分成4类
    (1)SISD:处理器有一次处理一条指令,每条指令处理一份数据 (single instruction single data)
    (2)SIMD:一次处理一条数据,一条指令可以处理多分数据(数据并行)
    (3)MISD:一次处理多条指令,每条指令处理一份数据(此设计无用)
    (4)MIMD:一次处理多条指令,每条指令可以处理多分数据

  2. 指令并行
    (1)发射单元一次发射多条指令,就能达到指令并行(multi issue)
    (2)multi-issue的2种方式
       i. SuperScalar:超标量(硬件)
          a. 超标量是cpu内部增加一个硬件单元,负责把穿行指令输入进行并行化处理。
          b. 奔腾4采用超标量进行指令并行
       ii. VLIW:超长指令字(软件)
          a. 超长指令字是编译器或程序员在汇编语言中声明多条指令要在一个cycle内执行。||符号链接2条指令
          b. TI C6000 DSP采用超长指令字进行指令并行
    (3)超标量由于需要增加电路设计,增大了功耗,超标量在执行阶段制定并行。x86为了保证程序兼容性,不能不采用超标量,而后来的RISC处理器,则可以采用超长指令字VIEW结构
    (4)并行性上来讲,VIEW更胜一筹,因为他从源头实现了指令并行,擅长于数据密集型运算。但发生cache miss,执行跳转时,VIEW无能为力,采用乱序执行+超标量处理器则能将后面的指令提前执行,因此乱序+超标量适合负责的控制类程序。

  3. 数据并行
    (1)多媒体程序有一个特点:同一个操作应用于多个数据,于是SIMD产生出来
    (2)Intel的MMD,SSE2,SSE3,SSE4.1,SSE4.2,AVX指令集都是SIMD的。AMD的3DNOW!,SSE5指令集也是SIMD
    (3)MMX指令一次可处理64bit数据,SSX指令一次可处理128bit数据
    (4)几种不同的SSE指令
        (a) 垂直计算
          SSE指令把需要操作的2个寄存器中的数划分成对应的几个段,2个寄存器对应段中的数据进行操作
        (b) 水平计算
          2个源操作数来自于同一个寄存器
        (c) 标量计算
          a. 类似于垂直计算的2个寄存器分段,不同的是标量计算可以只计算2个寄存器中对应的1个段,而其他段保持不变
          b. 如下,只有x0和y0进行操作,而其他段保持不练
        

  4. 线程并行
    (1)软件多线程:时分复用操作系统
    (2)硬件多线程
       (a) 粗粒度硬件多线程:
             当处理器发现一个线程被长时间阻塞,eg:cache miss,发射器就发射另一个线程的指令
       (b) 细粒度硬件多线程
             处理器每个cycle发送不同线程的指令
       (c) 同时多线程
             超标量处理器同一时间可以发送多条指令,这些指令来自于不同线程
    (3)多核处理器架构
       

    i.   p:processor处理核心,c:cache缓存,s:switch用于核间通信。线条表示通信路径
    ii.  Bus Multicore结构多核:设计简单,但任意两个核心通信都要占用总线,导致其他核心不能通信,降低效率。 iii. Swich Multicore结构多核:任意两个核心间右独立的通信连线。1,2核心通信不会阻塞3,4核心通信。但这种方式消耗大量互联资源,4核心时通常使用SwitchMulticore,核心数量再多则资源浪费
    v.  Ring Multicore结构多核:改进了Bus结构,1和3通信要经过2,相邻的两个核心通信速度最快.通常8核心使采用这种结构

    vi. Mesh Multicore结构多核:类似于二维的Ring结构。核心太多时,switch结构就会导致连线过于复杂。Mesh结构利于扩展,效率高。64核心等众核可以采用这个设计。

(4)各种硬件多线程的对比
      
      (a)图形解释: 每行代表一个cycle
      (b)每列代表一个功能单元
         i.    第一个是单线程处理器,大量功能单元空闲,有时if,id阶段的所有控制单元空闲
         ii.   细粒度多线程:每个cycle发送不同线程的指令
         iii.  粗粒度多线程:当cpu发现线程阻塞时(cache miss),就发射另一个线程的指令
         iv.  多核心技术:每个核心2个功能单元,没有硬件多线程,2个核心分别处理2个不同的线程
         V.   同时多线程:功能单元的利用率最高,超标量cpu同时发送多个线程的指令,每个线程使用不同的功能单元

posted @ 2016-12-18 16:40  Dragonir  阅读(8584)  评论(1编辑  收藏  举报