计算机如何只使用少量寄存器?

14

我对汇编语言有一些了解,知道计算机通常只有4或8个通用寄存器。那么在多线程等情况下,所有计算机程序都是如何仅凭这么少的寄存器运作的呢?


3
我喜欢把寄存器看作是处理器的本地变量。我通常不会在当前函数范围内使用大量变量。它们中的大多数都存在于内存中的程序的其他部分。 - Merlyn Morgan-Graham
@Merlyn:没错,但可能有点误导,因为在没有编译器优化的情况下,局部变量通常存储在堆栈上。 - bcat
@bcat:是的,这个概念并不完全适用。不同的架构也会有不同的行为(虽然这可能被解释为编译器优化)。 - Merlyn Morgan-Graham
@bcat:我发现最有用的心理模型是本地变量=可以保存在寄存器中的值,如果寄存器不足,可以将它们溢出到堆栈中。在调试模式下编译始终会为了一致的调试而编译所有内容。(你说得对,C语言模型除了“register”变量之外,每个对象都有一个地址,但实际上在汇编中编程,我建议的方法才是正确的起点。) - Peter Cordes
7个回答

11
多线程本身不会影响使用的寄存器数量。当一个线程被换出时,它通常会将其寄存器保存到内存中,下一个要运行的线程会从先前的保存中加载这些寄存器。
一个例子是系统具有线程控制块结构(TCB)。当线程未运行时,此结构将包含已保存的指令指针、堆栈指针、通用寄存器、浮点寄存器、线程统计信息等内容。简而言之,一切需要完全恢复线程状态的东西都在其中,以便在为其他线程运行而交换出线程时进行恢复。
并非计算机中的所有操作都在寄存器中完成。现代编译器可以优化代码,使得最常用的数据项保留在寄存器中,但绝大部分数据都存储在内存中,只有在需要时才会进入寄存器。
我曾经阅读过的关于这个主题最好的书是Tanenbaum的 "Structured Computer Organization",它从数字逻辑层面到操作系统层面考虑了计算机的各个层次,并且每个层次都建立在上一个层次的基础之上。

           alt text

顺便说一下:我的梦想是有一天能写一本像这样从夸克层面到Emacs覆盖所有内容的书 :-)


+1 给 Tanenbaum,不是因为他是 Tanenbaum,而是因为那本书真的很棒。 - bcat
在这个行业中,我认为像Tanenbaum这样的人很少(Knuth是我马上想到的唯一一个)。他们都通过他们丰富的书籍和软件做出了巨大的贡献。我仍然拥有我在大学时期得到的第二版这本书(_很久_以前)。 - paxdiablo
哦,我完全同意。我只是想说我喜欢这本书是因为它的内在价值,而不仅仅是因为作者是计算机科学界非常重要的人物(Tanenbaum 确实如此)。是的,我也在大学里读过这本书,我很高兴我的教授选择了它而不是另一本计算机系统书籍。我仍然对它如何以清晰明了的方式呈现材料而不降低它的难度感到惊讶。 - bcat
1
这本书的封面看起来像是Richard Scarry的作品。但仅凭外表,我不确定它是......等等......有人告诉我不应该这么做...... - Dave Markle

5
其他变量和线程堆栈通常存储在受保护的内存空间中,需要时可以调用它们到寄存器中。
你可能想看一下 The Elements of Computing Systems 这本书,以更好地理解计算机CPU的工作原理。该书设置为一系列项目,您可以从一个NAND门到CPU、汇编器、简单编译器,再到一个小型操作系统。它是理解计算机所有部件如何组合在一起的无价之宝。

2
每次线程(或进程)交换时,操作系统内核会将所有寄存器推入堆栈中,放入一个通常称为“进程控制块”的数据结构中。然后,当线程/进程再次交换回来时,寄存器数据将从PCB中读取并从堆栈中弹出到寄存器中。
x86还有一些内部寄存器和映射表,这些内部元素构成了虚拟寄存器表,以保留IA32指令集架构,同时具有更大的灵活性来设计超标量体系结构和复杂的指令调度算法。
此外,指令集通常具有加载存储指令,与指向内存的指针配合使用,允许将数据从寄存器存储到内存中。这就是“Load-Store机器”的来源,即没有直接在内存上运行指令的计算机。
一些计算机确实具有直接在内存上运行指令的功能,一些是基于堆栈的。这取决于硬件设计人员和所施加的约束条件。

1

你必须认识到,即使是简单的事情也需要执行数千到数百万条汇编指令。这些寄存器的值经常被交换。


1

计算机可以用非常少的寄存器完成所有操作,这实际上非常有趣。

在汇编语言层面上,通过巧妙的编译器实现了高效利用很少的寄存器,真的很聪明。

如果一个问题使用提供的寄存器无法完成,程序通常会将其寄存器“溢出”到主内存堆栈中。通过记住我们放置溢出寄存器的堆栈位置,我们可以轻松地检索它们。

当我们用尽所需的寄存器时,我们只需将它们存储在堆栈上,这比大多数人需要的程序要提供更多的空间。

在多线程的特定情况下,我们只需将所有寄存器保存到内存中,然后为其他线程提供一个干净的状态。


0

这是计算机的另一个存储器,特别是RAM所用的一种方式:将数据位保存和恢复到寄存器中。

当线程被切换以便运行另一个线程时,第一个线程的寄存器状态会被保存在某个地方(堆栈或其他数据结构上),并且第二个线程的寄存器状态会从之前保存的位置恢复。RAM足够快,可以在一秒钟内发生数千次这样的切换,但如果你不必要地交换线程,它可能会影响性能。

另一个非常常见的情况是局部变量 - 如果局部变量使用的时间足够短,它可能永远不存在于寄存器之外。然而,在许多情况下,局部变量可能需要从寄存器保存到内存位置,以便可以加载和操作其他值。实际上,对于几乎任何变量都是如此,不仅仅是局部变量(但局部变量更有可能从未存在于内存中)。


0

这是一个相当复杂的问题,答案取决于您的CPU架构。

在旧时代,你是正确的 - 确实只有一些通用寄存器。现在,CPU和编译器通过像寄存器重命名这样的技术来与你的通用寄存器玩 "三张牌蒙特卡罗" 的游戏。

虽然在简单的架构上,当发生上下文切换时,寄存器确实会被复制到[缓存]内存中,但SMT之类的技术可以“欺骗”操作系统,让它认为有更多的核心。

但是对于你的问题最普遍的答案是数据会频繁地被移入和移出寄存器。这就是为什么在任何给定的汇编程序中,你会看到大量的“MOV”指令。CPU和编译器设计师花费了大量时间和金钱来优化他们的设计,以便你不需要将数据从主内存(速度较慢)移动到寄存器中,他们尽可能地保持数据缓存。这种大量的“MOV”指令是为什么内存延迟和总线速度对整个计算机性能如此重要的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接