【问题标题】:Does PyPy translate itself?PyPy 会自行翻译吗?
【发布时间】:2012-01-17 03:40:27
【问题描述】:

我说得对吗? PyPy 解释器是否真的解释自己然后翻译自己?

这是我目前的理解:

  • RPython 的工具链涉及部分执行要翻译的程序,以获得一种预处理版本来注释和翻译。
  • 在 CPython 之上运行的 PyPy 解释器执行部分解释自身,此时它将控制权交给执行翻译的 RPython 一半?

如果这是真的,那么这是我见过的最令人费解的事情之一。

【问题讨论】:

  • 如果你觉得这很令人费解,你应该看看 Lisp。
  • 等等,这是真的吗? RPython 不是一个单独的软件,而只是 PyPy 的一个子部分?没办法。
  • 你称之为“RPython”的东西是什么,你希望它成为一个软件? RPython 是一种编程语言,仅此而已。 PyPy 代码库包含该语言的编译器,尽管它大部分与完整 Python 的解释器分开(它不包含在解释器中,而是在分析的早期部分重用 Python 解释器) .
  • 所以 RPython 编译器重用部分 PyPy 代码库来对从 dis 收到的字节码执行抽象解释?

标签: python pypy rpython


【解决方案1】:

免责声明:我不是 PyPy 方面的专家 - 特别是,我不了解 RPython 翻译的细节,我只是引用我以前读过的东西。有关 RPython 翻译如何可能工作的更具体的帖子,请查看answer

答案是,是的,它可以(但只有在它第一次使用 CPython 编译之后)。

详细说明:

起初,这似乎是高度弯曲和自相矛盾的,但一旦你理解它,它就很容易了。在Wikipedia 上查看答案。

程序开发的引导始于 1950 年代,当时每个程序都是在纸上用十进制代码或二进制代码逐位(1 和 0)构建的,因为没有高级计算机语言,没有编译器,也没有汇编器,并且没有链接器。为一台新计算机(例如 IBM 650)手工编写了一个小型汇编程序,它将一些指令转换为二进制或十进制代码:A1。然后用刚刚定义的汇编语言重写了这个简单的汇编程序,但扩展了一些额外的助记符来处理更复杂的操作代码。

该过程称为软件引导。基本上,您构建一个工具,例如 C++ 编译器,使用已经制作的低级语言(在某一时刻,所有内容都必须从二进制编码),例如 ASM。现在您已经有了 C++,您现在可以用 C++ 编写 C++ 编译器,然后使用 ASM C++ 编译器来编译您的新编译器。编译完新编译器后,您现在可以使用它来编译自己。

因此,基本上,通过手工编码制作第一个计算机工具,使用该解释器制作另一个稍微更好的工具,并使用该解释器制作更好的工具,......最终您将获得今天所有的复杂软件! :)

另一个有趣的例子是CoffeeScript 语言,它是用... CoffeeScript 编写的。 (虽然这个用例仍然需要使用外部解释器,即 Node.js)

在 CPython 之上运行的 PyPy 解释器执行以部分解释自身,此时它将控制权交给执行翻译的 RPython 一半?

你可以使用已经编译好的 PyPy 解释器来编译 PyPy,或者你可以使用 CPython 来编译它。但是,由于 PyPy 现在有 JIT,使用它自己编译 PyPy 会比 CPython 更快。 (大多数情况下是PyPy is now faster than CPython

【讨论】:

  • 那么我在这里描述的是引导位,它发出一个完全编译的 PyPy,然后可以用来编译自己和后续版本?
  • 我不太了解 PyPy 或 RPython 的工作原理,但请在我的回答顶部的免责声明中查看可能有助于理解其中一部分的链接。我得到的简短部分是,Python 代码生成了一些东西,并使用 GCC 编译(非常密集的过程),但我知道使用带有 JIT 的 PyPy 会比 CPython 快得多。因此,您将需要比 PyPy 更多的依赖项。其中一些可以“自行编译”的程序实际上依赖于 GCC 进行编译(或其他系统,如 Node.js),但关于引导的过程通常仍然相同。
【解决方案2】:

PyPy 的翻译过程实际上在概念上没有听起来那么递归。

其实它只是一个处理 Python 函数/类/其他对象(不是 Python 源代码)并输出 C 代码的 Python 程序。但当然它不只处理 any Python 对象;它只能处理特定的形式,如果你用 RPython 编写你的待翻译代码,就会得到这些。

由于翻译工具链是一个 Python 程序,你可以在任何 Python 解释器上运行它,这显然包括 PyPy 的 Python 解释器。所以这没什么特别的。

由于它翻译 RPython 对象,您可以使用它来翻译 PyPy 的 Python 解释器,它是用 RPython 编写的。

但是你不能在翻译框架本身上运行它,它是不是 RPython。只有 PyPy 的 Python 解释器本身是 RPython。

事情只会变得有趣,因为 RPython 代码也是 Python 代码(但不是相反),并且因为 RPython 从来没有“真正存在”在源文件中,而只存在于一个工作 Python 进程内的内存中,该进程必然包含其他非-RPython 代码(例如,没有“纯 RPython”导入或函数定义,因为翻译器对已经已定义和导入的函数进行操作)。

请记住,翻译工具链在内存中的 Python 代码对象上运行。 Python 的执行模型意味着这些在某些 Python 代码运行之前并不存在。你可以想象开始翻译过程看起来有点像这样,如果你高度简化它:

from my_interpreter import main
from pypy import translate

translate(main)

众所周知,仅导入 main 将运行大量 Python 代码,包括所有其他模块 my_interpreter 导入。但是翻译过程开始分析函数对象 main;它永远不会看到,也不关心任何代码被执行以产生main

一种理解方式是“用 RPython 编程”意味着“编写一个 Python 程序,该程序生成一个 RPython 程序,然后将其提供给翻译过程”。这相对容易理解,并且类似于许多其他编译器的工作方式(例如,考虑用 C 编程的一种方式是,您实际上是在编写一个 C 预处理器程序,该程序生成一个 C 程序,然后将其馈送到C 编译器)。

在 PyPy 的情况下,事情只会变得令人困惑,因为所有 3 个组件(生成 RPython 程序的 Python 程序、RPython 程序和翻译过程)都被加载到同一个 Python 解释器中。这意味着很有可能在使用某些参数调用而不是在使用其他参数调用时具有 RPython 函数,从翻译框架调用帮助函数作为生成 RPython 程序的一部分,以及许多其他奇怪的事情。所以情况变得相当模糊,你不一定能将你的源代码行清晰地划分为“要翻译的 RPython”、“Python 生成我的 RPython 程序”和“将 RPython 程序交给翻译框架”。


PyPy 解释器,在 CPython 之上运行,部分执行 解释自己

我认为您在这里暗示的是 PyPy 在翻译过程中使用the flow object space 来进行抽象解释。即使这并不像起初看起来那样疯狂和令人费解。我对 PyPy 的这一部分知之甚少,但据我所知:

PyPy 通过将 Python 解释器的所有操作委托给“对象空间”来实现它们,该“对象空间”包含所有基本内置操作的实现。但是你可以插入不同的对象空间来获得不同的效果,只要它们实现相同的“对象空间”接口,解释器仍然能够“执行”Python代码。

PyPy 翻译工具链处理的 RPython 代码对象是可以由解释器执行的 Python 代码。因此,PyPy 通过插入流对象空间来重新使用其 Python 解释器的一部分作为翻译工具链的一部分。当用这个对象空间“执行”代码时,解释器实际上并不执行代码的操作,而是产生流程图,类似于许多其他编译器使用的那种中间表示;它只是代码的简单机器可操作表示,需要进一步处理。这就是常规 (R)Python 代码对象如何转化为其余翻译过程的输入的方式。

由于通常用翻译过程翻译的东西是 PyPy 的 Python 解释器,它确实用流对象空间“解释自己”。但这真正意味着你有一个 Python 程序正在处理 Python 函数,包括那些进行处理的函数。就其本身而言,它并不比对自身应用装饰器或让包装类包装自身的实例(或包装类本身)更令人费解。


嗯,这有点漫不经心。无论如何,我希望它有所帮助,并且我希望我没有说任何不准确的话;如果有请指正。

【讨论】:

  • 听完前两句话,我的大脑就自动关闭了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-12
  • 1970-01-01
  • 2019-06-14
  • 1970-01-01
  • 2020-02-15
  • 1970-01-01
  • 2018-10-10
相关资源
最近更新 更多