20

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

4

6 回答 6

22

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

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

于 2010-08-19T03:19:16.907 回答
11

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

于 2010-08-19T03:19:09.857 回答
3

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

直接与间接寻址

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

复杂因素

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

固定大小与可变大小

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

复杂因素

一些 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 个通用寄存器,而更高端的乱序处理器可以有更多的寄存器)。相对于上述考虑,这也是一个不太重要的考虑因素,但不应忽视它。

结论

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

于 2020-06-04T21:20:41.673 回答
2

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

于 2010-08-19T03:16:51.983 回答
1

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

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

于 2010-08-20T02:01:01.743 回答
0

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

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

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

于 2010-08-19T03:22:57.430 回答