【计算机体系结构-02】机器模型

news2024/9/24 11:21:27

1. 机器模型 (Machine Model)

计算机的基本工作就是进行运算,那么计算就需要有用来处理计算方法的处理单元和提供或保存数值的存储单元。一般将用来处理计算方法的处理单元称为 算术逻辑单元 (ALU--Arithmetic Logic Unit)。在一个计算过程中可能会是这样的一个流程,从存储单元中取出一些数据,放进 ALU 中进行计算,然后将计算结果保存到存储单元中。

那么完成这个计算流程的机器模型是如何利用存储单元和算术逻辑单元构建起来的?该如何访问存储器?机器模型该允许什么样的指令和操作?操作数怎么获取从哪来?结果又该放在哪?


首先明确一点,机器模型不是指令集架构,上篇文章《【计算机体系结构-01】指令集体系结构、微体系结构简介》已经说明了,指令集架构是为软件编程提供的一种规范,它是关于计算和操作指令如何封装制定的一套标准,而 机器模型则是一种更加基础的理论,是构建出存储器和 ALU 的物理结构,从而确定数据如何移动,又如何从 ALU 移动到存储器。

2. 几种简单的机器模型

2.1. 栈基架构 (Stack-Based Architecture)

不管你信不信,在一个处理器中是可以没有寄存器的(寄存器不是必要的)。OK,来看一个历史上实际搭建过的处理器,它的结构非常简单。
在这里插入图片描述

那么可以看图片中的这种机器模型,利用栈结构存储,而栈只是一个存储器,并非寄存器。栈的结构相信大家已经很清楚,数据会遵循先入后出的顺序入栈、出栈。这个 机器模型的工作流程很简单,只需要从栈顶开始取两个元素,通过 ALU 计算,然后将结果保存栈顶。

2.2. 累加器架构 (Accumluator Architecture)

下面是一个累加器的结构。
在这里插入图片描述
累加器,顾名思义会对一个数值不停的做加法,而得到的结果最终也只会有一个,操作数只有一个,因此在处理器中只会有一个寄存器,所有的计算都是自动的或不可见的。同时还可以提供给累加器另外一个操作数,即除了寄存器中的操作数还提供一个来自存储器 (Memory) 的操作数,此时则需要给该操作数命名。

2.3. 寄存器-存储器架构 (Register-Memory Architrecture)

在这里插入图片描述

根据上面累加器的两个参数方案那么很容易想到上图这种架构,寄存器-存储器架构,即一个源操作数来自于存储器,该操作数需要命名,而另一个操作数可以来自于寄存器(不需要命名),然后,还可以有一个目的存储(可选),用于保存计算结果的存储位置,同样需要命名才可以。那么这种结构一个操作最多需要有两个命名的操作数。

2.4. 寄存器-寄存器架构 (Register-Register Architecture)

在这里插入图片描述
那么寄存器-寄存器架构,两个操作数均来自于处理器中的寄存器,同时两者均需被命名,当需要有目的存储时,还需要为目的存储操作数命名。

2.5. 以上几种架构的比较

在这里插入图片描述
这四种架构,需要被命名的操作数数目分别为 012或32或3,相信通过上文你已经清楚了,后面两种架构为什么是 2或3,因为有时候数据计算结果的保存目的地会隐式的确定无需命名,当需要明确指出时,那么就会多一个命名操作数。

例如,X86 架构中的第一个操作数总是作为结果的保存位置,因此它会少命名一个操作数。例如 MIPSRISC 架构则需要将三个操作数全部命名。

3. 栈基架构 (Stack-Based Architecture)

3.1. 栈基架构机器模型工作原理

栈基架构虽然是个很老的架构模型,但是很经典。过去的 Burrough's 5000 (B5000) 机器就是使用的栈基架构(1960年),近一点的 Java 虚拟机实际上也是利用栈结构实现。
在这里插入图片描述

用一个例子🌰来说明栈基架构机器模型的工作原理,来看上图这个计算表达式,首先用 a 加上 b * c,然后整个括号的计算结果除以 a 加上 d * c 再减去 e 的运算结果才得到最终的表达式结果。这对于我们来说计算这个表达式很简单,但对于计算机来说就没那么直接了,甚至有点复杂。
在这里插入图片描述

实际上计算机会将这个表达式分解并生成一个 解析树,就上图这样。一个二叉树的结构,所有的叶子节点上是需要做计算的数,其它节点则是运算符。开始先由 b 乘以 c 加上 a,用这个结果除以另一边的子表达式。整个二叉树用顺序结构保存即为计算机应该要做的操作表达式,像下面这样。

在这里插入图片描述
如果现在使用的是栈基架构的机器模型,那么就意味着会有一个求值栈,首先 push a 到栈中,接着 push b 到栈中、push c 到栈中,当遇到运算符,拿栈顶的两个元素做对应的运算,再保存到栈中,依次类推就会得到下面这样的栈操作流程。

在这里插入图片描述
通过这样的栈操作流程,就可以把左边的子表达式结果计算出来,依照该方法继续姐可以计算出整个表达式的结果。你会发现计算机在计算这样长的表达式,实际上可以在一个很小的栈中完成对整个表达式的计算,并且有一个好处是,完全不需要对任何一个操作数命名(因为操作数是固定的,总是拿栈顶的两个元素作运算)。因此这个机器模型可以让你跑任何现实的程序。


需要注意的一点是,栈是处理器状态的一部分,并且很多时候在指令集体系架构的角度来看栈是无限大的。但在实际上,计算机中的栈是有限长的,因为在计算机中无论如何都不可能出现一个无限长且真实存在的栈。它只是在概念上是无限的,因此你需要保证不能发生栈溢出(内存溢出、主存储器溢出)。 例如,上面例子中的表达式如果是很长,或者产生的解析树太深,都会导致栈溢出的可能。

3.2. 栈基架构的设计和优化(求值栈)

🤕基础设计:
假如现在有一个指令集架构的微架构实现是基于栈基架构的,由于求值栈在进行计算时总是需要两个操作数,那么就将求值栈中的顶部的两个元素的地址是保存在寄存器中,而其余的则保存在主存中(包含溢出的元素)。每一次入栈操作都会产生一次内存引用,每一次出栈操作也会有一次内存引用(出栈会将栈中的元素返回到主存中)。而且更重要的是,当发生栈溢出后,将会增加额外的内存引用,因为在做栈操作时需要将主存的数据先拿出来。显然,这样的结构让操作产生的步骤非常的多(内存引用)。

🤪优化设计:
那么站在微系体系架构优化的角度考虑,将栈中的 N 个元素全部直接保存在寄存器中(栈中的所有元素 N 小于或等于可用的寄存器数),这样每次执行 pushpop 操作时就不必执行内存引用,就只有当发生栈溢出时才需要增加一步内存引用操作。

Okay,用优化后的栈来计算一下,继续再用上面的例子,现在给出限制条件,限制栈的大小为 2,即最多只能放两个元素,超过则会发生溢出。

在这里插入图片描述

先来看这个例子都会做什么,首先 push 了三次元素进栈,然后对顶部的两个元素做乘法,然后再做加法,之后再三次 push 操作,栈顶两个元素相乘,用结果加上 a,接着 push e,用上一次结果减去 e,再将栈顶两个元素做除法完成计算。

但是栈中只能存放两个元素,那么来看看在栈操作中具体会发生什么事情,

  • 第一步 push a,这时候时空栈,没问题,直接进,非空非满栈;
  • 第二步,接着 push b,也 OK,此时为满栈;
  • 第三步,当 push c 的时候,发现栈已经满了,这个时候还想要再 push,那就会发生 栈下溢 (Underflow),此时会先将栈底元素也就是 a,保存到主存中 (Main Memory),该操作会发生一次内存引用,图中的 ss(a) 表示将 a store 到主存中,R0 寄存器的值改变为 a 的主存地址(这便发生额外的一次内存引用),然后将 c 压入栈顶。
  • 第四步,将栈顶的两个元素做乘法运算,将结果 b * c 保存到 R1,栈元素减一,同时从 a 的主存地址获取 (fetche) a(发生内存引用)也就是图中的 sf(a),将 a 保存到 R0 中,此时栈中有两个元素,栈顶是 R1 = b * c,满栈;
  • 第五步,将栈顶的两个元素做加法运算,将结果 a + b * c 入栈保存到 R0,栈元素减一,此时栈中只有一个元素即为栈顶元素 R0 = a + b * c,非空非满栈;
  • 第六步,push a,进栈,没问题,R1 = a,此时为满栈,栈顶元素为 a
  • 第七步,push d 进栈,发生 underflow,将栈底元素保存到主存发生一次内存引用 (ss(a + b * c))R0 保存 a + b * c 的存储地址,此时栈顶元素为 R2 = d,满栈;
  • 第八步,push c 进栈,发生 underflow,将栈底元素保存到主存,发生一次内存引用 (ss(a)),此时栈顶元素为 R3 = c,满栈;
  • 第九步,将栈顶的两个元素做乘法运算,将结果 d + c 保存到 R2,栈元素减一,同时从 a 的主存地址获取 (fetche) a(发生内存引用)也就是图中的 sf(a),将 a 保存到 R1 中,此时栈中有两个元素,栈顶是 R2 = b * c,满栈;
  • 第十步,将栈顶的两个元素做加法运算,将结果 a + d * c 保存到 R1,栈元素减一,同时从 a + b * c 的主存地址获取 (fetche) a + b * c 的值(发生内存引用)也就是图中的 sf(a + b * c),将 a + b * c 保存到 R0 中,此时栈中有两个元素,栈顶是 R1 = a + d * c,满栈;
  • 第十一步,push e 入栈操作,发生 underflow,将栈底元素保存到主存发生一次内存引用 (ss(a + b * c))R0 保存 a + b * c 的存储地址,此时栈顶元素为 R2 = e,满栈;
  • 第十二步,将栈顶的两个元素做减法运算,将计算结果 a + d * c - e 保存到 R1,栈元素减一,同时从 R0 保存的主存地址拿出 a + b * c 的值保存进 R0,此时为满栈,栈顶元素 R1 = a + d * c - e
  • 第十三步,将栈顶的两个元素做除法运算,将计算结果 (a + b * c) / (a + d * c - e)R0 保存,栈元素减一。

统计一下,利用这个求值栈计算过程中间接性的做了 4store 操作和 4fetche 操作,也就是说使用该结构栈在正常的流程中额外的增加了 8 次访存操作。

我们改变一下栈大小会发生什么,将栈的大小增加为 4,那么它的执行流程将会是下面这样。

在这里插入图片描述

哇呜,一气呵成,中间没有发生内存引用,Good。因为计算该表达式最多的情况下会一次性有 4 个元素在栈中,那么栈的大小只要满足,就不会发生栈溢出的情况,也就不会有内存引用这样额外的访存操作了。

3.3. 栈基架构小结

通过上面的几个例子,相信各位已经清楚了栈基架构的机器模型。看起来他们是可以成功的完成计算任务,但是有一个明显的问题不知道机智的你有没有发现。第一步的时候 push a,将 a 入栈,接着后面又有一次 push apush c 同样是,我们再做重复多余的工作欸!!栈基机器模型的架构很简单,指令很密集,但这对于机器性能不算是好事,因为使用该模型将意味着要面临多次的重新读取。

如果使用指令集架构,如 MIPS,那么将会有 32 个通用寄存器,可以在任何指令中为任何一个寄存器命名。那么这时候,当你已经 loada、b、c、d、e 数值到寄存器空间中一次了,那么之后所有关于这几个数的操作时都无需重新 load。事实上这个问题时由指令集解决的,并不是基础机器模型的问题,也不是微架构要解决的问题。

4. 对比几种简单的机器模型

上文已经介绍了这四种机器模型,Stack-BasedAccumulatorRegister-MemoryRegister-Register。现在用一个简单的计算 C = A + B,分别使用这四个机器模型,看看会有什么不同。

在这里插入图片描述

  • Stack-Based Architecture

栈基架构的机器模型的工作过程就是,Push A,将 A 的值入栈,Push B,将 B的值入栈,用栈顶两个元素做 Add 操作,然后将结果 PopC 中保存。

  • Accumulator Architecture

累加器的机器模型工作过程为,Load AAdd BA + B,然后将结果 StoreC 中。

  • Register-Memory Architecture

寄存器-存储器架构机器模型的工作过程,Load R1, AAdd R3, R1, B,将 B 的值与 R1 保存的值相加,将结果保存到 R3 中,再将 R3 的值 StoreC 中。

  • Register-Register Architecture

寄存器-寄存器架构机器模型的工作过程为,Load R1, ALoad R2, B,执行 Add,将 R1R2 保存的值相加,并将结果保存到 R3 中,再将 R3 的值 StoreC 中,完成计算。

相比前两个架构,后两个架构在使用 A、B 之前需要先 Load,计算完后还需要 Store,看起来似乎不够高效,没有前面两个模型简洁,但是当之后需要重复性的使用 A、B 的值进行计算的话,那么后两个架构,将不需要再次 Load,而可以直接使用,而前两个架构则需要重复性的执行 PushLoad 指令,这样看起来优势就会体现出来了。

觉得这篇文章对你有帮助的话,就留下一个赞吧*^v^*
请尊重作者,转载还请注明出处!感谢配合~
[作者]: Imagine Miracle
[版权]: 本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
[本文链接]: https://blog.csdn.net/qq_36393978/article/details/128717724

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

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

相关文章

springboot整合flowable

自动部署 流程图提前画好,放入 指定文件夹 配置: spring.datasource.usernameroot spring.datasource.passwordroot # nullCatalogMeansCurrenttrue 自动生成表 spring.datasource.urljdbc:mysql:///flowable_process?serverTimezoneUTC&nullCat…

CISP_VULHUB_HACK ME PLEASE

vulhub_HACK ME PLEASE简介扫描开放端口探测web服务搜集seedms的信息登录mysql,尝试获得seeddms的密码利用RCE漏洞提权简介 靶机链接:https://www.vulnhub.com/entry/hack-me-please-1,731/难度:简单描述:一个完全为OSCP设计的简…

数据结构之经典八大排序的实现(万字详谈)

文章目录前言1.插入排序2.希尔排序3.选择排序4.堆排序5.冒泡排序6.快速排序hoare方式版本快排实现非递归方式实现快排挖坑法实现快排前后指针法(双指针法)快排的各种优化1.减少后几层的递归调用(小区间优化)2.三数取中优化3.三路划分(处理大量重复数据&a…

信息论复习—信息的度量

目录 离散信源信息的度量: 离散信源的信息量: 单符号离散无记忆信源: 离散无记忆信源及熵: 自信息: 信息量的定义: 信息熵的定义: 熵的性质: 离散信源的最大熵定理&#xf…

Elasticsearch高级查询—— 关键字精确查询文档

目录一、初始化文档数据二、字段匹配查询文档2.1、概述2.2、示例一、初始化文档数据 在 Postman 中,向 ES 服务器发 POST 请求 :http://localhost:9200/user/_doc/1,请求体内容为: {"name":"张三","age&…

LINUX学习之了解系统目录结构(一)

前言 Linux 系统目录结构是一个由各种目录和文件组成的树形结构,每个目录都有特定的用途。在这篇文章中,我们将讨论 Linux 系统中最常见的目录,并解释它们的用途 登录系统后输入ls命令查看系统目录 树状目录结构图 系统常用目录 目录名描述…

单目相机标定实现--张正友标定法

文章目录一:相机坐标系,像素平面坐标系,世界坐标系,归一化坐标系介绍1:概述公式二:实现1:整体流程4:求出每张图像的单应性矩阵并用LMA优化5:求解理想无畸变情况下的摄像机的内参数和…

SpringBoot项目集成liquibase,数据库版本控制解决方案

liquibase 数据库版本留痕解决方案,在实际生产过程中如何高效管理数据库的DDL与DML语句,对这些语句留痕处理。如果能将sql的执行与SpringBoot项目启动结合在一起,每次启动项目自动执行新增的sql语句,这样就可以使得项目组成员各个…

在 VMware Workstation 16 Pro 中安装 Ubuntu Server 22.04.1 并配置静态 IP 地址

文章目录1.下载 Ubuntu Server 22.04.12.新建虚拟机向导3.编辑虚拟机设置4.开启此虚拟机并配置Ubuntu系统5.设置 root 用户的密码6.允许远程连接 root 用户7.配置静态 IP 地址7.1 查看 Windows 的网络信息7.2 查看 Ubuntu 的网络信息7.3 修改配置文件7.4 测试 Windows 能否互相…

springcloud--xxl-job

xxl-job 虽然java自带定时器,但是在springcloud内,如果对多个模块进行统一任务调度,这是自带的定时器就显得不够用,这时就可以使用xxl-job。 xxl-job是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习…

智能门锁-手机应用相机国产、非国产统计参数对比分析

智能门锁-手机应用相机国产、非国产统计参数对比分析 智能门锁应用 从2019年1月1日至2020年12月31日,3D人脸识别智能门锁在全市场统计中,总销量已接近20万套。其中德施曼以其先发优势,良好的市场定位和大力度的推广,成为市场发展…

C语言萌新如何使用printf函数?

🐎作者的话 如果你搜索输入输出函数,那么你会看到输入输出流、Turbo标准库、标准输出端、stdout什么什么乱七八糟的,作为一个萌新,哪懂这些? 本文介绍萌新在前期的学习中,常用的输入输出函数及其功能~ 跳跃…

【Python标准库】LZ77编码的基本原理和lzma模块

文章目录lz77编码lzma模块调用lz77编码 Python标准库总共提供了三种压缩算法,分别是zlib, bz2以及lzma,并且位这三个模块提供了高度相似的API,考虑到zlib中已经对很多定义做了详尽的解读,本文主要介绍一下lzma算法,然…

vue使用rem, vscode使用px to rem工具

一、使用px2rem-loader实现pxtorem 1、安装 首先,我们使用 vue 的脚手架 vue-cli 初始化一个 webpack 项目(前提是已经安装过 vue-cli,具体不再阐述),一些选项根据自己项目需要选择。 vue init webpack my-app命令执…

深度学习算法数据-网络-算法总结

深度学习算法数据-网络-算法总结 1 数据集大全 通用2D检测数据集、交通标志、车道线、行人检测、3D目标检测、ReID等数据集 2 Backbone知识汇总 该部分主要是针对常见CNN结构以及ViT结构进行汇总,同时也包含轻量化CNN Backbone以及轻量化Transformer模型等高性…

详解pandas的read_excel函数

一、官网参数 pandas官网参数网址:pandas.read_excel — pandas 1.5.2 documentation 如下所示: 二、常用参数详解 1、io 一般指读取文件的路径。【必须指定】 import pandas as pddf pd.read_excel(r"C:\Users\wwb\Desktop\data3.xlsx")p…

chromecast激活

小白误入旁路由添加dns解析(1)外部网络设置不动,内部网络设置第一个dns服务器指向旁路由自己,第二个dns服务器用常用的保证能用就行(2)添加dns解析,把安卓ntp,更新时间的服务器链接成…

一文读懂CPU工作原理、程序是如何在单片机内执行的、指令格式之操作码地址码

文章较长,大家可选择性阅读,嘎嘎细 计算机结构 CPU的运行原理 CPU的控制单元在时序脉冲的作用下,将指令计数器里所指向的指令地址(这个地址是在内存里的)送到地址总线上去,然后CPU将这个地址里的指令读到指令寄存器进行译码。由运算器执行对应的机器指令,并将结果通过地…

如何用C++扩展NodeJS的能力?

文章目录前言C结合NodeJS的魅力C和NodeJS怎么结合通过Addon增强NodeJS环境的准备1. node-gyp2. nan (Native abstraction for NodeJS)编写Addon的C代码JS方法的C表示JS方法的传入参数 v8::Argument进阶进阶1: 输出一个JS包装类型进阶2: 使用多线程异步计算最后前言 Javascript…

Qt使用第三方库QXlsx将数据库的数据导出为Excel表格

一、参考和下载第三方库QXlsx 参考1 这篇博客对第三方库QXlsx介绍的比较详细。 1、概述 QXlsx是一个可以读写Excel文件的库。不依赖office以及wps组件,可以在Qt5支持的任何平台上使用。 2、使用方式 (1) QXlsx可以编译为静态库库使用(可以提升项目编…