【计组】数据通路和流水线设计--《深入浅出计算机组成原理》(三)

news2024/10/6 0:35:34

 课程链接:深入浅出计算机组成原理_组成原理_计算机基础-极客时间 

目录

一、建立数据通路

(一)组合逻辑电路

1、指令周期

2、数据通路

3、CPU所需硬件电路

(二)时序逻辑电路

1、时序逻辑电路可以解决的问题

二、面向流水线的指令设计

(一)现代处理器的流水线设计

​编辑

(二)“主频战争”带来的超长流水线

1、超长流水线面临的问题

三、冒险和预测

(一)Hazard

1、结构冒险

2、数据冒险

3、通过流水线停顿解决数据冒险

(二)操作数前推 

(三)乱序执行

(四)控制冒险

1、缩短分支延迟

2、分支预测

3、动态分支预测


一、建立数据通路

(一)组合逻辑电路

1、指令周期

  • Fetch(取得指令):从 PC 寄存器里找到对应的指令地址,根据指令地址从内存里把具体的指令,加载到指令寄存器中,然后把 PC 寄存器自增,好在未来执行下一条指令。
  • Decode(指令译码):根据指令寄存器里面的指令,解析成要进行什么样的操作,是 R、I、J 中的哪一种指令,具体要操作哪些寄存器、数据或者内存地址。
  • Execute(执行指令):实际运行对应的 R、I、J 这些特定的指令,进行算术逻辑操作、数据传输或者直接的地址跳转。
  • 重复进行 1~3 的步骤。

 除了 Instruction Cycle,在 CPU 里还有另外两个常见的 Cycle。一个叫 Machine Cycle,机器周期或者 CPU 周期。CPU 内部的操作速度很快,但是访问内存的速度却要慢很多。每一条指令都需要从内存里面加载而来,所以我们一般把从内存里面读取一条指令的最短时间,叫作 CPU 周期。还有一个是Clock Cycle,也就是时钟周期以及机器的主频。一个 CPU 周期,通常会由几个时钟周期累积起来。一个 CPU 周期的时间,就是这几个 Clock Cycle 的总和。 

2、数据通路

数据通路就是处理器单元,通常由两类原件组成:

第一类叫操作元件,也叫组合逻辑元件(Combinational Element),就是 ALU。ALU的功能就是在特定的输入下,根据下面的组合电路的逻辑,生成特定的输出。

第二类叫存储元件,也有叫状态元件(State Element)的。比如我们在计算过程中需要用到的寄存器,无论是通用寄存器还是状态寄存器,其实都是存储元件。

通过数据总线的方式,把它们连接起来,就可以完成数据的存储、处理和传输了,这就是所谓的建立数据通路。

控制器会重复“Fetch - Decode - Execute“循环中的前两个步骤,然后把最后一个步骤,通过控制器产生的控制信号,交给 ALU 去处理。

3、CPU所需硬件电路

  • 首先需要ALU,它是一个没有状态的,根据输入计算输出结果的第一个电路。
  • 第二,需要有一个能够进行状态读写的电路元件,也就是寄存器,能够存储到上一次的计算结果。
  • 第三,需要有一个“自动”的电路,按照固定的周期,不停地实现 PC 寄存器自增,自动地去执行“Fetch - Decode - Execute“的步骤。
  • 第四,需要有一个“译码”的电路。无论是对指令进行 decode,还是根据内存地址获取对应的数据或者指令,都需要通过一个电路去寻找数据。

CPU在空闲状态就会停止执行,具体来说就是切断时钟信号,CPU的主频就会瞬间降低为0,功耗也会瞬间降低为0。由于这个空闲状态是十分短暂的,所以你在任务管理器里面也只会看到CPU频率下降,不会看到降低为0。当CPU从空闲状态中恢复时,就会接通时钟信号,这样CPU频率就会上升。所以你会在任务管理器里面看到CPU的频率起伏变化。

(二)时序逻辑电路

1、时序逻辑电路可以解决的问题

  • 自动运行问题。时序电路接通之后可以不停地开启和关闭开关,进入一个自动运行的状态,使得我们上一讲说的,控制器不停地让 PC 寄存器自增读取下一条指令成为可能。
  • 存储问题。通过时序电路实现的触发器,能把计算结果存储在特定的电路里面,而不是像组合逻辑电路那样,一旦输入有任何改变,对应的输出也会改变。
  • 各个功能按照时序协调。无论是程序实现的软件指令,还是到硬件层面,各种指令的操作都有先后的顺序要求。时序电路使得不同的事件按照时间顺序发生。

二、面向流水线的指令设计

(一)现代处理器的流水线设计

CPU 的指令执行过程,是由各个电路模块组成的。取指令的时候,需要一个译码器把数据从内存里面取出来,写入到寄存器中;指令译码的时候,需要另外一个译码器,把指令解析成对应的控制信号、内存地址和数据;到了指令执行的时候,需要的则是一个完成计算工作的 ALU。

这样一来,就不用把时钟周期设置成整条指令执行的时间,而是拆分成完成一个一个小步骤需要的时间。同时,每一个阶段的电路在完成对应的任务之后,也不需要等待整个指令执行完成,而是可以直接执行下一条指令的对应阶段。 这样的协作模式,就是指令流水线。这里面每一个独立的步骤,就称为流水线阶段或者流水线级(Pipeline Stage)。

如果某一个操作步骤的时间太长,就可以考虑把这个步骤,拆分成更多的步骤,让所有步骤需要执行的时间尽量都差不多长。这样,也就可以解决我们在单指令周期处理器中遇到的,性能瓶颈来自于最复杂的指令的问题。

流水线可以增加吞吐率,但是增加流水线深度,是有性能成本的。每一级流水线对应的输出,都要放到流水线寄存器(Pipeline Register)里面,然后在下一个时钟周期,交给下一个流水线级去处理。每增加一级的流水线,就要多一级写入到流水线寄存器的操作。所以,设计合理的流水线级数也是现代 CPU 中非常重要的一点。

可以看到,为了能够不浪费 CPU 的性能,把指令的执行过程,切分成一个一个流水线级,来提升 CPU 的吞吐率。而 CPU 的设计,是由一个个独立的组合逻辑电路串接起来形成的,天然能够适合这样采用流水线“专业分工”的工作方式。因为每一级的 overhead,一味地增加流水线深度,并不能无限地提高性能。同样地,因为指令的执行不再是顺序地一条条执行,而是在上一条执行到一半的时候,下一条就已经启动了,所以也给我们的程序带来了很多挑战。

(二)“主频战争”带来的超长流水线

乍看起来,流水线技术是一个提升性能的灵丹妙药。它通过把一条指令的操作切分成更细的多个步骤,可以避免 CPU“浪费”。每一个细分的流水线步骤都很简单,所以单个时钟周期的时间就可以设得更短。这也变相地让 CPU 的主频提升得很快。

但并不能简单地通过 CPU 的主频,来衡量 CPU 乃至计算机整机的性能。因为不同的 CPU 实际的体系架构和实现都不一样。同样的 CPU 主频,实际的性能可能差别很大。

增加流水线深度,在同主频下,其实是降低了 CPU 的性能。因为一个 Pipeline Stage,就需要一个时钟周期。那么我们把任务拆分成 31 个阶段,就需要 31 个时钟周期才能完成一个任务;而把任务拆分成 11 个阶段,就只需要 11 个时钟周期就能完成任务。事实上,因为每个 Stage 都需要有对应的 Pipeline 寄存器的开销,这个时候,更深的流水线性能可能还会更差一些。

1、超长流水线面临的问题

(1)功耗问题。提升流水线深度,必须要和提升 CPU 主频同时进行。因为在单个 Pipeline Stage 能够执行的功能变简单了,也就意味着单个时钟周期内能够完成的事情变少了。所以,只有提升时钟周期,CPU 在指令的响应时间这个指标上才能保持和原来相同的性能。同时,由于流水线深度的增加,需要的电路数量就变多了,也就是使用的晶体管变多了。主频的提升和晶体管数量的增加都使得 CPU 的功耗变大了。

(2)流水线技术带来的性能提升,是一个理想情况。在实际的程序执行中,并不一定能够做得到,因为有时候指令之间是存在依赖关系的,而流水线里同时执行的指令不能具备依赖关系。

三、冒险和预测

(一)Hazard

流水线设计需要解决的三大冒险,分别是结构冒险(Structural Hazard)、数据冒险(Data Hazard)以及控制冒险(Control Hazard)。

1、结构冒险

结构冒险,本质上是一个硬件层面的资源竞争问题,也就是一个硬件电路层面的问题(可以靠增加硬件资源的方式来解决)。

对于访问内存数据和取指令的冲突,一个直观的解决方案就是把我们的内存分成两部分,让它们各有各的地址译码器。这两部分分别是存放指令的程序内存和存放数据的数据内存。这种解决方案在计算机体系结构里叫作哈佛架构(Harvard Architecture),来自哈佛大学设计Mark I 型计算机时候的设计。

目前使用的CPU,采用的是冯·诺依曼体系结构,也叫普林斯顿架构。并没有把内存拆成程序内存和数据内存这两部分。因为如果那样拆的话,就没有办法根据实际的应用去动态分配程序指令和数据需要的内存空间。虽然解决了资源冲突的问题,但是也失去了灵活性。

不过,借鉴了哈佛结构的思路,现代的 CPU 虽然没有在内存层面进行对应的拆分,却在 CPU 内部的高速缓存部分进行了区分,把高速缓存分成了指令缓存(Instruction Cache)和数据缓存(Data Cache)两部分。

内存的访问速度远比 CPU 的速度要慢,所以现代的 CPU 并不会直接读取主内存。它会从主内存把指令和数据加载到高速缓存中,这样后续的访问都是访问高速缓存。而指令缓存和数据缓存的拆分,使得我们的 CPU 在进行数据访问和取指令的时候,不会再发生资源冲突的问题了。

2、数据冒险

数据冒险,就是同时在执行的多个指令之间,有数据依赖的情况。这些数据依赖,可以分成三大类,分别是先写后读(Read After Write,RAW)、先读后写(Write After Read,WAR)和写后再写(Write After Write,WAW)。

其中,先写后读叫数据依赖,先读后写叫反依赖,写后再写叫输出依赖。

3、通过流水线停顿解决数据冒险

流水线架构的核心,就是在前一个指令还没有结束的时候,开始执行后面的指令。

解决这些数据冒险的一个最简单也是最笨的方法叫流水线停顿(Pipeline Stall),或者叫流水线冒泡(Pipeline Bubbling):指令译码的时候,会拿到对应指令所需要访问的寄存器和内存地址,如果发现后面执行的指令,对前面执行的指令有数据层面的依赖关系,就“再等等”,让整个流水线停顿一个或者多个周期。

并不是让流水线停下来,而是在执行后面的操作步骤前面,插入一个 NOP 操作,也就是执行一个其实什么都不干的操作。这个插入的指令,就好像一个水管(Pipeline)里面,进了一个空的气泡,又被叫作流水线冒泡。

(二)操作数前推 

 操作数前推的逻辑含义是转发——前面指令的执行结果,直接“转发”给了依赖其结果的后面指令的 ALU 作为输入;硬件含义是旁路(Bypassing)——为了能够实现转发”,需要在 CPU 的硬件里单独拉一根信号传输的线路出来,使得 ALU 的计算结果能够重新回到 ALU 的输入里来。这样的一条线路,就是“旁路”。它越过(Bypass)了写入寄存器,再从寄存器读出的过程。

操作数前推的解决方案不但可以单独使用,还可以和流水线冒泡一起使用。有些时候,的操作数前推并不能减少所有“冒泡”,只能去掉其中的一部分。仍然需要通过插入一些“气泡”来解决冒险问题。

(三)乱序执行

无论是流水线停顿,还是操作数前推,归根到底,只要前面指令的特定阶段还没有执行完成,后面的指令就会被“阻塞”住。但是这个“阻塞”很多时候是没有必要的。因为尽管代码生成的指令是顺序的,但是如果后面的指令不需要依赖前面指令的执行结果,完全可以不必等待前面的指令运算完成。这样的解决方案,在计算机组成里面,被称为乱序执行(Out-of-Order Execution,OoOE)。

  • 在取指令和指令译码的时候,乱序执行的 CPU 和其他使用流水线架构的 CPU 是一样的。它会一级一级顺序地进行取指令和指令译码的工作。
  • 在指令译码完成之后,CPU 不会直接进行指令执行,而是进行一次指令分发,把指令发到一个叫作保留站(Reservation Stations)的地方。
  • 这些指令不会立刻执行,而要等待它们所依赖的数据,传递给它们之后才会执行。一旦指令依赖的数据来齐了,指令就可以交到后面的功能单元(Function Unit,FU),其实就是 ALU,去执行了。
  • 指令执行的阶段完成之后,并不是立刻把结果写回到寄存器里面去,而是把结果再存放到一个叫作重排序缓冲区(Re-Order Buffer,ROB)的地方。
  • 在重排序缓冲区里,CPU 会按照取指令的顺序,对指令的计算结果重新排序。只有排在前面的指令都已经完成了,才会提交指令,完成整个指令的运算结果。
  • 实际的指令的计算结果数据,并不是直接写到内存或者高速缓存里,而是先写入存储缓冲区(Store Buffer 面,最终才会写入到高速缓存和内存里。

可以看到,在乱序执行的情况下,只有 CPU 内部指令的执行层面,可能是“乱序”的。只要我们能在指令的译码阶段正确地分析出指令之间的数据依赖关系,这个“乱序”就只会在互相没有影响的指令之间发生。

即便指令的执行过程中是乱序的,我们在最终指令的计算结果写入到寄存器和内存之前,依然会进行一次排序,以确保所有指令在外部看来仍然是有序完成的。

乱序执行,极大地提高了 CPU 的运行效率。核心原因是,现代 CPU 的运行速度比访问主内存的速度要快很多。如果完全采用顺序执行的方式,很多时间都会浪费在前面指令等待获取内存数据的时间里。CPU 不得不加入 NOP 操作进行空转。而现代 CPU 的流水线级数也已经相对比较深了,到达了 14 级。这也意味着,同一个时钟周期内并行执行的指令数是很多的。

(四)控制冒险

在结构冒险和数据冒险中,所有的流水线停顿操作都要从指令执行阶段开始。流水线的前两个阶段,也就是取指令(IF)和指令译码(ID)的阶段,是不需要停顿的。CPU 会在流水线里面直接去取下一条指令,然后进行译码。取指令和指令译码不会需要遇到任何停顿,这是基于所有的指令代码都是顺序加载执行的假设。在执行的代码中,一旦遇到 if…else 这样的条件分支,或者 for/while 循环,就会不成立。

在 jmp 指令发生的时候,CPU 可能会跳转去执行其他指令。jmp 后的那一条指令是否应该顺序加载执行,在流水线里面进行取指令的时候,我们没法知道。要等 jmp 指令执行完成,去更新了 PC 寄存器之后,才能知道,是执行下一条指令,还是跳转到另外一个内存地址,去取别的指令。这种为了确保能取到正确的指令,而不得不进行等待延迟的情况,就是控制冒险(Control Harzard)

1、缩短分支延迟

解决控制冒险的第一个办法。

条件跳转指令其实进行了两种电路操作:第一种事进行条件比较,第二种是进行实际的跳转。

可以将条件判断、地址跳转,都提前到指令译码阶段进行,而不需要放在指令执行阶段。需要在 CPU 里面设计对应的旁路,在指令译码阶段,就提供对应的判断比较的电路。

这种方式,本质上和前面数据冒险的操作数前推的解决方案类似,就是在硬件电路层面,把一些计算结果更早地反馈到流水线中。这样反馈变得更快了,后面的指令需要等待的时间就变短了。

但只是改造硬件,并不能彻底解决问题。跳转指令的比较结果,仍然要在指令执行的时候才能知道。在流水线里,第一条指令进行指令译码的时钟周期里,其实就要去取下一条指令了。这个时候,还没有开始指令执行阶段,自然也就不知道比较的结果。

2、分支预测

让CPU 来猜测,条件跳转后执行的指令,应该是哪一条。

最简单的分支预测技术,叫作“假装分支不发生”。顾名思义,就是仍然按照顺序,把指令往下执行。这样的预测方法,是一种静态预测技术。

分支预测正确意味着将节省下来本需要停顿等待的时间。如果分支预测失败就把后面已经取出执行的部分丢弃掉。这个丢弃的操作,在流水线里面,叫作 Zap 或者 Flush。所以,CPU 需要提供对应的丢弃指令的功能,通过控制信号清除掉已经在流水线中执行的指令。只要对应的清除开销不要太大,就是划得来的。

3、动态分支预测

用一个比特,记录当前分支的比较情况,用当前分支的比较情况,来预测下一次分支时候的比较情况。这种策略叫一级分支预测(One Level Branch Prediction),或者叫 1 比特饱和计数(1-bit saturating counter)

还可以引入一个状态机(State Machine)存储更多的信息用来进行预测(如果连续下雨,我们就认为更有可能下雨。之后如果有一天放晴了,我们仍认为会下雨),状态机里,一共有 4 个状态,需要 2 个比特来记录。这样的策略,叫作 2 比特饱和计数,或者叫双模态预测器(Bimodal Predictor)

 课程链接:深入浅出计算机组成原理_组成原理_计算机基础-极客时间 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/124347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python爬虫进行正则数据解析实战

今天继续给大家介绍Python爬虫相关知识,本文主要内容是Python爬虫进行正则数据解析实战。 一、需求分析 今天,我们尝试使用re正则表达式来对爬取到的页面进行数据解析。需求如下: 针对网页:https://blog.csdn.net/weixin_402282…

第三十二章 数论——组合数详解(1)

第三十二章 数论——组合数的多种求法一、数学基础二、组合数——递推公式1、题目2、思路3、代码三、组合数——快速幂1、问题:2、分析一、数学基础 组合数来自于高中排列组合的知识: 我们从aaa个小球中随机一次性取出bbb个,所有的取法记作…

Numpy学习记录

numpy.ma.ravel 返回一个连续的扁平数组。 参考: numpy中的ravel()方法使用介绍https://blog.csdn.net/weixin_44025103/article/details/125062287 Python numpy.ravel函数方法的使用https://www.cjavapy.com/article/870/ numpy.meshgrid 二维坐标系中,X轴…

图片如何批量重命名?一步一步教会你

爱拍照的小伙伴应该都知道,无论是手机还是相机拍出来的照片,导入电脑后,它的默认名称都是一串长长的字符。不仅让桌面看起来杂乱无章,还会给我们在查找图片时带来诸多的不便。其实我们可以通过软件对这些图片进行批量重命名的&…

线性代数基础----矩阵

秩的理解: 举证的秩的理解: 特征值和特征向量 线性相关和线性无关 向量的拉伸和旋转: 特征值和特征向量: 特征值: 特征向量: 用来描述矩阵的信息 特征向量 和 特征值之间的关系 拳击:方向和力量 特征值和特征向量的物理意义: 表示相关的重要性 特征值和特征向量:进行提…

阳了在家没事干?教大家用python在家做一个万能看视频软件,绝对正经啦~

嗨害大家好鸭!我是小熊猫~ 我前阵子不小心阳了,该说不说,真挺难受的 我在家真的就是纸巾热水不离手, 楼下水果店老板娘还说我年轻人身体怎么这么差… 哼我阳过之后我就锻炼去!!! 效果展示 有…

如何删除掉设备和驱动器下百度网盘的图标

电脑安装百度网盘后,在设备和驱动器这里会有百度网盘的图标,如下图所示。 对有强迫症的人来说,这很难受,就一定要想办法删除掉该图标,那么具体怎么操作呢? 你如果在设备和驱动器下右击百度网盘&#xff0c…

12月小报|读小报,涨知识

本期知识小集的主要内容包括:• Flutter桥调用请注意结果反馈• Flutter await代码带来的潜在并发• Flutter FPS 高不代表一定流畅• Flutter新渲染引擎impeller尝鲜Flutter桥调用请注意结果反馈通过桥来拓展Flutter的能力,是非常通用的Flutter开发场景…

Weda创建视图表格

这边我们先创建一个数据集。 在更多操作里面分布这个数据集。 点击编辑,选择视图配置。 在下面这个界面,新建视图。 在操作里面,点击更多,发布这个视图。 我们点击编辑设置视图的第一行(表列设置)。 点击…

马上又是新的一年了 “跨年倒计时”送给大家

🏆今日学习目标: 🍀跨年倒计时 ✅创作者:林在闪闪发光 ⏰预计时间:30分钟 🎉个人主页:林在闪闪发光的个人主页 🍁林在闪闪发光的个人社区,欢迎你的加入: 林在闪闪发光的…

pytorch 深度学习

第二章 回归问题 即使是最简单的线性回归,由于观测误差的存在,也不可能找到一个满足所有样本的函数。因此,我们退而求其次,寻找到一个满足大部分样本点的直线。那么如何衡量这个呢,我们可以求出所有样本点真实值和预测值的误差,满足总误差最小的就是最好的。 连续值…

年关在即,源站安全如何保障?|ScanV(云监测)重保哨兵值守

2022年,全球重大网络安全事件频发,大规模数据泄露、漏洞利用事件、勒索软件攻击等网络犯罪威胁持续上升。网络安全形势日趋严峻,对政府、党政机关、央企国企业务安全造成严重威胁。据知道创宇云防御2022年截至目前的数据统计,平均…

如何去掉任务栏的英伟达图标,并阻止英伟达服务自启动

进入服务,找到NVIDIA的两个服务项,右键属性,停止服务,英伟达图标自动消失。 再把启动方式改为手动,下次就不会开机自启。

WebGL

1、WebGL介绍 1.1 WebGL不足 效果较差:较于桌面开发API:Direct3D、OpenGL、UE、Unity。 开发成本:熟悉并掌握一定的数据知识,例如:线性代数。 硬件要求:开发及部署系统硬件要求较高,尤其GPU…

炒股经验总结

判断大盘调整到后期 高位补跌,大盘出现大阴线 板块炒作到尾声 板块龙头开始回调,炒作炒到边角料 良好的量价关系 放量突破,缩量回踩 跌破前低3天收不回来止损,无关涨跌,这是底线,以后涨了也不后悔&am…

正点原子IMX6ULL-Linux驱动开发

目录 第一期 第6讲 Ubuntu终端操作与Shell命令 第一期 第8讲 Ubuntu文件系统结构 第一期 第9讲 Ubuntu磁盘管理 第一期 第10讲 Ubuntu压缩与解压缩 第一期 第11讲 Ubuntu用户和用户组 第一期 第12讲 Ubuntu文件权限 第一期 第13讲Linux连接文件 第一期 第14讲 vim编辑器 第一期…

专利申请与专利转让有什么区别?

专利申请与专利转让有什么区别? 一、专利转让和专利申请的流程不同 专利转让的流程: (1)找到合适的转让途径; (2)专利转让人和专利受让人进行签署专利权转让合同; (3)填写专利转让相关文件,需要严格按照国家规定形式进行填写&#…

公司想要做自动化测试,那么自动化测试发展和价值回报有哪些?

很长一段时间,都在思考,怎么能通俗的看待自动化测试的收效 自动化测试到底能不能成为一种趋势? 自动化测试到底能不能形成一种规模? 自动化测试到底能不能成为我们的利器? 自动化测试到底能对我们的职业带来何种发…

为什么有的公司会禁用spring声明式事务

在之前我一直偏向于使用声明式事务,我一直觉得声明式事务比较好用。相比于编程式事务,使用声明式事务时只需要加上一个注解,spring就能够帮助我们完成所有的事务控制。反观编程式事务却需要我们自己去控制事务的提交和回滚,这种代…

【JavaSE】Clonable?关于深拷贝与浅拷贝那些事儿咱们一次聊明白

💁 个人主页:黄小黄的博客主页 ❤️ 支持我:👍 点赞 🌷 收藏 🤘关注 🎏 格言:All miracles start from sometime somewhere, make it right now. 本文来自专栏:JavaSE从入…