16

我有一个关于装配的基本问题。

如果寄存器也可以在内存上工作,为什么我们还要费心只在寄存器上做算术运算呢?

例如,以下两个原因(本质上)将相同的值计算为答案:

片段 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



据我所知,大多数文本和教程主要在寄存器上进行算术运算。使用寄存器是否更快?

编辑:那很快:)

给出了一些很好的答案;最佳答案是根据第一个好的答案选择的。

4

9 回答 9

29

如果您查看计算机体系结构,您会发现一系列内存级别。那些靠近 CPU 的是快速、昂贵(一点点),因此体积小,而在另一端你有大、慢和便宜的内存设备。在现代计算机中,这些通常类似于:

 CPU registers (slightly complicated, but in the order of 1KB per a core - there
                are different types of registers. You might have 16 64 bit
                general purpose registers plus a bunch of registers for special
                purposes)
 L1 cache (64KB per core)
 L2 cache (256KB per core)
 L3 cache (8MB)
 Main memory (8GB)
 HDD (1TB)
 The internet (big)

随着时间的推移,添加了越来越多的缓存级别——我记得有一次 CPU 没有任何板载缓存,而且我还没有老!如今,HDD 带有板载缓存,并且 Internet 缓存在任意数量的位置:内存中、HDD 上,可能还缓存代理服务器上。

在远离 CPU 的每一步中,带宽都会急剧下降(通常是数量级) ,延迟会增加。例如,一个 HDD 可能能够以 100MB/s 的速度读取,延迟为 5ms(这些数字可能不完全正确),而您的主内存可以以 6.4GB/s 的速度读取,延迟为 9ns(六个数量级震级!)。延迟是一个非常重要的因素,因为您不想让 CPU 等待的时间超过它必须等待的时间(对于具有深管道的架构尤其如此,但这是另一天的讨论)。

这个想法是您经常会一遍又一遍地重用相同的数据,因此将其放在一个小的快速缓存中以进行后续操作是有意义的。这被称为时间局部性。局部性的另一个重要原则是空间局部性,这表示彼此靠近的内存位置可能会在大约同一时间被读取。正是由于这个原因,从 RAM 读取将导致更大的 RAM 块被读取并放入 CPU 缓存中。如果不是这些局部性原则,那么内存中的任何位置在任何时候都有同样可能被读取,因此无法预测接下来会访问什么,以及所有级别的缓存在世界上不会提高速度。您不妨只使用硬盘驱动器,但我相信您知道在分页时让计算机停止运行是什么感觉(这基本上是使用硬盘驱动器作为 RAM 的扩展)。从概念上讲,除了硬盘驱动器之外没有内存是可能的(许多小型设备只有一个内存),

拥有寄存器(并且只有少量寄存器)的另一个优点是它可以让您拥有更短的指令。如果您有包含两个(或更多)64 位地址的指令,那么您将有一些很长的指令!

于 2010-03-02T06:15:55.557 回答
10

寄存器要快得多,而且您可以直接在内存上执行的操作也受到更多限制。

于 2010-03-02T05:01:57.310 回答
4

x86,就像你可能学习汇编的几乎所有其他“普通”CPU一样,是一个寄存器机器1。还有其他方法可以设计一些你可以编程的东西(例如,沿着内存中的逻辑“磁带”移动的图灵机,或生命游戏),但注册机已被证明基本上是实现高效率的唯一方法。表现。

https://www.realworldtech.com/architecture-basics/2/涵盖了可能的替代方案,例如现在也已过时的累加器或堆栈机器。尽管它省略了像 x86 这样的 CISC,它可以是加载存储或寄存器内存。x86 指令实际上可以是reg,mem;注册,注册;甚至内存,注册。(或有直接来源。)

脚注 1:称为寄存器机的抽象计算模型不区分寄存器和内存;它所说的寄存器更像是真实计算机中的内存。我在这里说“寄存器机器”是指具有多个通用寄存器的机器,而不是只有一个累加器,或堆栈机器或其他任何东西。大多数 x86 指令有 2 个显式操作数(但它会有所不同),最多其中之一可以是内存。即使是像 6502 这样只能在一个累加器寄存器中真正进行数学运算的微控制器,几乎总是有一些其他寄存器(例如用于指针或索引),这与像 Marie 或 LMC 这样的真正玩具 ISA 不同,它们的编程效率极低,因为您需要继续存储和将不同的东西重新加载到累加器中,甚至不能在任何可以直接使用的地方保留数组索引或循环计数器。


由于 x86 被设计为使用寄存器,因此您无法真正完全避免它们,即使您想要并且不关心性能。

当前的 x86 CPU 在每个时钟周期可以读取/写入比内存位置更多的寄存器。

例如,英特尔 Skylake 每个周期可以从/向其 32KiB 8 路关联 L1D 缓存执行两次加载和一次存储(最佳情况),但每个时钟可以读取多达 10 个寄存器,并写入 3 或 4 个(加上 EFLAGS)

构建具有与寄存器文件一样多的读/写端口的 L1D 缓存将非常昂贵(在晶体管数量/面积和功耗方面),特别是如果您想保持它尽可能大。构建一个可以像 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用一个 insn 得到第 th 寄存器的内容。)

寄存器对于单个 CPU 内核也是私有的,因此乱序执行可以随心所欲。有了内存,它必须担心其他 CPU 内核可以看到的顺序。

拥有固定数量的寄存器是让 CPU为无序执行进行寄存器重命名的一部分。当指令被解码时,寄存器号立即可用也使这更容易:永远不会读取或写入未知的寄存器。

请参阅为什么 mulss 在 Haswell 上只需要 3 个周期,与 Agner 的指令表不同?(使用多个累加器展开 FP 循环)解释寄存器重命名和一个具体示例(稍后对问题的编辑/我的答案的后面部分显示了使用多个累加器展开以隐藏 FMA 延迟的加速,即使它重用相同建筑登记反复)。


带有存储转发的存储缓冲区基本上可以为您提供“内存重命名”。存储/重新加载到内存位置独立于之前的存储,并从该内核中加载到该位置。(推测性执行的 CPU 分支是否可以包含访问 RAM 的操作码?

使用堆栈参数调用约定的重​​复函数调用和/或通过引用返回值是相同字节的堆栈内存可以重复使用多次的情况。

即使第一个存储仍在等待其输入,秒存储/重新加载也可以执行。(我已经在 Skylake 上对此进行了测试,但如果我曾经在任何地方的答案中发布了结果,IDK。)

于 2017-08-10T03:11:06.967 回答
3

寄存器的访问速度比 RAM 内存快,因为您不必访问“慢”内存总线!

于 2010-03-02T05:01:44.963 回答
1

我们使用寄存器是因为它们速度很快。通常,它们以 CPU 的速度运行。
寄存器和 CPU 缓存采用不同的技术/结构制成,
而且价格昂贵。另一方面,RAM 很便宜,而且速度要慢 100 倍。

于 2010-03-02T05:30:42.817 回答
1

一般来说,寄存器算术要快得多,而且更受欢迎。但是,在某些情况下,直接内存算法很有用。如果您只想在内存中增加一个数字(至少对于几百万条指令没有别的),那么单个直接内存算术指令通常比加载/添加/存储稍快。

此外,如果您正在执行复杂的数组操作,您通常需要大量寄存器来跟踪您的位置和数组的结束位置。在较旧的架构上,您可能会很快用完寄存器,因此在不破坏任何当前寄存器的情况下将两位内存添加在一起的选项非常有用。

于 2010-03-02T07:58:40.603 回答
0

是的,使用寄存器要快得多。即使您只考虑从处理器到寄存器的物理距离,而不是从 proc 到内存的物理距离,您也可以通过到目前为止不发送电子来节省大量时间,这意味着您可以以更高的时钟频率运行。

于 2010-03-02T05:01:46.850 回答
0

是的 - 您通常也可以轻松地推送/弹出寄存器以调用程序、处理中断等

于 2010-03-02T05:01:56.367 回答
-2

只是指令集不允许你做这么复杂的操作:

add [0x40001234],[0x40002234]

你必须通过寄存器。

于 2010-03-02T05:03:13.330 回答