从理论上讲,不需要寄存器;所有微处理器在没有寄存器的情况下仍然可以工作。但是,这种看似微不足道的添加帮助使微处理器更加高效。
为什么我们不能拥有更多的寄存器来进一步从中受益呢?它们只是片上存储器,可以想象添加起来不是很困难吗?哪些因素影响了寄存器的数量而不是原来的数量,比如说多了十倍?
从理论上讲,不需要寄存器;所有微处理器在没有寄存器的情况下仍然可以工作。但是,这种看似微不足道的添加帮助使微处理器更加高效。
为什么我们不能拥有更多的寄存器来进一步从中受益呢?它们只是片上存储器,可以想象添加起来不是很困难吗?哪些因素影响了寄存器的数量而不是原来的数量,比如说多了十倍?
Answers:
有几个因素:
高性能微体系结构使用寄存器重命名。也就是说,物理寄存器的数量大于体系结构可见的寄存器的数量,并且它们能够跟踪它们的独立使用。
寄存器数量加倍不会使性能提高一倍。ISTR(来自计算机体系结构,一种量化方法)从16个寄存器增加到32个寄存器会带来10%的改善,前提是假定增加不会带来不利影响(这是非常乐观的假设)。
在体系结构上可见的寄存器具有成本。例如:
尽管寄存器和RAM都是内存,但是它们以不同的方式访问,以反映访问它们的成本(在芯片区域或隐藏时钟周期内)。
寄存器与ALU紧密绑定,可以担当数据源,接收器,修饰符等的许多角色。因此,它们需要大量的宽复用连接。在某些架构中,我们可以写出R1 <= R2 + R3,而这恰恰是在单个时钟周期内发生的。每个寄存器都直接在操作码中寻址,这种寻址是非常有限的资源。
由于寄存器的实现成本很高,因此在大多数体系结构中,寄存器的数量通常限于10/20的数量级。
RAM松散地绑定到CPU,通常通过单个共享连接进行引导。这使得实现大量RAM的成本大大降低。RAM地址通常来自寄存器存储的地址,因此不会占用大量指令宽度。
SPARC是一种有趣的体系结构,具有72至640个64位寄存器,具有32个寄存器上下文,可以通过重叠进行移位,以通过参数传递进行快速子例程调用。您往往不会在成本很重要的PC和服务器中找到它们,例如在99.999%的应用程序中。
在大多数情况下,寄存器的数量是成本,复杂性和实用性之间的折衷。
寄存器被实现为多端口静态RAM,这使其比其他存储选项成本更高(芯片面积)。
然后,它们与处理器的指令集耦合,增加寄存器的数量会增加指令集的复杂性。因此,如果您想与指令集保持兼容,就不能仅仅增加下一代处理器中可用寄存器的数量来提高效率,这些程序就不会使用它们。
接下来,您真正需要多少个寄存器?它们的用途是有限的。考虑您编写了一种算法,该算法在1024字节上执行一些数学运算,比方说乘以5。使用当前的寄存器计数,最终会得到以下结果:
load operand1=5
load address
loop: load operand2=byte1@address
multiply Register1 with Register2
store result
increment address
if address = end goto endLoop
jump loop
endLoop:
现在,如果您将具有1024个寄存器并将所有数据存储在其中,则程序将如下所示:
multiply Register1 with Register2
multiply Register1 with Register3
multiply Register1 with Register4
multiply Register1 with Register5
multiply Register1 with Register6
...
由于每个指令都是不同的指令,因此必须将每个指令都写出。因此,您所需的程序存储器正在爆炸。在意识到这一点之后,您可能希望引入一些说明,例如multiply register1 with register(2 to 256)
。但是什么时候停止,您是否提供所有组合的说明?
因此,也许我们目前可用的数字是成本,复杂性和实用性之间的良好折衷。
multiply Register1 with Register2 multiply Register1 with Register3
非常不切实际,因为数据必须直接或间接来自计算机外部,因此需要加载寄存器,并且需要在某个地方直接或间接使用结果,因此需要存储寄存器。实际上,针对高级语言的体面的优化编译器将“展开”第一个程序的循环以创建类似于第二个程序的内容,从而优化寄存器使用,内存延迟,也许是高速缓存占用率和执行速度。
multiply register1 with register(2 to 256)
说明。流水线显着提高了CPU吞吐量,尤其是对于更易于解码和执行指令的情况。因此,可以通过使用几个具有较高执行率的较简单的指令来实现复杂的大量指令的效果。具有更多数量的寄存器有助于编译器生成许多独立的指令(不共享寄存器的指令),这些指令可以独立完成,从而提高了吞吐量。您的示例=更多的寄存器更好。
寄存器非常昂贵。非常贵。与其说寄存器本身,不如说是寄存器之间的所有连接。假设您有一条指令reg1 = reg2 + reg3。快速实施,您需要在一个周期内从两个寄存器读取数据,并在第二个周期内写入另一个寄存器。现在,如果您有一个处理器每个周期可以执行多个指令(例如3条指令),则您需要能够每个周期从六个寄存器读取数据,并将数据写入3个寄存器。那是非常可怕的,非常快的连接。
当然,您可以只使用更多的晶体管。问题是:速度下降。您需要更多的硬件来从更多的寄存器中进行选择。寄存器文件的空间变大。所有这些使事情变慢。因此,使用相同的技术,您可能能够拥有16个寄存器并以2,600 MHz运行,或者有32个寄存器并以2,400 MHz运行。现在,额外的寄存器必须弥补时钟速度的显着下降。
什么因素影响寄存器的数量
寄存器,缓存,RAM均使用不同的存储技术来实现。
不同的技术在以下方面有所不同
一个示例:CPU中的内部寄存器是静态随机存取存储器,而计算机主存储器是动态随机存取存储器
静态RAM二进制单元使用6晶体管电路实现,而动态RAM二进制单元则使用电容器和晶体管实现。比较SRAM和DRAM
因此,增加快速,昂贵,密度较小的内存的数量并不是一件实际的事情。实际上,我们可能会使用其中的一些,编写良好的程序会将最常用的数据存储在这些快速寄存器中,而较不常用的数据则存储在较慢的内存中。
寄存器的地址包含在一条指令中,该指令根据可表示该地址的位数限制访问寄存器的数量。例如,在MIPS体系结构中,32位长度的指令仅保留5位来表示可访问寄存器的地址,这将寄存器的数量限制为2 5 = 32寄存器。增加寄存器的数量将需要增加指令长度,以包括足以访问所有寄存器的位。
如果您查看处理器的指令集,则有多种将它们分组的方法。例如,所有ADD
指令可能和所有XOR
指令一起分组。
在同一条指令的每一组中,可能存在在存储器或寄存器上运行的版本。正是这个子分组有效地定义了处理器拥有的寄存器数量。
作为一个8位的假设示例,假设$Ax
指令可能是ADD
指令,也$Cx
可能是XOR
指令。通过这种设计,仅剩下四个位来定义操作数!
$x0
可以是累加器本身)。当然,我们已经过了8位指令集。但是,这种逻辑过去仍然可以帮助定义寄存器集-将来它将继续这样做。
编辑(按要求)
说前四位的指令:ADD
,SUB
,XOR
,MOV
,CMP
等这里有16种可能性。然后,对于那些有意义的寄存器间指令(例如ADD Rx,Ry
),您需要指定Rx
和Ry
。说接下来的两个位用于x
,最后两个位用于y
。从而:
ADD R1, R2 => 'ADD' + 'R1' + 'R2' => $A0 + $04 + $02
仅用两位来定义这样的寄存器,总共只有四个寄存器的空间!
顺便说一句,您会注意到某些寄存器组合没有任何意义。例如,MOV Rx, Rx
(什么都不做)和SUB Rx, Rx
(总是产生0
)。这些可能成为特殊情况的说明:
SUB Rx, Rx
可能成为NOT Rx
-单操作数指令。MOV Rx, Rx
可能变成一条MOV
以第二个字节为立即值的指令,解释为MOV Rx, #$yy
。通过这种方式,您可以“玩弄”指令图,为其他无用或无意义的指令填充空洞,从而为程序员提供更大的指令集。但最终,指令集定义了寄存器集。