PyPy -- 它如何可能击败CPython?

283

来自Google 开源博客

PyPy是Python的重新实现版本,使用先进技术,旨在比CPython具有更好的性能。多年的辛勤工作终于得到了回报。我们的速度结果通常击败CPython,从稍微慢一点,到实际应用代码加速2倍,甚至在小基准测试中加速高达10倍。

这怎么可能呢?是哪个Python实现版本用来实现PyPy的?CPython?那么,有没有机会让PyPyPy或PyPyPyPy超越它们的分数呢?

(另外...为什么会有人尝试这样做呢?)


45
吹毛求疵:PyPy确实就是PyPyPy。将Py-*前缀视为投影运算符。 - u0b34a0f6ae
好的,那么PyPy比CPython更受推荐吗?它有什么缺点吗? - balki
11
PyPy在运行时优化方面表现出色,但它不同的内部结构使其与一些流行的C语言扩展不兼容 - Cees Timmerman
5
几乎每个人都没有关注到一个理论上如何实现速度提升的问题。但是想一想:Python 可以做任何事情,就像图灵机一样。毕竟它可以调用 gcc。因此,你也可以编写一些在 CPython 上运行的 Python 代码,解释其他一些 Python 代码,将其转换为 C 代码,并执行 gcc,然后执行编译后的程序。如果代码被频繁调用,它可能会更快。 - Sergey Orshanskiy
4个回答

316

"PyPy是Python在Python中的重新实现"这样描述PyPy是有点误导人,但从技术上讲确实如此。

PyPy有两个主要部分:

  1. 翻译框架
  2. 解释器

翻译框架是一个编译器。它将RPython代码编译成C(或其他目标),自动添加垃圾收集和JIT编译器等方面的内容。它不能处理任意的Python代码,只能处理RPython。

RPython是普通Python的一个子集;所有的RPython代码都是Python代码,但反过来不一定成立。没有关于RPython的正式定义,因为RPython基本上只是“可以由PyPy的翻译框架翻译的Python子集”。但是,为了能够进行翻译,RPython代码必须是静态类型的(类型是推断出来的,你不需要声明它们,但变量只能有一种类型),而且你也不能在运行时声明/修改函数/类等。

解释器是一个用RPython编写的普通Python解释器。

由于RPython代码是普通的Python代码,所以你可以在任何Python解释器上运行它。但是,PyPy的速度优势并不来自这种方式运行它;这只是为了进行快速测试循环,因为翻译解释器需要很长的时间。

了解了这些,就应该立即清楚关于PyPyPy或PyPyPyPy的猜测实际上没有任何意义。你有一个用RPython编写的解释器。你将其翻译成执行Python的C代码。这个过程到此为止;没有更多的RPython可以通过再次处理来加速。

因此,“PyPy如何比CPython更快”也变得相当明显。 PyPy有一个更好的实现,包括JIT编译器(我相信如果没有JIT编译器,它通常不会比较快,这意味着对于能够进行JIT编译的程序,PyPy才会更快)。CPython从未被设计为Python语言的高度优化实现(尽管他们确实试图使其成为高度优化的实现,如果你能理解这个区别的话)。

PyPy项目的真正创新之处在于,他们不手写复杂的 GC 方案或 JIT 编译器,而是使用 RPython 相对简单地编写解释器。虽然 RPython 比 Python 更低级,但它仍然是一种面向对象的垃圾回收语言,比 C 更高级。然后,翻译框架会自动添加 GC 和 JIT 等功能。因此,翻译框架是一个非常大的工作量,但它同样适用于 PyPy Python 解释器的任何实现方式,允许更自由地进行性能改进实验(无需担心引入 GC 错误或更新 JIT 编译器以适应变化)。这也意味着,当他们开始实现 Python3 解释器时,它将自动获得相同的优势。同时,所有使用 PyPy 框架编写的解释器(其中有许多处于不同阶段的完善)以及使用 PyPy 框架的解释器都自动支持框架支持的所有平台。
因此,PyPy项目的真正好处在于尽可能地分离出为实现动态语言的高效、跨平台解释器所需的所有部分。然后,在一个地方提供一个良好的实现,可以在许多解释器中重复使用。这不像“我的 Python 程序现在运行更快”那样立竿见影,但对于未来来说是一个很好的前景。
并且它可能会使您的 Python 程序运行更快。

9
我没能理解其中的区别。 - polvoazul
47
"优化语言实现"和"优化型"的区别?当我说CPython是一种良好优化的实现时,我指的是开发人员尝试使解释器内部算法和内置数据结构运行高效。相反,"优化型"实现会分析最终用户的代码,并尝试找出将其转换为更高效执行的方法。 - Ben

166

Q1. 这是如何实现的?

手动内存管理(也就是CPython使用计数方法)在某些情况下可能比自动管理更慢。

CPython解释器实现上的限制阻碍了PyPy可以做的某些优化(例如细粒度锁定)。

像Marcelo所说的,这是通过JIT实现的。能够即时确认对象类型可以节省您不必要的多次指针反引用,最终到达您想要调用的方法。

Q2. PyPy使用了哪个Python实现进行实现?

PyPy解释器是用RPython实现的,它是Python的静态类型子集(语言而不是CPython解释器)。-有关详细信息,请参见https://pypy.readthedocs.org/en/latest/architecture.html

Q3. PyPyPy或PyPyPyPy有打败其得分的机会吗?

这将取决于这些假设解释器的实现方式。如果其中一个解释器例如获取源代码,对其进行某种分析,并在一段时间后直接将其转换为紧密的目标特定汇编代码,我认为它将比CPython更快。

更新:最近,在精心制作的示例中,PyPy的性能超过了类似C程序使用gcc -O3编译的程序。这是一个人为的情况,但确实展示了一些想法。

Q4. 为什么有人会尝试这样的事情?

来自官方网站。 https://pypy.readthedocs.org/en/latest/architecture.html#mission-statement

  

我们的目标是提供:

     
  • 提供通用的翻译和支持框架,用于生成动态语言的实现,强调语言规范和实现方面之间的清晰分离。我们称之为RPython工具链_。

  • 一种符合标准、灵活且快速的Python_语言实现,它使用上述工具链,以便在不编码低级细节的情况下启用新的高级特性。

  • 通过这种方式分离关注点,我们对Python和其他动态语言的实现能够自动生成任何动态语言的即时编译器。它还允许在实现决策方面进行混合和匹配,包括许多历史上超出用户控制范围的决策,例如目标平台、内存和线程模型、垃圾回收策略以及应用的优化,包括是否首先使用JIT。

    像C编译器gcc一样是用C实现的,Haskell编译器GHC是用Haskell编写的。你有没有理由不用Python编写解释器/编译器呢?


93
这个回答完全缺少解释PyPy快的主要原因;虽然它提到了PyPy不是真正用Python实现的,而是用RPython实现的,但它没有指出RPython代码是静态编译和优化以生成PyPy解释器(它也恰好是可以在CPython上运行得更慢的有效Python代码)。他们在“普通Python”中实现的是RPython“编译器”(块引用中提到的翻译框架)。 - Ben
12
这是深埋了主要内容。大部分性能来源于将代码翻译为C语言(这使得解释器的速度与CPython相差不大),以及JIT技术,它可以显著加快热点路径的执行速度。 - Tobu
5
更新:最近,PyPy在仔细设计的示例上表现优于使用gcc -O3编译的类似C程序。如果你阅读该帖子下的第一条评论,会发现该帖子作者并不了解链接时优化。启用链接时优化后,C代码运行速度更快。 - Ali
3
好的,这篇博客文章发布于2011年,而这个回答是在2014年。此外,评论中提到了共享库。我不知道这些(回答和博客文章)有多少仍然有效。过去几年中,所有涉及的技术都发生了很大变化。 - Noufal Ibrahim
1
在两个精心设计的Pypy比等效C更快的例子中,每个基准测试之所以更快,是因为有非常特定的原因。第一个原因是因为Pypy足够聪明,能够意识到紧密循环计数的事情从未被使用过,因此可以完全删除它(JIT pass)。第二个原因是由于Pypy JIT可以“跨库边界内联”,例如“printf”函数被专门化为只能发出整数,并消除了重复的malloc(内存分配开销)。 - amcgregor

24

PyPy是用Python实现的,但它使用JIT编译器即时生成本机代码。

实现PyPy在Python之上的原因可能是Python是一种非常高效的语言,特别是因为JIT编译器使得宿主语言的性能有些无关紧要。


JIT 是否生成与 PyPy 同级别运行的 Python 代码,还是生成在 PyPy 运行所在的 Python 实现层次上运行的真正本机代码? - Edmund
3
真正的本地代码(参见这里);确切地说,是32位x86代码。 - Marcelo Cantos

12

PyPy使用Restricted Python编写,据我所知,它并不运行在CPython解释器之上。Restricted Python是Python语言的一个子集。据我所知,PyPy解释器被编译为机器码,因此安装后在运行时不需要利用Python解释器。

你的问题似乎期望PyPy解释器在执行代码时运行在CPython之上。 编辑:是的,要使用PyPy,您需要首先将PyPy Python代码翻译为C并使用gcc构建,或者翻译为jvm字节码,或者翻译为.Net CLI代码。请参见入门指南


11
PyPy可以在CPython的基础上运行,但在这种模式下它并不能提供人们期望的速度优势。 :-) http://codespeak.net/pypy/dist/pypy/doc/getting-started-python.html#id9 - Frank V

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