指令流水线总结

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同时发送多个线程的指令,每个线程使用不同的功能单元

时间: 2024-08-29 00:17:48

指令流水线总结的相关文章

指令流水线归纳总结

      Pipelining 流水线   中山大学         报告目录   一.      思维导图--------------------------------- 3 二.      课件理解--------------------------------- 4 三.      名词解释--------------------------------- 14 四.      归纳总结--------------------------------- 15 五.      参考文献-

计算机组成原理——指令流水线

计算机组成原理——指令流水线 1. 综述 为提高CPU利用率,加快执行速度,将指令分为若干个阶段,可并行执行不同指令的不同阶段,从而多个指令可以同时执行.在有效地控制了流水线阻塞的情况下,流水线可大大提高指令执行速度.博客园知识库:CPU流水线的探秘之旅 经典的五级流水线:取址.译码.执行.访问内存(读或写).结果写回寄存器.链接:史上最经典的5级流水线 流水线阻塞的情况有三种(baidu知道): 1. 结构相关:指令重叠执行的过程中,硬件资源满足不了指令重叠执行的要求,发生资源冲突,这时将产生

Linux学习笔记:【002】ARM指令流水线

指令的处理 在CPU中,对于指令的处理一般分为: 1.取指令阶段 取指令(Instruction Fetch,IF)阶段是将一条指令从主存中取到指令寄存器的过程. 程序计数器PC中的数值,用来指示当前指令在主存中的位置. 当一条指令被取出后,PC中的数值将根据指令字长度而自动递增: 若为单字长指令,则(PC)+1àPC: 若为双字长指令,则(PC)+2àPC,依此类推. 2.指令译码阶段 取出指令后,计算机立即进入指令译码(Instruction Decode,ID)阶段. 在指令译码阶段,指令

指令流水线的吞吐率

假设一个四段流水线,取指段的时间为t,译码段的时间为t,取数段的时间为3t,执行段的时间为t. 为了便于计算假设取指和译码段总是连续执行的,每隔一段的时间(取最长一段的时间,例如上面的取数3t)下一条指令执行 一条指令之后每隔一段的时间(取最长一段的时间,例如上面的取数3t),就会执行完一条指令. 流水线时间计算公式: 一条指令所需时间 + (指令条数-1) * 时间最长的指令的一段(例如上面的取数3t) 吞吐率公式: 指令条数 / 流水线时间

GCC在C语言中内嵌汇编 asm __volatile__ 【转】

转自:http://blog.csdn.net/pbymw8iwm/article/details/8227839 在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C 变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可, GCC会自动插入代码完成必要的操作. 1.简单的内嵌汇编 例: __asm__ __volatile__("hlt"); "__asm__"表示后面的

深入理解Java内存模型(1 ) -- 基础(转载)

原文地址:http://www.infoq.com/cn/articles/java-memory-model-1 并发编程模型的分类 在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体).通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信.在消息传递的并发模型里,线程之间没有公共状态,线

CS考研_统考大纲

序号 政治 外语 业务课一 业务课二 1 (101)思想政治理论 (201)英语一 (301)数学一 (408)计算机学科专业基础综合 以上是计算机全国统考考试科目,三门公共课非统考基本也都是这三个,大家如果看到非统考的科目如果是三个1,就可以直接来参考我这里列出的大纲了!所以在此,我就直接列出最近的2015年考研这四个的考试大纲: 政治101: Ⅰ.考试性质 思想政治理论考试是为高等院校和科研院所招收硕士研究生而设置的具有选拔性质的全国招生考试科目,其目的是科学.公平.有效地测试考生掌握大学本

(转)C++学习建议

原文:http://www.cnblogs.com/xilentz/archive/2010/05/01/1725460.html 博主传达了大量的去其糟粕的思想,所以,我只取了他对如何学习C++的建议,我还标记那些我觉得重要的话,方便以后提醒自己. C++是一门强大的语言,我们没有任何理由不学习他,领略其中的风采. 建议1:有辨别力地阅读(包括那些被广泛称为"经典"的)C++书籍. 如果书中介绍的某块内容你认为在日常编程中基本不会用到(属于20%场景),那么也许最好的做法是非常大概的

volatile变量

一.volatile概述 volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址(内存)中读取数据.如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象.下面举例说明. volatile一般用于修饰多线程间被多个任务共享的变量和并行设备硬件寄存器等. 二.volatile使用案例 在DSP开发中,经常需要等待某个事件的触发,所以经常会写