学习编译器课程,我想知道为什么要使用寄存器。 通常情况下,呼叫者或被呼叫者必须保存寄存器值,然后恢复。
在某种程度上,他们总是最终使用堆栈。使用寄存器真的值得创造额外的复杂性吗?
请原谅我的无知。
更新:请知道,寄存器比RAM和其他类型的缓存更快。我主要担心的是,必须“保存”寄存器中的值,然后将其“恢复”到寄存器中。在这两种情况下,我们都在访问某种缓存。首先使用缓存不是更好吗?
答案 0 :(得分:8)
在速度/延迟层次结构中,寄存器最快(通常为零周期延迟),L1缓存是下一个(通常是1个或更多个延迟周期),然后在此之后迅速下降。因此,通常寄存器访问是“免费的”,而内存访问总是涉及一些成本,即使缓存该访问也是如此。
保存和恢复寄存器通常只发生在(a)函数调用或上下文切换的开始/结束时,或(b)当编译器用完临时变量的寄存器并需要“溢出”一个或多个寄存器时回到记忆中。通常,优化良好的代码会将大多数频繁访问的(“热”)变量保留在寄存器中,至少在函数的最内层循环中。
答案 1 :(得分:2)
我说这对编译器来说不是一个问题,因为它与CPU有关。编译器必须使用目标体系结构。
以下是其他答案所掩盖的内容:它取决于实际电路级别的CPU架构。机器指令归结为从某处获取数据,修改数据,加载或转到下一条指令。
想想问题就像木工为你修建或修理椅子一样。他的问题将是“椅子在哪里”,以及“椅子需要做什么”。他或许可以在你的房子里修理它,或者他可能需要把椅子带回他的商店去做。无论哪种方式都可行,但取决于他是如何准备在固定地点以外工作的。它可能会减慢他的速度,也可能是他的专长。
现在,回到CPU。
无论CPU有多么并行,例如具有多个加法器或指令解码流水线,这些电路都位于芯片上的特定位置,并且数据必须加载到可以执行操作的位置。该程序负责将数据移入和移出这些位置。在基于堆栈的机器中,它可能提供直接修改数据的指令,但它可能在微代码中进行内务处理。无论数据来自堆栈还是来自堆,加法器都以相同的方式工作。不同之处在于程序员可用的编程模型。寄存器基本上是处理数据的定义位置。
答案 2 :(得分:2)
嗯,似乎答案也是在书中(java中的现代编译器实现)。 这本书提出了4个答案:
答案 3 :(得分:1)
在延迟和带宽方面,访问RAM通常比访问寄存器慢得多。有些CPU具有有限大小的硬件堆栈 - 这允许将寄存器推送到堆栈并将其弹回 - 但它们仍然直接使用寄存器进行计算。使用纯堆栈机器(其中有许多学术示例)也很困难,增加了更多的复杂性。
答案 4 :(得分:1)
基于堆栈和寄存器的计算机之间的区别是模糊的。许多现代注册机使用寄存器重命名来隐藏堆栈,只在内部寄存器耗尽时才将数据转储到实际堆栈。一些旧的堆栈机器做了类似的事情,将几个指令和窥视孔流水线化,将push-modify-pop序列优化为就地修改。
现代CPU使用花式并行执行指令的方式,堆栈与寄存器机器之间可能没有太大区别。寄存器机器可能略有优势,因为编译器可以提供有关数据重用的更好提示,但如果英特尔不愿意设计一个,那么十亿美元的Lisp堆栈机器将会非常快速。
答案 5 :(得分:1)
我相信你在问为什么要使用寄存器,因为无论如何变量最终会进入堆栈。
答案是,将寄存器视为堆栈顶部前5或6(或其他)项目的缓存。如果堆栈的顶部被访问的次数远多于底部(在许多程序中都是如此),那么拥有这个缓存会加快速度。
我想你可以说为什么有用户可见的寄存器,而不是前几位的透明缓存。我不确定,但我怀疑让编译器知道将缓存哪些值可以让它更多地优化存储分配。毕竟,如果堆栈中存在一些截止,之后变量访问将会更加昂贵,那么您可以安排变量来处理它。
答案 6 :(得分:0)
它可以产生巨大的变化。我曾经指导PowerPC / Macintosh上的编译器将正确的局部变量放入寄存器中,并使应用程序的主要处理任务加速了2倍。该任务基本上是CPU绑定的,但通过使用寄存器消除了内存访问,提高了2倍的速度。在其他情况下,加速可能会更加激烈。
这是一个叶子功能。它没有调用其他功能。