当前位置: 首页 > news >正文

五、指令集架构深入分析

五、指令集架构深入分析

雇主不是需要汇编语言程序员,而是需要有理解计算机体系结构的人来写出各高效的程序

指令的格式

指令集设计的考虑因素?

ISA设计考虑因素:

  • 指令占用内存空间大小;
  • 指令系统复杂程度,主要指指令执行所需要的译码数量和指令所执行任务的复杂性;
  • 指令长度大小以及是否固定
  • 指令系统中指令的总数目。
  • 不同类型的寻址方法
  • 字节存储的小端大端位序问题
  • ISA需要多少寄存器并如何组织这些寄存器

短指令的好处和缺点?

指令一般越短越好,较短的指令占用较少的内存空间,并且提取指令速度更快。但采取短指令会限制指令的数量(受到能够编码的二进制数的位数限制),同样也会限制操作数的大小和数量。

为什么要按字节编址?

存储器的组成会影响指令的格式。如果存储器为16或32位字,不按字节编址则很难访问到一个单一字符,因此有些16/32/64位机器也是按照字节编址。

什么是大端和小端?

计算机存储多字节的方法,所有现代计算机都是按字节编址,大多数UNIX采用大端方式,个人计算机采用小端方式,大多数RISC架构采用大端方式。

注意:大端小端的方式只是字节顺序的颠倒,不是数字位的颠倒,在字节内部还是正常的顺序

1571626002919-d0c66ed6-e0e5-4dd3-9738-2fa297d28e52.png

上面的例子一个字节一个地址,32位需要四个字节占用4个地址,大端的高位在低地址,小端的低位在低地址,注意最后的0x10虽然只有两个十六进制,也就是2*4位一个字节,但是还是要占用4个字节,不够高位补0,因为这个整数是32位的。

大端和小端各自的优缺点?

  • 大端优点:符合大多数人的习惯,易于读取数据,大端机器会用同样的方式存放字符串,如果使用小端要找到字符串开头需要知道字符串长度再移动到字符串的高位才可以。大端则直接读取即可。处理图像的时候,大端方式直接处理,小端处理大图像的时候必须不断翻转字节顺序
  • 大端缺点:不允许字被存放到不是按字编写的边界上,当一个字是2或者4字节的时候,不会允许地址是奇数字节开始,这样显然会浪费存储空间,小端计算机实现高精度计算的时候更加快速容易
  • 计算机网络使用大端的方式,当小端通过网络传输数据的时候,将整数转换为网络的字节顺序,接收的时候也要进行转换

有哪些CPU架构类型?

根据CPU如何存储数据:

  • 堆栈型架构:使用堆栈执行指令,操作数存在栈顶,不能随机访问堆栈,无法有高效代码,如果内存访问快是一个好的选择
  • 累加器型架构:操作数存在累加器里面,最大限度地降低机器内部复杂性,暂存数据,与内存的交互太频繁,通信量太高
  • 通用寄存器型架构:广泛使用,长指令在通用寄存器会有较长的取指译指时间,所以设计ISA一定要缩短指令

根据操作数的存放位置:

  • 存储器-存储器型:允许任何操作数都不放在寄存器
  • 寄存器-存储器型:至少一个在寄存器,其余在存储器
  • 装入-存储(load-store):需要在任何对数据的操作前把数据装入寄存器中。

指令的定长与变长

  • 定长指令:浪费空间,速度快。使用指令集流水线的时候性能高
  • 变长指令:节省空间,译码更加复杂
  • 指令长度必须和机器字长想对应,即使不够字长也必须对齐
  • 基于寻址的原因,指令往往需要按照存储字来对其,变长指令字长不同也需要进行字对齐,所以变长指令也会浪费空间,一般方案使用两种或三种指令长度通过组合模式来对齐

操作数的个数和指令长度

  • 零地址:只有操作码,eg:halt
    • 没有操作数
    • 必须使用堆栈
  • 单地址:操作码和一个地址:使用一个内存地址
  • 双地址:操作码和双地址,两个寄存器或者一个寄存器地址+一个内存地址
  • 三地址:三个寄存器地址或者寄存器地址加内存地址

逆波兰式的书写(心中有堆栈)

  • 操作符在操作数之后,又叫后缀表达式。与此对应还有前缀表达式中缀表达式。
  • 具体做法就是:操作符后移,优先级越高的离操作数越近,没有括号,最后写出来的式子只要一个一个放入栈中执行最后结果对即可
  • 使用堆栈对逆波兰表达式求值:从左到右扫描表达式,将每一个操作数压栈,遇到双目操作符的时候弹出栈顶的两个操作数,并执行由该操作符指定的运算,然后再将结果压入栈顶
  • 1571661348047-7e8a2cb4-4926-4598-8a8a-4121b52b3884.png

扩展操作码expanding opcode

代表一种折衷方案,要求有尽可能多的操作码,又要求采用尽可能短的操作码。

设计思想是:选用短操作码,而有需要时可以有某种方法将操作码加长。如果使用短操作码就留给操作数大量的指令位,因此每条指令可以有2~3个操作数;若指令不需要操作数(如halt),或计算机使用堆栈操作,则指令所有位都可用于操作码,生成独特指令。

假设计算机具有16位指令系统和16个寄存器,指令用4个二进制位就可以指定其中的某个寄存器。如果使用4位操作码,剩下12位作为存储器地址,寻址范围可达2^12 = 4KB。如果所有数据都被预先装入寄存器,则可以将12位地址分成3个寄存器地址(一个寄存器的地址只需要4位)。

举例说明:用16位编码产生

  • 15条3地址指令,
  • 14条2地址指令,
  • 31条1地址指令,
  • 16条0地址指令。
15个3地址编码 0000 R1 R2 R3; ··· ; 1110 R1 R2 R3
14个2地址编码 1111 0000 R1 R2; ··· ; 1111 1101 R1 R2
31个1地址编码 1111 1110 0000 R1; ··· ; 1111 1111 1110 R1
16个0地址编码 1111 1111 1111 0000; ··· ; 1111 1111 1111 1111

指令类型

  • 数据传送:使用最频繁的指令类型
  • 算数运算:整数和浮点数,
  • 布尔逻辑运算:
  • 位操作:逻辑移位和循环移位等
  • 输入输出指令:
  • 传送控制指令:分支转移、跳过、进程调用等
  • 专用指令:如字符串处理指令,高级语言支持指令,保护和标志位控制指令,高速缓存指令等。
  • 正交指令集

寻址

数据类型

指令可以寻址的数据类型:数值数据,字符型数据、布尔类型、指针。后三个是自然数但是会和数值数据区别对待

寻址方式

  • 立即寻址:操作码后紧跟着操作数就是要处理的数据,指令中操作码后的数值会被立刻引用,例如Load 008会直接将数值8装入累加器AC中。编译之前写入固定值不灵活
  • 直接寻址:操作码后是要操作数据在存储器里面的地址,如Load 008将存储器地址为008的存储单元中的数值作为操作数装入累加器AC
  • 寄存器寻址:和直接寻址的区别就在数据在寄存器而不再主存储器
  • 间接寻址重要,操作码后面的是存储器里面的地址a,地址a存放的是要操作数据的真实地址,也叫做有效地址,这种寻址方式允许我们决定实际操作数的位置,只要把位置放到地址a指向的取余即可
  • 寄存器间接寻址和间接寻址的区别就在地址a在寄存器而不再主存储器
  • 变址寻址或基址寻址:变址寄存器用于存储一个偏移量,将这个偏移量与操作数相加,产生实际的有效地址。如Load X中的操作数X采用变址寻址方式,假定R1为变址寄存器,如果R1中存放着1,则Load X寻找到的有效地址是X+1。基址寻址与变址寻址类似,但基址寻址使用基地址寄存器而不是变址寄存器,操作数域中的内容表示的是偏移量。这两种寻址方式在访问数组元素和字符串中字符时非常有效,大部分汇编语言都提供专门的变址寄存器。
  • 堆栈寻址:操作数存放在堆栈里面

指令流水线

取指译指执行分解为多个小步骤,一个时钟周期内执行不同指令的小步骤,较小步骤并行执行,时间上的交叠可以加快CPU执行速度。这种方法称为流水线(pipeline)

假设将取指-译码-执行周期分解为如下6个小步骤:S1取指令;S2操作码译码;S3计算操作数有效地址;S4取操作数;S5执行指令;S6存储结果,这是一个6级流水线,其中的每一个步骤称为流水线级(pipeline stage),将流水线逐级连接就构成指令执行的管道。假设现在有n条指令,k级流水线(本例的k=6)时钟周期时间为tp,即每级流水线需要tp时间。如果不采用流水线执行指令,则需要T = n × k × tp的总时间。如果采用流水线系统,一旦第一条指令的S1完成,该指令就会被送去执行S2,同时可以开始第二条指令的S1,当第二条指令执行S2时,第三条指令S1开始,第一条指令则执行S3,以此类推。因此执行第一条指令需要k × tp时间完成,之后的n - 1个任务每隔一个时钟周期就会从流水线中流出一个流水线级,因此剩下的总时间为(n - 1)×tp,利用k级流水线的总时间为T’=(k × tp)+(n - 1)×tp = (k + n - 1 )× tp。获得的加速比S = T/T’,当n较大时,k + n - 1与n近似,因此n较大时S = k。

1571666550771-bcfd692e-0a63-463a-a050-d48e06b710a9.png

流水线级数越多,计算机运行速度越快,从某种意义上说这一点是对的,但流水线控制逻辑的数量和大小也会随之增加,拖慢系统速度。另外还存在一些条件会导致“流水线冲突”,这些情况会阻碍计算机实现每个时钟周期执行一条指令的目标。条件包括:资源冲突(这时存的指令继续,取的指令等待)、数据关联(后面的操作需要前面的结果,硬件通过在流水线中插入一个no-op指令(不执行操作),让计算机有足够时间解决冲突。)、条件分支语句(有些条件不成立,但是流水线执行的时候条件还没有判断,就造成执行但是没有指令输出,只是执行了一般就发现不用执行。解决方法是设计分支预测机构,利用合理的逻辑对下一条指令做最优预测,编译器试图通过重新安排机器代码的方式产生延迟的分支转移操作。另一种方法是对某个已知的条件分支转移的两条分支通道都开始执行取指操作,并将结果存储,等到分支语句的实际执行时,需要执行的真实分支路径已经被计算过)。

指令集架构实例

Intel

Intel使用的是小端、双地址的体系结构,采用可变长度指令系统,寄存器-存储器结构,所以指令都能在寄存器上操作,但是其他操作数必须在寄存器。

MIPS

小端、按字编址、3地址、采用固定长度,是装入/存储式体系结构。MIPS限制只有固定长度的操作,操作数句必须具有相同的字节数。

JVM

与工作平台无关,实际运行在JVM(Java Virtual Machine)上,不同硬件机器的JVM不同,JVM按照机器固有的指令集编写。

JVM读取java字节码,将字节码解释成各种机器指令。对Java程序编译时,生成特殊字节码,这些字节码是JVM的输入源程序。这个Java的class文件使用的堆栈语言,也就是JVM的ISA使用的是堆栈架构,

不允许使用无符号整数,使用2的补码来表示有符号整数。字符使用16位的Unicode。Java虚拟机有4个寄存器,支持访问5个不同的主存储区区域,所有对主存的访问都是基于寄存器里面的偏移量,不使用指针和绝对寄存器地址。JVM是堆栈类型的机器,不提供通用寄存器。

编译型语言如C、C++、Ada、FORTRAN、COBOL等通常具有很好的可执行性能,只能在特定的目标体系结构上运行。解释型语言如LISP、PhP和Tcl等与工作平台无关,但执行速度约比编译程序慢100倍。同时以两种形式存在的称为P代码语言,如Perl、Python和Java等,效率比编译程序语言慢5~10倍。

1571668850269-7aa45332-580c-452c-88ed-c5a77c7e9253.png

ARM

装载存储架构,所有的数据处理都需要寄存器的值来处理,是类RISC处理器内核的一个家族,一种广泛应用的32位指令集架构

http://www.hskmm.com/?act=detail&tid=29514

相关文章:

  • ARC 208 Div.2
  • 八、系统软件
  • 七、输入输出和存储系统
  • 那快把题端上来吧(五)
  • 机器学习学术研讨会柏林举办
  • 构建易受攻击的AWS DevOps环境:CloudGoat攻防实战
  • 虚拟机和windows
  • MySQL 5.7版本,搭建一个两主一从的多源主从复制环境
  • 测试哦 - sto-OI
  • SimAM注意力机制
  • python容器-字符串
  • 氛围编程陷阱:为什么AI生成代码正在制造大量伪开发者
  • 记一次的AI Agent开发的思维误区
  • 3_Linux 文件管理三部曲:操作、结构与权限控制
  • 2_终端入门:从 “不敢点” 到 “离不开” 的 5 个核心命令(附高频命令补充)
  • 1_从 0 到 1 入门 Linux:认知、选型、安装
  • 02020509 EF Core高级09-生成静态表达式树、动态创建表达式树、简化表达式树
  • 决胜职场:以准备为刃,破局而出
  • python的流程控制
  • 卖萌求 AccessKey
  • 详细介绍:【Linux】线程控制
  • 初识pytorch:关于数据展示的组件tensorboard
  • win11家庭版升级到专业版教程,专业版改为家庭版教程
  • 计网思维导图总结
  • 如何下载不同版本的像素地牢
  • 一生一芯学习:多道程序 yield-os.c
  • 速通ACM省铜第十六天 赋源码(Sigma Cubes和Find Permutation 2和Rotate and Sum Query) - 教程
  • Linux操作系统扫盲汇总
  • ABC round 427
  • 卸载驱动模块,内核崩溃排查调试记录