为什么微软的开发人员选择将 .NET 打造为基于堆栈的机器?
今天我在VS2008提供的工具之间找到了Disassembler IL。我尝试反汇编一个程序并查看结果。操作码并不难理解,但有一件事让我感到惊讶:.NET 是基于堆栈的?!阅读“编写出色的代码,第二卷”时,我没有很好地了解基于堆栈的机器,因为它们非常慢。它们也很容易实现,但我认为 MS 开发人员选择这种方法并不是因为它的简单性,毕竟这些代码必须转换为真正的机器代码,这样他们才能解决问题。
你们谁能解释一下这个奇怪的选择吗?
PS:
我在这里发布我读到的有关该主题的内容:
13.1.1 基于堆栈的机器 基于堆栈的机器使用内存 大多数计算,使用堆栈 在内存中保存所有操作数和 结果。计算机系统采用 堆栈架构提供了一些 与其他相比的重要优势 架构:
不幸的是,堆栈 机器也遭受一些严重的 缺点:
- 指令通常更小(每个 消耗更少的字节)比那些 在其他架构中发现,因为 说明书上一般没有 指定任何操作数。
- 它 通常更容易编写编译器 对于堆栈架构比其他架构 机器,因为转换算术 堆栈序列的表达式 操作非常简单。
- 临时变量很少 堆栈架构中需要, 因为堆栈本身就是服务于此的 目的。
A 堆栈是一种数据结构,允许 仅在少数有限的情况下进行操作 堆栈的元素(通常称为 堆栈顶部和堆栈下一个)。 对于堆栈,您通常会执行以下操作之一 三件事:将新数据推送到 堆栈,从堆栈中弹出数据,或者 对当前数据进行操作 坐在堆栈的顶部(并且 可能是下面的数据 它)。
- 几乎所有 指令引用内存(其中 在现代机器上速度很慢)。尽管 缓存可以帮助缓解这个问题, 内存性能仍是主要 堆栈机器上的问题。
- 即使从 HLL 转换 到堆栈机很容易,有 优化的机会较少 比其他 架构。
- 因为堆栈 机器不断地访问 相同的数据元素(即数据 堆栈的顶部),流水线和 指令并行很难 实现(请参阅编写出色的代码, 第 1 卷有关管道的详细信息和 指令并行性)。
和
13.1.1.5 现实世界的堆栈机
堆栈的一大优势 架构的特点是它很容易 为这样的机器编写一个编译器。 写一个也很容易 基于堆栈的机器的模拟器。 由于这些原因,堆栈架构 在虚拟机 (VM) 中很流行 例如 Java 虚拟机和 Microsoft Visual Basic p 代码 口译员。一些现实世界的 基于堆栈的 CPU 确实存在,例如 Java的硬件实现 虚拟机;然而,它们并不是很受欢迎 由于性能限制 的内存访问。尽管如此, 了解堆栈的基础知识 架构很重要,因为许多 编译器翻译 HLL 源代码 之前转换为基于堆栈的形式 翻译为实际的机器代码。 事实上,在最坏的情况下(尽管 罕见),编译器被迫发出 模拟基于堆栈的代码 编译复杂时的机器 算术表达式。
编辑:我刚刚发现@EricLippert 博客中的一篇文章回答了问题并确认了 @Aaron 的答案
Today I've found the Disassembler IL between the tools provided with VS2008. I tried to disassemble a program and give a look to the result. Opcodes weren't so hard to understand but one thing surprised me: the .NET is stack based?! Reading "Write great code, volume II" I didn't get a good picture of stack based machines because they're quite slow. They're easy to implement, too but I don't think MS devs chose this approach because of its simplicity, after all that code has to be translated into real machine code so they would just move the problem.
Can any of you explain this strange choice?
PS:
I post here what I read about this topic:
13.1.1 Stack-Based Machines
Stack-based machines use memory for
most calculations, employing a stack
in memory to hold all operands and
results. Computer systems employing a
stack architecture offer some
important advantages over other
architectures:
- The
instructions are often smaller (each
consuming fewer bytes) than those
found in other architectures because
the instructions generally don’t have
to specify any operands.- It
is generally easier to write compilers
for stack architectures than for other
machines because converting arithmetic
expressions to a sequence of stack
operations is very easy.- Temporary variables are rarely
needed in a stack architecture,
because the stack itself serves that
purpose.Unfortunately, stack
machines also suffer from some serious
disadvantages:
- Almost every
instruction references memory (which
is slow on modern machines). Though
caches can help mitigate this problem,
memory performance is still a major
problem on stack machines.- Even though conversion from HLLs
to a stack machine is very easy, there
is less opportunity for optimization
than there is with other
architectures.- Because stack
machines are constantly accessing the
same data elements (that is, data on
the top of the stack), pipelining and
instruction parallelism is difficult
to achieve (see Write Great Code,
Volume 1 for details on pipelining and
instruction parallelism).A
stack is a data structure that allows
operations only on a few limited
elements of the stack (often called
the top of stack and next on stack).
With a stack you generally do one of
three things: push new data onto the
stack, pop data from the stack, or
operate on the data that is currently
sitting on the top of the stack (and
possibly the data immediately below
it).
and
13.1.1.5 Real-World Stack Machines
A big advantage of the stack
architecture is that it is easy to
write a compiler for such a machine.
It’s also very easy to write an
emulator for a stack-based machine.
For these reasons, stack architectures
are popular in virtual machines (VMs)
such as the Java Virtual Machine and
the Microsoft Visual Basic p-code
interpreter. A few real-world
stack-based CPUs do exist, such as a
hardware implementation of the Java
VM; however, they are not very popular
because of the performance limitations
of memory access. Nonetheless,
understanding the basics of a stack
architecture is important because many
compilers translate HLL source code
into a stack-based form prior to
translating to actual machine code.
Indeed, in the worst case (though
rare), compilers are forced to emit
code that emulates a stack-based
machine when compiling complex
arithmetic expressions.
EDIT: I've just found an article in @EricLippert's blog answering the question and confirming @Aaron's answer
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
请记住,仅仅因为中间表示是基于堆栈的,并不意味着生成的机器代码是基于堆栈的。当代码从中间形式转换为机器代码时,它基本上被重新编译 - 允许本地优化。
使用基于堆栈的中间表示的一个好处是,您不依赖于任何特定的体系结构。
想象一下,如果他们决定使用基于寄存器的理论系统作为中间形式。他们应该选择多少个寄存器? 8? 16? 64?如果您的目标处理器的实际寄存器多于中间形式,那么您就失去了可能的优化机会。如果您的目标的实际寄存器少于中间寄存器,那么您的优化会适得其反,因为这些寄存器无论如何都会刷新到内存中。
即使在当前的 CPU 上,编译为 x86 与 x64 也存在很大差异 - 更不用说替代架构 (ARM) 或未来的架构。
对于这样的事情,他们最好将其保持在最简单的形式,然后在最终代码生成期间依靠优化来将其与实际硬件相匹配。
Keep in mind that just because the intermediate representation is stack-based it doesn't mean the generated machine code is stack-based. As the code is converted from the intermediate form to machine code it's basically recompiled - allowing for local optimizations.
Once nice thing about using a stack-based intermediate representation is that you're not tied to any specific architecture.
Imagine if they had decided to use a theoretical register-based system as their intermediate form. How many registers should they pick? 8? 16? 64? If your target processor has more actual registers than the intermediate form then you've lost out on possible optimizations. If your target has less actual registers than the intermediate then your optimizations are counter-productive because those registers are flushed to memory anyway.
Even on current CPUs you've got a big difference compiling down to x86 vs x64 - not to mention alternate architectures (ARM) or future architectures.
For something like this it's good that they kept it in the simplest form and then rely on optimization during final code generation to match it to the actual hardware.
CIL 之所以基于堆栈,是因为它并不是被设计为针对 VM 的指令集。这是编译的中间阶段。
CLR 更像是一个编译器+运行时,而不是像 JVM 那样的虚拟机。 CLR 设计并不试图提供解释字节码的良好性能。相反,它尝试在运行时检查高级字节码并将其编译为机器代码。
The reason CIL is stack-based is because it was not designed to be an instruction set targeting a VM. It is an intermediate stage of compilation.
The CLR is more of a compiler+runtime rather than a VM like the JVM. The CLR design does not attempt to provide good performance of interpreted byte-code. Instead it attempts to check and compile a high-level byte-code to machine code at run-time.
你得问微软的开发人员。但我猜性能问题不是他们最关心的问题。大多数 Windows 应用程序不受 CPU 限制,甚至不受 I/O 限制,因为它们大部分时间都在等待用户单击按钮。然而,拥有一个允许他们轻松实现新语言的架构可能是一个优先事项。
You would have to ask the Microsoft developers. But I would guess that the performance issue was not their #1 concern. Most Windows applications are not CPU-limited, or even really I/O limited, as they spend the great majority of their time waiting for for the user to click on a button. Having an architecture that allowed them to implement new languages easily probably was a priority, however.