口译员:简化了多少?

发布于 2024-09-01 07:43:13 字数 374 浏览 6 评论 0原文

在我的解释器中,如下代码

x=(y+4)*z
echo x

解析并“优化”为解释器执行的四个单个操作,几乎类似于汇编:

add 4 to y
multiply <last operation result> with z
set x to <last operation result>
echo x
  • 在现代解释器(例如:CPython、Ruby、PHP)中,简化程度如何 解释器运行的“操作码”有多简化?

  • 当尝试使解释器的结构和命令更加复杂和高级时,我能否获得更好的性能?这肯定会困难得多,或者?

In my interpreter, code like the following

x=(y+4)*z
echo x

parses and "optimizes" down to four single operations performed by the interpreter, pretty much assembly-like:

add 4 to y
multiply <last operation result> with z
set x to <last operation result>
echo x
  • In modern interpreters (for example: CPython, Ruby, PHP), how simplified are the "opcodes" for which are in end-effect run by the interpreter?

  • Could I achieve better performance when trying to keep the structures and commands for the interpreter more complex and high-level? That would be surely a lot harder, or?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

第几種人 2024-09-08 07:43:13

在Python的情况下,你可以让它告诉你给定函数的字节码 dis 模块。

from dis import dis
def foo():
    x=(y+4)*z
    print x

dis(foo)

给你:

2           0 LOAD_GLOBAL              0 (y)
            3 LOAD_CONST               1 (4)
            6 BINARY_ADD          
            7 LOAD_GLOBAL              1 (z)
           10 BINARY_MULTIPLY     
           11 STORE_FAST               0 (x)

3          14 LOAD_FAST                0 (x)
           17 PRINT_ITEM          
           18 PRINT_NEWLINE       
           19 LOAD_CONST               0 (None)
           22 RETURN_VALUE        

其中一些是无关的(例如最后的 LOAD_CONST 和 RETURN_VALUE 用于 foo() 中隐式的 return None ),但 Python 似乎推送 y 和4 入栈,加,压入z,乘,然后写入x。然后按下 x 并打印

In Python's case, you can have it tell you the bytecode for a given function with the dis module.

from dis import dis
def foo():
    x=(y+4)*z
    print x

dis(foo)

gives you:

2           0 LOAD_GLOBAL              0 (y)
            3 LOAD_CONST               1 (4)
            6 BINARY_ADD          
            7 LOAD_GLOBAL              1 (z)
           10 BINARY_MULTIPLY     
           11 STORE_FAST               0 (x)

3          14 LOAD_FAST                0 (x)
           17 PRINT_ITEM          
           18 PRINT_NEWLINE       
           19 LOAD_CONST               0 (None)
           22 RETURN_VALUE        

Some of that is extraneous (e.g. the LOAD_CONST and RETURN_VALUE at the end are for the implicit return None in foo()), but Python appears to push y and 4 onto the stack, add, push z, multiply, and write to x. Then it pushes x and prints

宛菡 2024-09-08 07:43:13
  1. 关于最后的结果:实际上,您创建了一个带有一个寄存器的 注册机:“最后的操作结果” 。这是并行性的阻碍。
  2. Eval/assign 类型的操作码通常比您的操作码低一层。查看 Python 操作码
  3. 更高级别的命令可以产生更高的性能,因为它们可以让您在(希望)快速解释器中花费更多时间。但它们也可能很痛苦,因为您将需要另一个高级操作码来实现这个或那个。

尝试一下并看看:)这实际上取决于您未提供的许多因素(并且区域和任务是如此之大,如果您提供了足够的因素,它们将包含一些明显的答案)。其中一个主要因素是您是否(以及如何)实现某些语言功能(或者换句话说,您是否打算使这些功能成为一流),例如:

  • 结构化编程。我的意思是,操作码是否包含“函数调用”、“带有 N 个参数的函数调用”的概念,还是盲推、推、推、调用、ret 序列。
  • lambda(特别是函数体,在其他函数内定义)。这将做出闭包决策:函数是否应该“捕获”外部范围的变量如果是的话,如何进行。
  1. About last result: effectively, you created a register machine with one register: "last operation result". Which is a blocker for parallelism.
  2. Eval/assign kind of opcodes are usually a layer lower than, yours. Take a look at Python opcodes.
  3. Higher level commands could yield more performance, because they can allow you to spend more time inside (hopefully) fast interpreter. But they can also be a pain because you will need another high-level opcode for this and that.

Try it and see :) it really depends on lots of factors you didn't provide (and the area and task is so huge that if you provided enough factors, they would contain a few obvious answers). One of such major factors are if (and how) are you going to implement some language features (or, to put it in other words, if you are going to make these things first-class), for example:

  • structured programming. What i mean is, would opcodes contain a notion of "function call", "function call with N arguments" or will it be a blind push,push,push,call,ret sequence.
  • lambdas (particularily — function bodies, defined inside other functions). Which will put a closure decision: whether functions should "capture" variables of outside scope and, if yes, how.
遗忘曾经 2024-09-08 07:43:13

尝试对操作码进行建模,就像它们模仿解释器的内部工作原理一样。 此页面有一篇关于 .NET 如何从正则表达式生成解释语言的文章。在 .NET 中,正则表达式首先被编译为中间语言。然后该中间代码将被解释。中间代码看起来非常像特定的正则表达式引擎的内部数据结构。

Try modeling your opcodes like they would mimic the internal workings of your interpreter. This page has an article about how .NET generates an interpreted language out of regexes. In .NET the regex is first compiled to an intermediate language. Then that intermediate code will be interpreted. The intermediate code looks very much like the internal data structures of a specific, uhh, regex engine.

も让我眼熟你 2024-09-08 07:43:13

经验法则:如果字节码中存在重复模式(例如,每个 GC 控制的堆分配都有一个通用模式),则每个模式都应该有一个特殊的高级操作。

无论如何,如今,有了所有可用的 .NET、JVM、LLVM 东西,如果您真的对解释器的性能感兴趣,那么插入适当的 JIT 编译器确实非常便宜且容易。

A rule of thumb: if there are repeating patterns in your bytecode (e.g., a common pattern for every GC-controlled heap allocation), there should be a special high level operation for every pattern.

Any way, nowdays, with all that .NET, JVM, LLVM stuff available, it's really cheap and easy to plug in a proper JIT compiler, if you're really interested in a performance of your interpreter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文