常见问题解答¶
目录
- 常见问题解答
- 什么是 RPython?
- RPython 能否将普通的 Python 程序编译成 C?
- 什么是 RPython 语言?
- RPython 与 Zope 的 Restricted Python 有什么关系?
- 在某些文档字符串中看到的
"NOT_RPYTHON"
是什么意思? - 我们能否简单地获取 Python 语法树并将其转换为 Lisp?
- 我是否必须将我的程序重写为 RPython?
- RPython 工具链有哪些后端?
- 我们可以使用 LLVM 吗?
- 编译 PyPy 时交换或内存不足
- 如何编译我自己的解释器?
- PyPy 的 RPython 模块可以独立翻译吗?
- 为什么翻译器在翻译时会绘制曼德勃罗集分形?
另请参见: PyPy 的常见问题解答。
什么是 RPython?¶
RPython 是一个用于实现编程语言(尤其是动态语言)的解释器和虚拟机的框架。
RPython 能否将普通的 Python 程序编译成 C?¶
不能,RPython 不是 Python 编译器。
在 Python 中,通过静态分析来证明程序将操作的类型几乎是不可能的。如果您熟悉 Python,这一点应该很清楚,但如有疑问,请参阅 [BRETT]。
如果您想要一个快速的 Python 程序,请改用 PyPy 的 JIT。
[BRETT] | Brett Cannon,Python 中原子类型的局部类型推断,http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.90.3231 |
什么是 RPython 语言?¶
RPython 是 Python 语言的一个受限子集。它用于在 PyPy 工具链中实现动态语言解释器。这些限制确保了 RPython 程序的类型推断(以及最终将其转换为其他语言)成为可能。
“是 RPython”的属性始终适用于整个程序,而不是单个函数或模块(翻译工具链会进行完整的程序分析)。翻译工具链会递归地跟踪所有调用,并发现哪些属于程序,哪些不属于程序。
RPython 程序限制主要限制了以任意方式混合类型的能力。RPython 不允许在同一个变量中绑定两个不同的类型。在这方面(以及其他一些方面),它有点像 Java。RPython 中不允许的其他特性包括使用特殊方法(__xxx__
),除了 __init__
和 __del__
,以及使用反射功能(例如 __dict__
)。
您不能从 RPython 中使用大多数现有的标准库模块。例外情况是 os
、math
和 time
中的一些具有原生支持的函数。
要详细了解 RPython 的限制,请阅读 RPython 描述。
RPython 与 Zope 的 Restricted Python 有什么关系?¶
没有关系。Zope 的 RestrictedPython 旨在为 CPython 提供一个沙盒执行环境。PyPy 的 RPython 是动态语言解释器的实现语言。但是,PyPy 还提供了一个强大的 沙盒 Python 解释器。
在某些文档字符串中看到的 "NOT_RPYTHON"
是什么意思?¶
如果将“NOT_RPYTHON”放入函数的文档字符串中,并且在尝试翻译 RPython 程序时找到了该函数,则翻译过程将停止并将其报告为错误。因此,您可以将函数标记为“NOT_RPYTHON”以确保它们永远不会被分析。
这种将函数标记为非 RPython 的方法已经过时。对于新代码,请改用 @objectmodel.not_rpython
装饰器。
我们能否简单地获取 Python 语法树并将其转换为 Lisp?¶
这并非完全没有道理,但这不是 PyPy 的方式。如果没有某种类型的推断,将这段 Python 代码
a + b
转换为比这段 Common Lisp 代码更有效的任何东西都非常困难
(py:add a b)
而使类型推断成为可能正是 RPython 的目标。
您可以将 #'py:add
设为一个泛型函数,并查看给定的 CLOS 实现是否足够快以提供有用的速度(但我认为强制转换规则可能会首先让您抓狂)。 – mwh
我是否必须将我的程序重写为 RPython?¶
不需要,而且您也不应该尝试。首先,RPython 是一种专为编写解释器而设计的语言。它是 Python 的受限子集。如果您的程序不是解释器,而是试图做“实际的事情”,例如使用标准 Python 库的任何部分或任何第三方库,那么它从一开始就不是 RPython。只有当您尝试 编写自己的解释器 时,才应该考虑 RPython。
如果您的目标是加速 Python 代码,那么请查看普通的 PyPy,它是一个完整且符合 Python 2.7 标准的解释器(碰巧是用 RPython 编写的)。不仅没有必要将您的代码重写为 RPython,即使您成功地重写了,它也可能不会带来任何速度提升。
是的,如果付出足够多的努力,可以编译执行少量性能敏感操作的小型自包含 RPython 代码片段。但这种情况对我们来说并不有趣。如果您需要将代码重写为 RPython,您也可以将其重写为 C 或 C++ 或 Java 等语言。这些语言得到了更多支持,文档也更加完善:-)
以上段落并非全部真相。确实存在一些情况,其中将程序编写为 RPython 比在 PyPy 上运行它能获得更高的速度。但是,PyPy 背后的核心人员的态度是回答:“那么将其作为 PyPy 的性能错误报告!”。
以下是一种更委婉的说法。“不,不要!”以上是一般性警告,我们提供给新手。他们很可能需要来自某些来源的大量帮助,因为 RPython 并不简单,也没有广泛的文档;但与此同时,我们,PyPy 的核心人员,不愿意投入时间来支持执行与动态语言解释器截然不同的事情的第三方项目——仅仅因为我们有其他兴趣,而且每天的时间是有限的。因此,作为总结,我认为公平的做法是将新手引导到现有的替代方案,这些方案更主流,并且他们可以从许多人那里获得帮助。
如果有人真的想推广 RPython,他们非常欢迎:我们不会积极抵制这样的计划。有很多事情可以做,例如将 RPython 打造成为一个更好的类似 Java 的语言,从支持非 GIL 的多线程开始,但我们没有实现它们,因为它们与我们关系不大。这是开源的,这意味着任何人都可以自由地推广和开发任何东西;但这也意味着您必须让我们选择不自己朝那个方向发展。
我们可以使用 LLVM 吗?¶
理论上可以。但我们已经尝试过 5 或 6 次了,将其用作翻译后端或 JIT 后端——但每次都失败了。
更详细地说:如今使用 LLVM 作为(静态)翻译后端毫无意义,因为您可以生成 C 代码并使用 clang 编译它。(请注意,使用 clang 编译 PyPy 的结果并不比使用 gcc 编译它快。)理论上,我们可能会从 LLVM 的 GC 集成中获得额外的好处,但这需要在 LLVM 端进行更多工作,然后再变得有用。无论如何,它可以通过 C 代码中的自定义原语进行接口。(最新的此类实验性后端位于 llvm-translation-backend
分支中,它可以在 Linux 上翻译带有或不带有 JIT 的 PyPy。)
另一方面,使用 LLVM 作为我们的 JIT 后端看起来也很有趣——但我们也尝试过,并且失败了:LLVM 无法修补生成的机器代码,并且完全不适合跟踪 JIT。甚至一个尝试使用 LLVM 的大型方法 JIT 也放弃了,原因类似;阅读该博文以获取更多详细信息。
因此,PyPy 核心开发人员的立场是,如果有人想用 LLVM 进行第 N+1 次尝试,他们非常欢迎,并且会在 IRC 频道上获得一些帮助,但他们需要承担证明其有效性的责任。
编译 PyPy 时交换或内存不足¶
这已在文档中说明(此处 和 此处)。它需要 4 GB 的 RAM 在 PyPy 之上运行“rpython targetpypystandalone”,在 CPython 之上运行时需要更多一些。如果您有不到 4 GB 的可用内存,它将永远交换(或者如果没有足够的交换空间则会失败)。我们指的是可用内存:如果机器总共只有 4 GB,那么它就会交换。
在 32 位系统上,将数字除以二。(我们最近没有尝试过,但在过去,可以在没有任何其他程序运行的 2 GB Linux 机器上编译 32 位版本:例如,没有 Gnome/KDE。)
PyPy 的 RPython 模块可以独立翻译吗?¶
不可以,您必须重建整个解释器。这意味着两件事
- 必须使用测试驱动开发。在尝试翻译之前,您必须在纯 Python 中对您的模块进行详尽的测试。翻译完成后,您应该只剩下一些类型问题需要解决,但其他方面结果应该可以直接使用。
- 其次,也许是最重要的:您是否有充分的理由一开始就用 RPython 编写模块?如今,您应该考虑其他方案,例如用纯 Python 编写,如果需要调用 C 代码,可以使用cffi。
在这种情况下,能够独立于翻译完整解释器来翻译 RPython 模块并不那么重要。(如果付出足够努力,是可以做到的,但这确实是一项非常艰巨的任务。目前可以认为不太可能。)
为什么翻译器在翻译时会绘制曼德勃罗集分形?¶
因为它很有趣。