在32位中,我们有8个“通用”寄存器。使用64位时,数量增加了一倍,但似乎与64位更改本身无关。
现在,如果寄存器是如此之快(无内存访问),为什么自然不存在更多的寄存器呢?CPU建设者不应该在CPU中使用尽可能多的寄存器吗?为什么我们只有数量的逻辑限制是什么?
Answers:
有很多原因,您不仅拥有大量的寄存器:
如今,我们确实有很多寄存器-只是没有显式编程。我们有“注册重命名”。尽管您仅访问一小套(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的操作,但是由于使用r0是完全独立的序列,因此将其重命名为“ v1”。假设由于高速缓存未命中而使来自“ 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设计者来说是一个不错的选择,而对于乱序的CPU设计,寄存器重命名是缓解这种情况的一种方法。
编辑:乱序执行和对此重命名的重要性。一旦有了OOO,寄存器的数量就无关紧要,因为它们只是“临时标签”,并被重命名为更大的虚拟寄存器集。您不希望数字太小,因为很难编写小的代码序列。对于x86-32来说,这是一个问题,因为有限的8个寄存器意味着很多临时对象最终要通过堆栈,并且内核需要额外的逻辑才能将读/写转发到内存。如果您没有OOO,则通常是在谈论小型内核,在这种情况下,大型寄存器集会降低成本/性能。
因此,对于寄存器库大小来说,自然是一个不错的选择,对于大多数类型的CPU,最大可以容纳约32个架构寄存器。x86-32有8个寄存器,绝对太小了。ARM提供了16个寄存器,这是一个很好的折衷方案。32个寄存器(如果有的话)有点太多-您最终不需要最后10个左右。
这些都不涉及您为SSE和其他矢量浮点协处理器获得的额外寄存器。这些作为额外的集合是有意义的,因为它们独立于整数内核运行,并且不会以指数方式增加CPU的复杂性。
因为几乎每条指令都必须选择1、2或3个结构上可见的寄存器,所以扩展寄存器的数量会使每条指令的代码大小增加几位,从而降低代码密度。这也增加了必须保存为线程状态并且部分保存在函数的激活记录中上下文。 这些操作经常发生。流水线联锁必须检查每个寄存器的记分板,这具有二次时间和空间复杂性。也许最大的原因仅仅是与已经定义的指令集兼容。
但是事实证明,由于寄存器重命名,我们确实确实有很多可用的寄存器,我们甚至不需要保存它们。CPU实际上有许多寄存器集,并且在代码执行时会自动在它们之间切换。这样做纯粹是为了让您获得更多的寄存器。
例:
load r1, a # x = a
store r1, x
load r1, b # y = b
store r1, y
在只有r0-r7的体系结构中,以下代码可能会被CPU自动重写为:
load r1, a
store r1, x
load r10, b
store r10, y
在这种情况下,r10是一个隐藏寄存器,暂时替代了r1。CPU可以知道第一次存储后就不再使用r1的值。这允许延迟第一加载(即使片上缓存命中通常也需要几个周期),而无需延迟第二加载或第二存储。
它们一直在增加寄存器,但是它们通常与特殊目的的指令(例如SIMD,SSE2等)联系在一起,或者需要编译为特定的CPU架构,从而降低了可移植性。现有指令通常在特定寄存器上运行,并且如果可用,则无法利用其他寄存器。旧版指令集和所有。
要在此处添加一些有趣的信息,您会注意到具有8个相同大小的寄存器使操作码可以保持十六进制表示法的一致性。例如,指令push ax
是x86上的操作码0x50,最后一个寄存器di的操作码升至0x57。然后,指令pop ax
从0x58开始并上升到0x5F pop di
以完成第一个基数16。十六进制一致性由每个大小的8个寄存器保持。