我有一个关于组装的基本问题。
如果寄存器也可以在内存上工作,为什么我们还要费心只在寄存器上进行算术运算呢?
例如,以下两个原因(本质上)将相同的值计算为答案:
代码段1
.data
var dd 00000400h
.code
Start:
add var,0000000Bh
mov eax,var
;breakpoint: var = 00000B04
End Start
代码段2
.code
Start:
mov eax,00000400h
add eax,0000000bh
;breakpoint: eax = 0000040B
End Start
据我所知,大多数文本和教程主要是在寄存器上进行算术运算。使用寄存器只是更快吗?
编辑:太快了:)
给出了几个很好的答案;根据第一个好答案选择了最好的答案。
发布于 2017-08-10 11:11:07
就像你学习汇编的其他“普通”x86一样,它是一个寄存器machine1。还有其他方法可以设计一些你可以编程的东西(例如,图灵机沿着内存中的逻辑“磁带”移动,或者生命的游戏),但寄存器机器已经被证明基本上是获得高性能的唯一途径。
https://www.realworldtech.com/architecture-basics/2/
涵盖可能的替代方案,如累加器或堆叠机,它们现在也已过时。尽管它省略了像x86这样的CISCs,它既可以是加载存储,也可以是寄存器内存。x86指令can actually bereg,mem;reg,reg;甚至mem,reg。(或直接提供消息来源。)
脚注1:抽象的计算模型称为register machine不区分寄存器和内存;它所称的寄存器更像是真实计算机中的内存。我在这里说“注册机器”是指具有
多通用寄存器,而不是只有一个累加器,或者堆栈机或其他任何东西。大多数x86指令都有2个显式操作数(but it varies),其中最多一个可以是内存。即使像6502这样的微控制器只能在一个累加器寄存器中进行数学运算,也几乎总是会有一些其他的寄存器(例如指针或索引),而不像Marie或LMC这样的真正的玩具ISA,它们的编程效率非常低,因为你需要不断地将不同的东西存储并重新加载到累加器中,甚至不能将数组索引或循环计数器保存在任何可以直接使用的地方。
由于x86是为使用寄存器而设计的,因此您不能完全避免它们,即使您想这样做并且不关心性能。
当前的x86 CPU在每个时钟周期内可以读/写的寄存器比内存位置多得多。
例如,英特尔Skylake可以在每个周期内对其32KiB 8路关联L1D高速缓存执行两次加载和一次存储(最好的情况),但可以
每个时钟读取10个以上的寄存器,并写入3或4个寄存器(加上EFLAGS。
构建一个L1D缓存,其读/写端口数与the register file将会非常昂贵(在晶体管计数/面积和功率使用方面),特别是如果你想保持它的大小。它可能只是在物理上不可能构建一个可以像x86使用寄存器那样使用内存的东西,并且具有相同的性能。
此外,写入寄存器然后再次读取它基本上具有零延迟,因为CPU检测到这一点,并绕过写回阶段,将结果直接从一个执行单元的输出转发到另一个执行单元的输入。(请参阅
https://en.wikipedia.org/wiki/Classic\_RISC\_pipeline#Solution\_A.\_Bypassing)。
这些执行单元之间的结果转发连接被称为“旁路网络”或“转发网络”,对于寄存器设计来说,对于CPU来说,这样做要比所有东西都必须进入内存再返回要容易得多。CPU只需检查3到5位寄存器编号,而不是32位或64位地址,即可检测需要立即将一条指令的输出作为另一操作的输入的情况。(这些寄存器编号被硬编码到机器代码中,因此它们立即可用。)
正如其他人所提到的,3位或4位对寄存器进行寻址使机器代码格式比每条指令都有绝对地址时要紧凑得多。
另请参阅
https://en.wikipedia.org/wiki/Memory\_hierarchy:您可以将寄存器看作是一个小的快速寄存器固定大小
独立于主内存的内存空间,其中只支持直接绝对寻址。(你不能“索引”一个寄存器:给定一个整数N
在一个寄存器中,您无法获取
N
th寄存器中有一个insn。)寄存器也是单个CPU核心的私有寄存器,因此乱序执行可以对它们做任何它想做的事情。对于内存,它必须担心其他CPU核心看到的事情的顺序。拥有固定数量的寄存器是让CPUs完成任务的一部分。
register-renaming用于无序执行。在对指令进行解码时立即获得寄存器编号也可以使这一点变得更容易:永远不会对未知的寄存器进行读或写操作。请参见
为什么mulss在Haswell上只需要3个周期,与Agner的指令表不同?(使用多个accumulators)展开FP循环
关于寄存器重命名的解释,以及一个具体的例子(对问题/我答案后面部分的编辑,显示了使用多个累加器展开以隐藏FMA延迟的加速,即使它重复使用相同的架构寄存器)。
带有存储转发的存储缓冲区基本上给了你“内存重命名”。对存储器位置的存储/重新加载独立于先前的存储并从该核心内加载到该位置。(
(Can a speculatively executed CPU branch contain opcodes that access RAM?)使用堆栈参数调用约定的重复函数调用,和/或通过引用返回值,是堆栈内存的相同字节可以多次重用的情况。
即使第一个存储仍在等待其输入,秒存储/重新加载也可以执行。(我已经在Skylake上测试过了,但IDK如果我在任何地方发布结果的话。)
https://stackoverflow.com/questions/2360997
复制相似问题