Performance 如果寄存器速度如此之快,为什么不';难道我们没有更多的吗?

Performance 如果寄存器速度如此之快,为什么不';难道我们没有更多的吗?,performance,history,cpu-registers,assembly,Performance,History,Cpu Registers,Assembly,在32位中,我们有8个“通用”寄存器。使用64位时,数量会翻倍,但似乎与64位本身的变化无关。 现在,如果寄存器的速度如此之快(没有内存访问),那么为什么没有更多的寄存器呢?CPU构建器不应该在CPU中使用尽可能多的寄存器吗?为什么我们只有我们所拥有的数量,其逻辑限制是什么?它们一直在添加寄存器,但它们通常与专用指令(如SIMD、SSE2等)相关联,或者需要编译到特定的CPU体系结构,这降低了可移植性。现有指令通常在特定寄存器上工作,如果其他寄存器可用,则无法利用它们。传统指令集和所有指令集。不

在32位中,我们有8个“通用”寄存器。使用64位时,数量会翻倍,但似乎与64位本身的变化无关。

现在,如果寄存器的速度如此之快(没有内存访问),那么为什么没有更多的寄存器呢?CPU构建器不应该在CPU中使用尽可能多的寄存器吗?为什么我们只有我们所拥有的数量,其逻辑限制是什么?

它们一直在添加寄存器,但它们通常与专用指令(如SIMD、SSE2等)相关联,或者需要编译到特定的CPU体系结构,这降低了可移植性。现有指令通常在特定寄存器上工作,如果其他寄存器可用,则无法利用它们。传统指令集和所有指令集。

不只是拥有大量寄存器有很多原因:

  • 它们与大多数管道阶段高度相关。首先,您需要跟踪他们的生命周期,并将结果返回到以前的阶段。复杂性很快变得难以处理,而且所涉及的电线数量(字面上)也以同样的速度增长。它在面积上很昂贵,这最终意味着它在功率、价格和性能上都很昂贵
  • 它占用指令编码空间。16个寄存器占用源和目标的4位,如果有3个操作数指令(如ARM),则另占用4位。仅仅为了指定寄存器就占用了大量的指令集编码空间。这最终会影响解码、代码大小和复杂性
  • 有更好的方法可以达到同样的效果
现在我们确实有很多寄存器——它们只是没有显式编程。我们有“注册重命名”。虽然您只访问一个小的集合(8-32个寄存器),但它们实际上是由一个更大的集合(例如64-256)支持的。然后CPU跟踪每个寄存器的可见性,并将它们分配给重命名的集合。例如,您可以在一行中多次加载、修改、然后存储到寄存器,并根据ARM中的缓存未命中等,让这些操作中的每一个实际独立执行:

ldr r0, [r4]
add r0, r0, #1
str r0, [r4]
ldr r0, [r5]
add r0, r0, #1
str r0, [r5]
Cortex A9内核进行寄存器重命名,因此第一次加载到“r0”实际上是到一个重命名的虚拟寄存器——我们称之为“v0”。负载、增量和存储发生在“v0”上。同时,我们还将再次执行对r0的加载/修改/存储,但这将被重命名为“v1”,因为这是一个使用r0的完全独立的序列。假设“r4”中指针的加载由于缓存未命中而暂停。没关系-我们不需要等待“r0”准备就绪。因为它被重命名了,我们可以用“v1”(也映射到r0)运行下一个序列——也许这是缓存命中率,我们刚刚获得了巨大的性能胜利

ldr v0, [v2]
add v0, v0, #1
str v0, [v2]
ldr v1, [v3]
add v1, v1, #1
str v1, [v3]
我认为x86现在已经有了大量重命名的寄存器(大概256个)。这意味着每个指令有8位乘以2,只是为了说明源和目标是什么。这将大大增加穿过核心所需的电线数量及其尺寸。因此,在16-32个寄存器周围有一个最合适的位置,大多数设计人员已经习惯了这个位置。对于无序的CPU设计,寄存器重命名是缓解这种情况的方法

编辑:在此基础上无序执行和寄存器重命名的重要性。一旦有了OOO,寄存器的数量就不再那么重要了,因为它们只是“临时标记”,并且被重命名为更大的虚拟寄存器集。您不希望数字太小,因为编写小的代码序列会变得很困难。对于x86-32来说,这是一个问题,因为有限的8个寄存器意味着大量临时寄存器最终会通过堆栈,而内核需要额外的逻辑来将读/写转发到内存。如果您没有OOO,那么您通常谈论的是一个小内核,在这种情况下,大寄存器集的成本/性能效益很差

因此,寄存器组大小有一个自然的最佳点,对于大多数CPU类,它的最大值约为32个体系结构寄存器。x86-32有8个寄存器,它肯定太小了。ARM有16个寄存器,这是一个很好的折衷方案。32个寄存器稍微太多了,如果有的话-您最终不需要最后10个左右的寄存器

这些都不涉及为SSE和其他向量浮点协处理器提供的额外寄存器。它们作为一个额外的集合是有意义的,因为它们独立于integer内核运行,并且不会使CPU的复杂度呈指数级增长。

我们确实有更多这样的集合 由于几乎每一条指令都必须选择1、2或3个体系结构可见寄存器,因此扩展它们的数量将使每条指令上的代码大小增加几位,从而降低代码密度。它还增加了必须保存为线程状态并部分保存在函数状态中的线程数量。这些操作经常发生。管道联锁必须检查每个寄存器的记分牌,这具有二次时间和空间复杂性。也许最大的原因仅仅是与已经定义的指令集的兼容性

但事实证明,多亏了,我们确实有很多可用的寄存器,我们甚至不需要保存它们。CPU实际上有许多寄存器集,当代码执行时,它会自动在它们之间切换。它这样做纯粹是为了给你更多的寄存器

例如:

在只有r0-r7的体系结构中,CPU可以自动重写以下代码,如下所示:

load  r1, a
store r1, x
load  r10, b
store r10, y

在这种情况下,r10是一个隐藏寄存器,暂时替代r1。CPU可以判断r1的值在第一次存储后不再使用。这允许延迟第一次加载(即使是片上缓存命中通常也需要几个周期),而不需要延迟第二次加载或第二次存储。

要在这里添加一些有趣的信息,您会注意到具有8个相同大小的寄存器允许操作码与
load  r1, a
store r1, x
load  r10, b
store r10, y