为什么最常见的 CPU(x86)中只有四个寄存器?如果添加更多寄存器,速度不会有很大的提高吗?什么时候会增加更多的寄存器?
9 回答
x86总是有四个以上的寄存器。原来有CS、DS、ES、SS、AX、BX、CX、DX、SI、DI、BP、SP、IP和Flags。其中,七个(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 也已经存在了一段时间(尽管它们的设计在这方面相当相似)。虽然细节随着新处理器的变化而变化,但将架构寄存器与物理寄存器分离的重命名功能现在或多或少已成为现实。
现在有4个以上。如果您查看x86 架构的历史,您会发现它是从 8086 指令集演变而来的。英特尔一直希望在其处理器系列中保持一定程度的向后兼容性,因此所有后续处理器都只是将原始 A、B、C、D 寄存器扩展到更多位数。原来的段寄存器现在可以用于一般目的,因为不再有真正的段(这是一个过度简化,但大致正确)。新的 x64 架构还提供了一些额外的寄存器。
X86 实际上是一个 8 寄存器机器(eax/ebx/ecx/edx/esi/edi/ebp/esp)。您将其中 1 个丢失到堆栈指针/基指针,因此在实际使用中您得到 7,这有点偏低,但即使是某些 RISC 机器也有 8 个(THUMB 模式下的 SuperH 和 ARM,因为它们有 16 位指令大小和更多寄存器将太长而无法编码!)。对于 64 位代码,您从 8 升级到 16(他们在指令编码 AFAIK 中使用了一些剩余位)。
尽管如此,8 个寄存器刚好足以流水线化 CPU,这对于 486s 和 pentiums 来说是完美的。其他一些架构,如 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 cpus 变得像 x86s 一样快,它们就会像 Intel 推出的一样疯狂和复杂。
- 寄存器过去实施起来很昂贵。
- 不必要。现代 x86 CPU 上的寄存器数量远远超出了 CPU 所显示的数量 - CPU 维护影子寄存器,这些寄存器根据指令流根据需要进行重命名。
- 在 AMD64/x86_64 中。在 64 位模式下运行时,通用寄存器的数量加倍(除了它们的大小加倍)。
有许多具有更多寄存器的体系结构(ARM、PowerPC 等)。有时,它们可以实现更高的指令吞吐量,因为在操作堆栈时完成的工作更少,并且指令可能更短(无需引用堆栈变量)。对应的一点是,由于更多的寄存器节省,函数调用变得更加昂贵。
更多的寄存器并不一定会让事情变得更快,它们会使 CPU 架构更加复杂,因为寄存器必须靠近其他组件,并且许多指令只能在特定的寄存器上工作。
但是现代 CPU 有四个以上的寄存器,从我的头顶上看,有 AX、BX、CX、DX、SI、DI、BP……然后一个 CPU 有内部寄存器,例如 PIC(处理器指令计数器)
好吧,还有更多,这四个只是特殊的,我认为它们是“通用目的”,所有这些的原因以及为什么其余部分没有被使用的原因是:
- x86 并不是事实上标准的最佳指令集,Intell 只是看到了向后兼容的潜力,一旦 AMD 加入它只是时间问题。
- 它现在是事实上的标准,所以我们必须接受它。
- 添加更多寄存器将不再是 x86,因此您的意思是“基于 x86 创建具有更多寄存器的新指令集”。
- 大多数编译器不会使用这些,因为它们也可以编译到 x86 以同时针对 x86 的超集。
- 更多的寄存器意味着更昂贵的硬件。
寄存器使用的内存在 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:指令指针。保存程序计数器,即当前指令地址。
嗯..... (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 个。:)
它仅取决于架构决策。Intel Itanium 有 128 个通用寄存器和 128 个浮点寄存器,而 Intel x86 只有 8 个通用寄存器和 8 个浮点数的堆栈。