【问题标题】:Why are there only four registers?为什么只有四个寄存器?
【发布时间】:2011-02-22 03:08:05
【问题描述】:

为什么最常见的 CPU (x86) 中只有四个寄存器?如果添加更多寄存器,速度不会有很大的提高吗?什么时候会增加更多的寄存器?

【问题讨论】:

标签: x86 cpu-architecture


【解决方案1】:

现在有 4 个以上 。如果您查看history of the x86 architecture,您会发现它是从 8086 指令集演变而来的。英特尔一直希望在其处理器系列中保持一定程度的向后兼容性,因此所有后续处理器都只是将原始 A、B、C、D 寄存器扩展到更多位数。原来的段寄存器现在可以用于一般目的,因为不再有真正的段(这是一个过度简化,但大致正确)。新的 x64 架构还提供了一些额外的寄存器。

【讨论】:

  • 另外,数学协处理器有自己的一组寄存器。
  • 由于寄存器重命名,超过x个。
  • 我认为您的意思是指针寄存器,而不是段。 (ESI / EDI / EBP,不是CS/DS/SS/GS/FS。)你可以对段寄存器做的就是移入或移出它们,而不是将它们用作@等指令的操作数987654328@.
  • 琐事:实际上是 A/C/D/B 而不是 A/B/C/D
【解决方案2】:
  1. 过去实施寄存器的成本很高。
  2. 不一定。现代 x86 CPU 上的寄存器数量远远超出了 CPU 所显示的数量 - CPU 维护影子寄存器,这些寄存器根据指令流根据需要进行重命名。
  3. 在 AMD64/x86_64 中。在 64 位模式下运行时,通用寄存器的数量增加了一倍(除了它们的大小增加了一倍)。

有许多具有更多寄存器的架构(ARM、PowerPC 等)。有时,它们可以实现更高的指令吞吐量,因为在操作堆栈时完成的工作更少,并且指令可能更短(无需引用堆栈变量)。与之相对的是,由于更多的寄存器节省,函数调用变得更加昂贵。

【讨论】:

    【解决方案3】:

    嗯,还有更多,这四个只是特殊的,我认为它们是“通用”的,所有这些的原因以及为什么其余部分没有那么多使用是:

    • x86 并不是成为事实上的标准的最佳指令集,Intell 只是看到了向后兼容的潜力,一旦 AMD 加入它只是时间问题。
    • 现在这是事实上的标准,所以我们必须接受它。
    • 添加更多寄存器将不再是 x86,因此您的意思是“基于 x86 创建具有更多寄存器的新指令集”。
    • 大多数编译器不会使用这些,因为它们也可以编译到 x86 以同时针对 x86 的超集。
    • 更多的寄存器意味着更昂贵的硬件。

    【讨论】:

    • 我认为更准确的说法是“x86 已经足够流行,以至于改用其他任何东西都证明是无利可图的”。请记住,英特尔曾尝试推出具有 128 个 int 寄存器并且是真正的 64 位的 Itanium,但由于缺乏向后兼容性而失败。
    【解决方案4】:

    寄存器使用的内存真的在 CPU 中设计起来非常昂贵。除了这样做的设计困难之外,增加可用寄存器的数量会使 CPU 芯片更加昂贵。

    另外:

    • 还有其他更具成本效益的方法可以提高 CPU 性能
    • 即使引入了更多内容,您仍然需要更新指令集并修改编译器以供使用。
    • 已经有超过 4 个寄存器:来自 wikipedia(世界,嗯,最可靠的来源)
      • AX/EAX/RAX:累加器
      • BX/EBX/RBX:基本索引(例如:数组)
      • CX/ECX/RCX:计数器
      • DX/EDX/RDX:数据/常规
      • SI/ESI/RSI:字符串操作的“源索引”。
      • DI/EDI/RDI:字符串操作的“目标索引”。
      • SP/ESP/RSP:堆栈顶部地址的堆栈指针。
      • BP/EBP/RBP:堆栈基指针,用于保存当前堆栈帧的地址。
      • IP/EIP/RIP:指令指针。保存程序计数器,即当前指令地址。

    【讨论】:

    • “真的很贵”在 1980 年是真的。现代 CPU 拥有大量(L1 大小)的内核时钟内存,虽然价格昂贵,但并不能解释感知寄存器短缺的原因。
    • 相对来说贵/真的贵。寄存器存储器的使用方式与 L1 高速缓存不同。除非自从我上次签到后,当然寄存器已经开始缓存未命中......
    • 对于当前的 x86 硬件(除了 insn 编码困难),更多的架构寄存器不会成为问题。当前的设计已经有超过 100 个物理寄存器,架构寄存器被重命名到。 “真正昂贵”的事情是拱门建筑登记如此之少的历史原因。这确实意味着在上下文切换时保存/恢复的状态更少。
    【解决方案5】:

    嗯..... (E/R)AX, (E/R)BX, (E/R)CX, (E/R)DX, (E/R)SI, (E/R)DI , (E/R)SP, (E/R)BP, (E/R)IP。我认为这超过了 4 个。:)

    【讨论】:

      【解决方案6】:

      更多的寄存器并不一定会让事情变得更快,它们会使 CPU 架构更加复杂,因为寄存器必须靠近其他组件,而且许多指令只能在特定的寄存器上工作。

      但是现代 CPU 有四个以上的寄存器,从我的头顶上看,有 AX、BX、CX、DX、SI、DI、BP……然后 CPU 有内部寄存器,例如 PIC(处理器指令计数器)

      【讨论】:

        【解决方案7】:

        x86 总是有四个以上的寄存器。原来有CS、DS、ES、SS、AX、BX、CX、DX、SI、DI、BP、SP、IP和Flags。其中,7 个(AX、BX、CX、DX、SI、DI 和 BP)支持大多数通用运算(加法、减法等)。BP 和 BX 还支持用作“基”寄存器(即保存间接)。 SI 和 DI 也可以用作变址寄存器,它们与基址寄存器大致相同,只是一条指令可以从一个基址寄存器和一个变址寄存器生成地址,但不能从两个变址寄存器或两个基址寄存器生成地址。至少在典型使用中,SP 致力于充当堆栈指针。

        从那时起,寄存器变得更大,增加了更多,其中一些变得更加通用,因此(例如)您现在可以在 2 寄存器寻址模式下使用任意 2 个通用寄存器。有点奇怪的是,在 386 中添加了两个段寄存器(FS 和 GS),这也允许 32 位段,这主要使所有段寄存器几乎无关紧要。它们有时用于线程本地存储。

        我还应该补充一点,当您执行多任务、多线程等操作时,大量寄存器可能会产生相当严重的损失——因为您不知道哪些寄存器正在使用,当您执行上下文时切换您必须将所有寄存器保存在一个任务中,并为下一个任务加载所有保存的寄存器。在像 Itanium 或 SPARC 这样具有 200 多个寄存器的 CPU 中,这可能相当慢。最近的 SPARC 投入了相当多的芯片面积来优化这一点,但它们的任务切换仍然相对较慢。 Itanium 上的情况更糟——这是它在典型的服务器任务上不那么令人印象深刻的原因之一,尽管它在(非常)很少的任务切换的情况下在科学计算上大放异彩。

        最后,当然,所有这一切都与 x86 的合理现代实现的工作方式完全不同。从 Pentium Pro 开始,英特尔将架构寄存器(即可以在指令中寻址的寄存器)与实现分离。为了支持并发、乱序执行,Pentium Pro 有(如果有记忆的话)一组 40 个内部寄存器,并使用“寄存器重命名”,因此其中两个(或更多)可能对应于给定时间的给定架构寄存器.例如,如果你操作一个寄存器,然后存储它,加载一个不同的值,然后操作它,处理器可以检测到加载破坏了这两组指令之间的依赖链,因此它可以同时执行这两个操作。

        当然,Pentium Pro 现在已经很老了——当然,AMD 也已经存在了一段时间(尽管它们的设计在这方面相当相似)。虽然细节随着新处理器的变化而变化,但将架构寄存器与物理寄存器分离的重命名功能现在或多或少已成为现实。

        【讨论】:

        • 非常棒的答案——了解# of registered on multithreading perf 的含义很有趣
        • 提到“Itanic”时,您是指“Itanium”吗
        【解决方案8】:

        这完全取决于架构决定。 Intel Itanium 有 128 个通用寄存器和 128 个浮点寄存器,而 Intel x86 只有 8 个通用寄存器和 8 个浮点堆栈。

        【讨论】:

        • x87 堆栈的时代早已过去几十年。现在 x86 CPU 使用更快的 SSE 或其后继者,它们都使用 xmm/ymm 寄存器而不是堆栈
        【解决方案9】:

        X86 实际上是一个 8 寄存器机器(eax/ebx/ecx/edx/esi/edi/ebp/esp)。您将其中的 1 个丢失到堆栈指针/基指针,因此在实际使用中您得到 7,这有点偏低,但即使是一些 RISC 机器也有 8 个(在 THUMB 模式下的 SuperH 和 ARM,因为它们有 16 位指令大小和更多寄存器将太长而无法编码!)。对于 64 位代码,您从 8 升级到 16(他们在指令编码 AFAIK 中使用了一些剩余位)。

        不过,8 个寄存器刚好足以对 CPU 进行流水线处理,这对于 486 和奔腾处理器来说是完美的。其他一些架构,如 6502/65816,在 32 位时代早期就消亡了,因为您无法制作快速的有序流水线版本(您只有 3 个寄存器,而一般数学只有 1 个,所以一切都会导致停顿! )。一旦你到了所有寄存器都被重命名并且一切都乱七八糟的一代(奔腾2等),那么它就不再重要了,如果你一遍又一遍地重用同一个寄存器,你就不会遇到停顿,并且那么8个寄存器就可以了。

        更多寄存器的另一个用途是将循环常量保存在寄存器中,您不需要在 x86 上,因为每条指令都可以执行内存加载,因此您可以将所有常量保存在内存中。这是 RISC 缺少的一个特性(根据定义),虽然它们通过更容易流水线(最长的延迟是 2 个周期而不是 3 个)和稍微超标量来弥补它,但您的代码大小仍然会增加一点。 ..

        添加更多寄存器会产生一些不明显的成本。您的指令会变长,因为您需要更多位,这会增加程序大小,如果您的代码速度受到读取指令的内存带宽的限制,则会减慢您的程序!

        还有一个事实是,您的寄存器文件越大,读取值所需通过的多路复用器级别/通用电路就越多,这会增加延迟,从而可能会降低时钟速度!

        这就是为什么 atm 的传统观点是超过 32 个寄存器并不是一个好主意(没有用,尤其是在无序 CPU 上),而 8 太低了(内存读取仍然很昂贵!),以及为什么理想的架构被认为是 75% RISC 25% CISC 之类的东西,以及为什么 ARM 很受欢迎(平衡得刚刚好!),几乎所有 RISC 架构仍然有一些 CISC 部分(每个内存 OP 中的地址计算,32 位操作码,但不是更多!),为什么 Itanium 失败(128 位操作码?64 个寄存器?内存操作中没有地址计算???)。

        由于所有这些原因,x86 并没有被超越——当然指令编码是完全疯狂的,但除此之外,它为保持效率所做的所有疯狂的重新排序和重命名以及推测性的加载存储疯狂实际上都是真的有用的特性,这正是它在各种更简单的有序设计(如 POWER6)中的优势所在。一旦你重新排序和重命名所有内容,所有指令集或多或少都是相同的,因此很难做出以任何方式实际上更快的设计,除了特定情况(本质上是 GPU)。一旦 ARM cpu 变得像 x86s 一样快,它们就会像 Intel 推出的一样疯狂和复杂。

        【讨论】:

        • x86_64 使用REX prefix 对新寄存器进行编码,因为旧的 x86 编码没有用于新寄存器大小的剩余位
        • Intel/AMD 的 x86 实现速度很快,尽管 insn 设置复杂,但不是因为。他们确实获得了为解码的微指令选择自定义内部表示的灵活性,但如果有收获,他们可以将更明智的指令编码转换为微指令。在某些情况下,可变大小的 insn 编码有几个优点:它有点像压缩,当它们简单且平均小于 4B 时,可以在每个高速缓存行打包更多指令。仍然 insn 解码可能是一个主要瓶颈,除非在 Sandybridge 和更高版本上耗尽 uop 缓存。
        猜你喜欢
        • 2012-01-18
        • 2018-01-16
        • 2012-01-28
        • 1970-01-01
        • 2012-02-10
        • 2020-09-14
        • 1970-01-01
        • 2011-01-20
        • 2012-10-14
        相关资源
        最近更新 更多