为什么处理器有32个寄存器?


52

我一直想知道为什么处理器在32个寄存器处停止。它是迄今为止最快的机器,为什么不仅仅用更多的寄存器来制造更大的处理器呢?这是否意味着减少对RAM的访问?


2
我想超出某个点的所有本地变量都适合寄存器。无论如何,您正在使用的实际数据可能太大了
Niklas B.

14
收益递减。显然,寄存器(在各种意义上)比RAM“昂贵”(或者在各种意义上),否则我们只有8GB的寄存器。
David Richerby 2014年

5
它之所以如此快的原因之一是因为其中有很多。
stackErr 2014年

5
cpu总共有多少个寄存器与一次可以使用多少个寄存器之间是有区别的。
托尔比约恩Ravn的安德森

CPU和GPU分别通过缓存和多线程来隐藏延迟。因此,CPU的寄存器很少,而GPU的寄存器只有数万个。请参阅我在GPU寄存器文件上的调查论文,其中讨论了所有这些权衡和因素。
user984260 '16

Answers:


82

首先,并非所有处理器架构都在32个寄存器处停止。几乎所有在指令集中公开了32个寄存器的RISC体系结构实际上都具有32个整数寄存器和32个更多的浮点寄存器(因此64个)。(浮点“加”使用的寄存器与整数“加”使用的寄存器不同。)SPARC体系结构具有寄存器窗口。在SPARC上,一次只能访问32个整数寄存器,但这些寄存器的作用就像堆栈一样,您一次只能压入和弹出16个新寄存器。HP / Intel的Itanium体系结构在指令集中公开了128个整数和128个浮点寄存器。NVidia,AMD,Intel,ARM和Imagination Technologies的现代GPU都在其寄存器文件中公开了大量寄存器。(我知道这对于NVidia和Intel体系结构是正确的,我对AMD,ARM和Imagination指令集不是很熟悉,但是我认为寄存器文件也很大。)

其次,大多数现代微处理器都实现了寄存器重命名,以消除由于需要重用资源而引起的不必要的序列化,因此基础物理寄存器文件可以更大(某些计算机上为96、128或192个寄存器。)(动态调度)消除了一些编译器需要生成这么多唯一的寄存器名称,同时仍然为调度程序提供更大的寄存器文件。

有两个原因可能导致难以进一步增加指令集中公开的寄存器数量。首先,您需要能够在每条指令中指定寄存器标识符。32个寄存器需要一个5位的寄存器说明符,因此3地址指令(在RISC体系结构中很常见)仅花费32个指令位中的15个来指定寄存器。如果将其增加到6或7位,则可以减少指定操作码和常量的空间。GPU和安腾有很多大的指令。较大的指令需要付出一定的代价:您需要使用更多的指令存储器,因此指令缓存行为不太理想。

第二个原因是访问时间。内存越大,从中访问数据的速度就越慢。(就基本物理而言:数据存储在二维空间中,因此,如果要存储位,则到特定位的平均距离为。)寄存器文件只是一个较小的多端口内存,而使其变大的限制之一是,最终您将需要开始为计算机提供较慢的时钟,以容纳较大的寄存器文件。通常就整体性能而言,这是一个损失。 nO(n)


1
我会提到SPARC64 VIIIfx的256 FPR和32个额外的非窗口GPR,这是通过添加Set XAR指令完成的,该指令为下一个或两个指令提供13位。它是针对HPC的,因此寄存器数更容易理解。我也很想解释与更多寄存器有关的一些权衡和技术。但是您展示了避免更多(甚至是不详尽)答案的智慧。
保罗·克莱顿

2
为“通用”代码增加更多寄存器的逐渐减少的好处可能是值得的,尽管要找到有意义的度量并不容易。我认为Mitch Alsup在comp.arch上提到,将x86扩展到32个寄存器而不是16个,将获得大约3%的性能,而选择的8-16个寄存器扩展的(ISTR)为10-15%。即使对于负载存储ISA,将其设置为64也可能带来一点好处(至少对于当前的GP代码而言)。(顺便说一句,GPU通常在线程之间共享寄存器:例如,一个线程有250个线程,其他线程共有16个私有线程。)
Paul A. Clayton

有趣的是,通常与高级语言相关联的环境管理(因此进行alpha转换)实际上是在寄存器级别使用的。
2014年

@ PaulA.Clayton我一直认为IA-64是拥有最多ISA寄存器的体系结构
phuclv 2014年

@LưuVĩnhPhúcSPARC64 VIIIfx特定于HPC。仅供参考,Am29k(于1987- 8年推出)具有64个全局GPR和128个窗口GPR,这比Itanium(确实具有8个分支寄存器和一个循环计数寄存器,其功能将在某些其他ISA中的GPR)的GPR更高。
保罗·克莱顿

16

限制寄存器数量的另外两个原因:

  • 收益微乎其微:诸如当前的Intel / AMD x64型号之类的CPU具有32kByte和更多的L1-D缓存,访问L1缓存通常仅需要一个时钟周期(相比之下,一个完整的RAM大约需要一百个时钟周期)访问)。因此,与在L1缓存中存储数据相比,在寄存器中存储更多数据几乎无济于事
  • 额外的计算成本:拥有更多的寄存器会产生开销,实际上可能会使计算机变慢:
    • 在多任务环境中,任务开关通常必须将进程的所有寄存器的内容保存到内存中,并必须加载要输入的进程的内容。您拥有的寄存器越多,花费的时间就越长。
    • 同样,在没有寄存器窗口的体系结构中,级联函数调用使用同一组寄存器。因此,调用函数B的函数A使用与B本身相同的寄存器集。因此,B必须保存它使用的所有寄存器的内容(仍然保存A的值),并且必须在返回之前将它们写回(在某些调用约定中,A的工作是在调​​用B之前保存其寄存器内容,但是开销类似)。您拥有的寄存器越多,节省的时间就越长,因此函数调用的成本就越高。

L1高速缓存如何工作,这样我们就不会遇到与寄存器相同的问题?
2014年

4
在高性能处理器上,L1 Dcache延迟通常为3或4个周期(包括地址生成),例如,英特尔的Haswell具有4个周期的延迟(不具有数据依赖性寄存器的延迟也更容易隐藏在流水线中)。与缓存文件相比,Dcache还倾向于每个周期支持较少的访问(例如2次读取,Haswell 1次写入)比寄存器文件(例如4次读取,对于复制文件的Alpha 21264进行6次写入,2次读取4次的文件快1次) 8)。
保罗·克莱顿

@ PaulA.Clayton:如果L1高速缓存具有3-4个周期的延迟,则这意味着拥有一些具有自己的64字地址空间的几套64字的单周期内存可能会有些好处,并且专用的“直接加载/存储”指令,特别是如果有一种方法可以将所有非零值后跟一个单词,说明哪些单词非零,然后将其弹出(将未弹出的所有寄存器清零) 。许多方法都有16到60个字的局部变量,因此将访问时间从3-4个周期减少到1个周期似乎很有帮助。
超级猫

@supercat在学术论文中已经提出了各种堆栈(和全局/ TLS [例如,背包])缓存的思想,以及诸如签名缓冲区PDF)之类的机制。这变得越来越闲谈(因此可能应该结束或转到其他地方)。
Paul A. Clayton 2014年

4

许多代码具有大量的内存访问(典型值是30%)。其中,通常约2/3位是读访问,而1/3位是写访问。这不是由于用尽寄存器而不是访问数组,访问对象成员变量等引起的。

由于C / C ++的制作方式,必须在内存(或数据高速缓存)中完成此操作(您可以获得的所有指针都需要有一个地址,该地址必须潜在地存储在内存中)。如果编译器可以猜测您不会使用疯狂的间接指针技巧来写入变量willy-nilly,则会将其放入寄存器中,这对函数变量很有用,但不适用于全局可访问变量(通常,所有来自malloc的变量) ()),因为基本上不可能猜测全局状态将如何变化。

因此,编译器无论如何都不能使用大约16个以上的通用寄存器来执行任何操作,这并不常见。这就是为什么所有流行的架构都拥有这么多架构的原因(ARM有16种架构)。

MIPS和其他RISC往往有32个寄存器,因为拥有这么多寄存器并不是很困难-成本足够低,所以有点“为什么不?”。超过32个几乎是无用的,并且不利之处在于使寄存器文件的访问时间更长(每次寄存器数量加倍都可能会增加多路复用器的额外层,从而增加更多的延迟...)。平均而言,这还会使指令稍长一些-这意味着,当运行取决于指令内存带宽的程序时,实际上额外的寄存器会减慢您的速度!

如果您的cpu是有序的,并且没有进行寄存器重命名,并且您试图每个周期执行很多操作(超过3个),那么理论上您需要更多的寄存器,因为每个周期的ops数量会增加。这就是Itanium具有如此多寄存器的原因!但是在实践中,除了数字浮点或面向SIMD的代码(Itanium确实擅长)之外,大多数代码将具有大量的内存读/写和跳转,这使每个周期实现3个以上ops的梦想成为不可能(尤其是在面向服务器的软件(如数据库,编译器,高级语言执行(如javascript,仿真等)中))。这就是使Itanium沉没的原因。

归结为计算与执行之间的差异!


2

谁告诉您处理器总是有32个寄存器?x86有8个,ARM 32位,x86_64有16个,IA-64有128个,还有其他许多数字。你可以在这里看看。即使是MIPS,PPC或任何在指令集中具有32个通用寄存器的体系结构,其数量也远大于32,因为始终仍然存在标志寄存器(如果有),控制寄存器...不包括重命名的寄存器和硬件寄存器

一切都有其代价。寄存器的数量越大,执行任务切换时您要做的工作越多,指令编码中需要的空间就越大。如果您的寄存器较少,则在某些功能强大的代码中缺乏寄存器的权衡下,在从函数调用和返回任务或切换任务时不必存储和恢复大量内容

而且,寄存器文件越大,它将越昂贵和复杂。SRAM是最快,最昂贵的RAM,因此仅用于CPU高速缓存。但是与具有相同容量的寄存器文件相比,它仍然便宜得多并且占用的空间更少。


2

例如,典型的英特尔处理器具有“官方”的16个整数和16个矢量寄存器。但实际上,还有更多:处理器使用“寄存器重命名”。如果您有一条指令reg3 = reg1 + reg2,那么如果使用reg3的另一条指令尚未完成,您将遇到问题-您无法执行新指令,以防新指令覆盖reg3之前被前一条指令读取。

因此,大约有160个左右的实际寄存器。因此,上面的简单指令更改为“ regX = reg1 + reg2,并记住regX包含reg3”。没有重命名寄存器,乱序执行将完全陷入水中。


1

我不是电气工程师,但是我认为限制寄存器数量的另一种可能是路由。算术单元数量有限,并且它们必须能够从每个寄存器获取输入,并输出到每个寄存器。当您拥有可以在每个周期执行许多指令的流水线程序时,尤其如此。

一个简单的版本将具有复杂度,使得无法扩展的寄存器数量成为可能,或者以其他方式需要重新设计路由,使其复杂得多,从而以更高的复杂度路由所有内容。O(n2)

通过观看Ivan Godard关于Mill CPU的一些演讲,我得到了这个答案的主意。Mill CPU的部分创新之处在于,您不能输出到任意寄存器-输出都被压入寄存器堆栈或“皮带”,从而减少了布线问题,因为您始终知道输出将到达何处。注意,它们仍然存在将输入寄存器传送到算术单元的路由问题。

有关问题说明,请参见Mill CPU体系结构-传送带(9之2),以及Mill的解决方案。


“他们必须能够从每个寄存器获取输入,并输出到每个寄存器。” -我希望这通常是通过总线实现的,不必为每个寄存器都单独连接到ALU。
user253751'8

1
@immibis:如果您想在300皮秒内传送数据,则总线将无法执行此操作。而且,如果您要移动大量数据(例如,用两个操作数执行三个指令,并在同一周期中每个执行一个结果),则总线绝对不会工作。
gnasher729

0

至于MIPS ISA,轩尼诗和帕特森,《计算机组织与设计》第4版,第4页。176,直接回答此特定问题:

越小越快。对速度的渴望是MIPS具有32个寄存器而不是更多寄存器的原因。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.