【问题标题】:Why are CPU registers fast to access?为什么 CPU 寄存器访问速度快?
【发布时间】:2011-03-31 21:54:37
【问题描述】:

注册变量是一种众所周知的快速访问方法 (register int i)。但是为什么寄存器位于层次结构的顶部(寄存器、缓存、主存储器、辅助存储器)?是什么让访问寄存器如此之快?

【问题讨论】:

  • 我不太明白你在问什么。寄存器位于顶部,因为它们位于顶部。没有什么比 ALU 更靠近完成工作的地方了。将数据保存在寄存器中意味着没有数据传输开销。顺便说一句,该关键字对现代优化编译器没有多大作用。
  • 一个寄存器直接存储在CPU中!
  • 更多关于 ALU 的信息:en.wikipedia.org/wiki/Arithmetic_logic_unit

标签: cpu-architecture cpu-registers


【解决方案1】:

寄存器本质上是内部 CPU 内存。因此,对寄存器的访问比任何其他类型的内存访问都更容易、更快捷。

【讨论】:

    【解决方案2】:

    寄存器是 CPU 的核心部分,CPU 的大部分指令集将针对寄存器而不是内存位置进行调整。访问寄存器的值通常需要很少的时钟周期(可能只需 1 个),一旦访问内存,事情就会变得更加复杂,并且会涉及到缓存控制器/内存总线,并且操作将花费更多时间。

    【讨论】:

      【解决方案3】:

      寄存器是直接连接到 ALU 的电路,ALU 包含算术电路。每个时钟周期,CPU 内核的寄存器单元可以将六个左右的变量输入其他电路。实际上,数据路径中的单元(ALU 等)可以通过旁路网络直接相互提供数据,这在某种程度上形成了高于寄存器的层次结构——但它们仍然使用寄存器——号码互相称呼。 (全流水线 CPU 的控制部分将数据路径单元动态映射到寄存器编号。)

      C 中的register 关键字没有任何用处,您不应该使用它。编译器决定哪些变量应该在寄存器中以及何时。

      【讨论】:

      • 将执行单元直接相互连接的线路(和 MUX)称为转发或旁路网络,因为它绕过了写回寄存器然后从寄存器文件中读取的延迟。这就是 add 指令即使在流水线 CPU 中也可以具有 1c 延迟的原因。 (参见Wikipedia's Classic RISC pipeline 文章。即使在乱序超标量 CPU 中,这个想法也是一样的,但多个执行单元可以并行相互转发。)
      【解决方案4】:

      正如 Bill 提到的,每个微控制器都有一个 CPU,它具有 ALU 的基本组件、一些 RAM 以及其他形式的内存来协助其操作。 RAM 就是您所说的主内存。

      ALU 处理所有的算术逻辑运算并对任何操作数进行操作以执行这些计算,它将操作数加载到寄存器中,对它们执行操作,然后您的程序直接或间接访问这些寄存器中存储的结果.

      由于寄存器最接近 CPU 的核心(也就是处理器的大脑),它们在链中的位置更高,当然直接在寄存器上执行的操作占用的时钟周期最少。

      【讨论】:

        【解决方案5】:

        较小的记忆通常比较大的记忆快;它们还可以需要更少的位来寻址。一个 32 位的指令字可以容纳三个四位寄存器地址,并且有很大的空间用于操作码和其他东西;一个 32 位内存地址将完全填满一个指令字,而没有空间容纳其他任何东西。此外,寻址存储器所需的时间以超过与存储器大小的对数成比例的速率增加。从 4 gig 的内存空间访问一个字将比从 16 个字的寄存器文件中访问一个字要花费数十倍甚至数百倍的时间。

        能够处理来自小型快速寄存器文件的大多数信息请求的机器将比使用较慢内存的机器要快。

        【讨论】:

          【解决方案6】:

          有几个因素导致寄存器比缓存快。

          直接寻址与间接寻址

          首先,寄存器是根据指令中的位直接寻址的。许多 ISA 将源寄存器地址编码在一个恒定位置,允许在指令解码之前将它们发送到寄存器文件,推测将使用一个或两个值。最常见的内存寻址模式通过寄存器间接寻址。由于基数+偏移寻址的频率,许多实现针对这种情况优化了流水线。 (在不同阶段访问缓存会增加复杂性。)缓存还使用标记并且通常使用集合关联性,这往往会增加访问延迟。不必处理未命中的可能性也降低了寄存器访问的复杂性。

          复杂因素

          乱序实现和具有堆叠或旋转寄存器的 ISA(例如,SPARC、Itanium、XTensa)会重命名寄存器。专用缓存,例如 Todd Austin 的背包缓存(直接使用偏移量索引缓存)和一些堆栈缓存设计(例如,使用小的堆栈帧号并使用该帧号和偏移量直接索引专用堆栈缓存的块)避免寄存器读取和添加。签名缓存将寄存器名称和偏移量与一小块存储相关联,从而为访问结构的较低成员提供较低的延迟。索引预测(例如,异或偏移和基数,避免进位传播延迟)可以减少延迟(以处理错误预测为代价)。人们也可以更早地为寄存器间接等更简单的寻址模式提供内存地址,但在两个不同的流水线阶段访问缓存会增加复杂性。 (Itanium 仅提供寄存器间接寻址 - 带有选项后增量。)方式预测(以及在直接映射缓存的情况下命中推测)可以减少延迟(同样具有误预测处理成本)。 Scratchpad(又名紧密耦合)内存没有标签或关联性,因此可以稍微快一些(并且访问能量较低),并且一旦确定访问该区域,就不可能错过。背包缓存的内容可以被视为上下文的一部分,并且在该缓存被填满之前,上下文不会被认为是准备好的。理论上,寄存器也可以延迟加载(尤其是安腾堆栈寄存器),因此必须处理寄存器未命中的可能性。

          固定大小与可变大小

          寄存器通常是固定大小的。这避免了移动从对齐存储中检索到的数据以将实际最低有效位放置到执行单元的适当位置的需要。此外,许多加载指令对加载的值进行符号扩展,这会增加延迟。 (零扩展不依赖于数据值。)

          复杂因素

          一些 ISA 确实支持子寄存器,尤其是 x86 和 zArchitecture(源自 S/360),这可能需要预移位。还可以以较低的延迟提供完全对齐的负载(可能以其他负载一个周期的额外延迟为代价);子字加载足够常见,并且增加的延迟足够小,以至于特殊大小写并不常见。符号扩展延迟可能隐藏在进​​位传播延迟之后;或者,可以使用符号预测(可能只是推测性的零扩展)或将符号扩展视为慢速情况。 (对未对齐负载的支持会使缓存访问更加复杂。)

          小容量

          有序 64 位 RISC 的典型寄存器文件只有大约 256 个字节(32 个 8 字节寄存器)。对于现代缓存而言,8KiB 被认为很小。这意味着将物理尺寸和静态功率相乘以提高速度对总面积和静态功率的影响要小得多。更大的晶体管具有更高的驱动强度,其他增加面积的设计因素可以提高速度。

          复杂因素

          一些 ISA 具有大量架构寄存器,并且可能具有非常宽的 SIMD 寄存器。此外,一些实现添加了额外的寄存器用于重命名或支持多线程。使用 SIMD 并支持多线程的 GPU 可以具有特别大容量的寄存器文件; GPU 寄存器文件也不同于 CPU 寄存器文件,它通常是单端口的,每个周期访问一个操作数/结果的向量元素的数量是可用于执行的四倍(例如,使用 512 位宽的乘加执行,读取三个操作数各 2KiB,并写入 2KiB 结果)。

          常见案例优化

          由于寄存器访问旨在成为常见情况,因此将面积、功耗和设计工作用于提高此功能的性能更有利可图。如果 5% 的指令不使用源寄存器(直接跳转和调用、寄存器清除等),70% 使用一个源寄存器(简单加载、立即数操作等),25% 使用两个源寄存器,75 % 使用目标寄存器,而 50% 访问数据内存(40% 加载,10% 存储)——基于 SPEC CPU2000 用于 MIPS 的数据的粗略近似——然后是 3 倍以上(对时间更关键) 读取来自寄存器而不是内存(每条指令 1.3 对 0.4)和

          复杂因素

          并非所有处理器都是为“通用”工作负载而设计的。例如,处理器使用内存中的向量并使用寄存器作为向量起始地址、向量长度和累加器的点积性能可能没有理由优化寄存器延迟(极端并行性简化了隐藏延迟)并且内存带宽将比寄存器更重要带宽。

          小地址空间

          寄存器的最后一个小优点是地址空间很小。这减少了索引存储阵列时地址解码的延迟。可以将地址解码想象为一系列二进制决策(存储块的一半或其他)。一个典型的高速缓存 SRAM 阵列有大约 256 个字线(列、索引地址)——8 位要解码——而且 SRAM 阵列的选择通常还涉及地址解码。一个简单的有序 RISC 通常有 32 个寄存器 — 5 位要解码。

          复杂因素

          现代高性能处理器可以轻松拥有 8 位寄存器地址(安腾在上下文中有超过 128 个通用寄存器,而高端无序处理器可以拥有更多寄存器)。相对于上述考虑,这也是一个不太重要的考虑因素,但不应忽视。

          结论

          上述许多考虑因素重叠,这是优化设计的预期。如果期望一个特定的功能是通用的,那么不仅实现会被优化,接口也会被优化。限制灵活性(直接寻址、固定大小)自然有助于优化,更小更容易更快。

          【讨论】:

          • 确实,在现代(Haswell?)Intel x86 上,使用 AH (RAX[15:8]) 等高 8 位部分寄存器作为源寄存器会增加 1 个周期的延迟。 movsx edx, al(低 8 位)比 movsx edx, ah 快。 (即使关键路径不是通过 AH!例如,add cl, ah 从 CL->CL 以及从 AH->CL 都有 2 个周期的延迟。)
          • 如果有人想知道,How exactly do partial registers on Haswell/Skylake perform? Writing AL seems to have a false dependency on RAX, and AH is inconsistent 提供了关于未在现代 Intel 上单独重命名的低 8 寄存器的详细信息,这与 P6 系列和 SnB 不同。并且在写入 AH/BH/CH/DH 时仍然被重命名,但随着合并 uop 可能不得不自己在一个循环中发出。
          • Is there a penalty when base+offset is in a different page than the base? 调查了 Sandybridge-family 的 AGU 快捷方式的一些细节,用于[reg + 0..2047] 形式的寻址模式。似乎他们推测最终地址将与基址寄存器在同一页中,从而提前 1 个周期开始 TLB 访问。显然,这是在关键路径上。似乎只有当基本 reg 本身来自负载而不是 ALU uop 时才会这样做,因此它只在负载使用延迟至关重要的指针追踪工作负载中尝试这样做。
          猜你喜欢
          • 1970-01-01
          • 2014-01-18
          • 2011-11-23
          • 2011-08-30
          • 2020-04-24
          • 2017-05-03
          • 2018-12-22
          • 2021-01-16
          • 1970-01-01
          相关资源
          最近更新 更多