如果 PyPy 快 6.3 倍,为啥我不应该使用 PyPy 而不是 CPython?

Posted

技术标签:

【中文标题】如果 PyPy 快 6.3 倍,为啥我不应该使用 PyPy 而不是 CPython?【英文标题】:Why shouldn't I use PyPy over CPython if PyPy is 6.3 times faster?如果 PyPy 快 6.3 倍,为什么我不应该使用 PyPy 而不是 CPython? 【发布时间】:2013-09-27 14:33:04 【问题描述】:

我听说过很多关于PyPy 项目的消息。他们声称它比 their site 上的 CPython 解释器快 6.3 倍。

每当我们谈论 Python 等动态语言时,速度都是最重要的问题之一。为了解决这个问题,他们说 PyPy 的速度提高了 6.3 倍。

第二个问题是并行性,臭名昭著的Global Interpreter Lock (GIL)。为此,PyPy 表示can give GIL-less Python。

如果 PyPy 能够解决这些巨大的挑战,那么阻碍更广泛采用的缺点是什么?也就是说,是什么阻止了像我这样的典型 Python 开发人员现在切换到 PyPy?

【问题讨论】:

清除了 cmets,因为大多数东西要么应该在答案中充实(在某些情况下是),要么根本不应该说。还进行了编辑以解决对该问题的主观性提出的一些担忧。 请尽量用事实来回答,如果可能的话,请用来源支持断言! 我一直在使用 Pypy。它往往工作得很好。然而,虽然 Pypy 对于许多 CPU 繁重的工作负载来说要快得多,但对于我所投入的 I/O 繁重的工作负载来说,它实际上要慢一些。例如,我编写了一个名为 backshift 的重复数据删除备份程序。对于执行大量文件分块的初始备份,pypy 非常棒。但是对于大部分只是更新时间戳的后续备份,CPython 更快。 【参考方案1】:

支持的 Python 版本

引用Zen of Python:

可读性很重要。

例如,Python 3.8 引入了fstring =。

Python 3.8+ 中可能还有其他对您更重要的功能。 PyPy 目前不支持 Python 3.8+。

无耻的自我宣传:Killer Features by Python version - 如果你想通过使用旧版 Python 了解更多你错过的东西

【讨论】:

但是对于相同的 Python 版本,PyPyCPython 快 - 我可以理解使用 python3.7 和 3.8 并获得更多好处,但如果对于某些项目,我可以在旁边使用PyPy,以绕过 GIL 并在面向 CPU 的进程中进行更快的并行处理 截至今天 9/OCT/21,PyPy 支持或兼容 python3.7,现在团队正在努力支持 python3.8。参考pypy.org/posts/2021/04/… @GhassanMaslamani 谢谢!我已经更新了:-) @aspiring PyPy 有一个 GIL。【参考方案2】:

注意: 与 2013 年提出这个问题时相比,PyPy 现在更加成熟并且得到了更好的支持。避免从过时的信息中得出结论。


    正如其他人很快提到的,PyPy 对 C 扩展的支持很薄弱。它有支持,但通常速度比 Python 慢,而且充其量也很不稳定。因此,很多模块只需要 CPython。 PyPy 不支持 numpy。仍然不支持某些扩展(PandasSciPy 等),请在进行更改之前查看the list of supported packages。 请注意,现在支持列表中标记为不支持的许多软件包。 Python 3 支持目前处于试验阶段。 刚刚达到稳定!截至 2014 年 6 月 20 日,PyPy3 2.3.1 - Fulcrum is out! PyPy 有时对于很多人使用 Python 的“脚本”来说并不快。这些是做一些简单而小的事情的短期程序。因为 PyPy 是一个 JIT 编译器,它的主要优势来自于长时间的运行和简单的类型(例如数字)。与 CPython 相比,PyPy 的 pre-JIT 速度可能很差。 惯性。迁移到 PyPy 通常需要重新组装,这对于某些人和组织来说工作量太大了。

我想说,这些是影响我的主要原因。

【讨论】:

很高兴您提到了重组。例如,我的虚拟主机可以在 Python 2.4 和 2.5 之间进行选择;我附近的“娱乐软件主要生产商”正在使用 2.6,并且没有计划很快升级。有时,甚至发现转化成本可能是一项重大而昂贵的工作。 PyPy “与 C 一样快”更多的是关于通用 C,而不是用于数字的高度优化的多线程缓存感知 C 库。对于数字,Python 仅用于传递指向大数组的指针。因此,PyPy “与 C 一样快”意味着“您的指针+元数据的移动速度与 C 一样快”。没什么大不了的。那为什么还要用 Python 呢?去看看 cblas 和 lapacke 中的函数签名。 @cjordan1:我不明白你在说什么。高级 numpy 构造在 Python 中极具表现力(np.sum(M[1:2*n**2:2, :2*n**2] * M[:2*n**2:2, :2*n**2].conjugate(), axis=1)?),这使得 Python 非常适合科学界。此外,在 Python 中执行非密集部分并为较小的密集循环使用 C 语言是一种常见且可用的策略。 @Veedrac 这就是我的意思。就像“去看看 cblas 和 lapacke 中的函数签名”一样,因为它们太长且难以使用,以至于您会立即理解为什么我们使用 Python 来传递指针和元数据。 @tommy.carstensen 这不是深入研究的好地方,但我会尝试。 1. 这在我写它的时候比现在更真实。 2. “脚本”通常是 IO 繁重的。 PyPy 的 IO 仍然通常比 CPython 慢 - 它曾经明显慢得多。 3. PyPy 过去在处理字符串方面比 CPython 慢 - 现在它通常更好,很少更差。 4. 许多“脚本”只是粘合代码 - 在这种情况下,让解释器更快不会改善整体运行时间。 5. PyPy 的预热时间过去更长 - 运行时间短的脚本很少能产生大量热代码。【参考方案3】:

PyPy 支持 Python 3 已经有一段时间了,但根据HackerNoon post by Anthony Shaw from April 2nd, 2018,PyPy3 仍然比 PyPy(Python 2)慢几倍。

对于许多科学计算,尤其是矩阵计算,numpy 是更好的选择(参见FAQ: Should I install numpy or numpypy?)。

Pypy does not support gmpy2. 你可以改用gmpy_cffi 虽然我还没有测试过它的速度,而且该项目在 2014 年发布了一个版本。

对于 Project Euler 问题,我经常使用 PyPy,对于简单的数值计算,from __future__ import division 对我的目的来说已经足够了,但是截至 2018 年,Python 3 的支持仍在进行中,最好的选择是 64位Linux。截至 2018 年 12 月的最新版本 Windows PyPy3.5 v6.0 处于测试阶段。

【讨论】:

【参考方案4】:

我找到了一些例子,其中 PyPy 比 Python 慢。 但是:仅在 Windows 上。

C:\Users\User>python -m timeit -n10 -s"from sympy import isprime" "isprime(2**521-1);isprime(2**1279-1)"
10 loops, best of 3: 294 msec per loop

C:\Users\User>pypy -m timeit -n10 -s"from sympy import isprime" "isprime(2**521-1);isprime(2**1279-1)"
10 loops, best of 3: 1.33 sec per loop

所以,如果您想到 PyPy,请忘记 Windows。 在 Linux 上,您可以实现出色的加速。 示例(列出 1 到 1,000,000 之间的所有素数):

from sympy import sieve
primes = list(sieve.primerange(1, 10**6))

这在 PyPy 上的运行速度比在 Python 上快 10(!) 倍。 但不是在窗户上。它的速度只有 3 倍。

【讨论】:

有趣!更多的比较和数字会很棒。【参考方案5】:

为了简单起见:PyPy 提供了 CPython 所缺乏的速度,但牺牲了它的兼容性。然而,大多数人选择 Python 是因为它的灵活性和“包含电池”的特性(高兼容性),而不是它的速度(尽管它仍然是首选)。

【讨论】:

"battery-included" 表示大型标准库,AFAIK【参考方案6】:

因为 pypy 不是 100% 兼容的,需要 8 gigs 的 ram 编译,是一个移动的目标,并且是高度实验性的,其中 cpython 是稳定的,是模块构建器 2 十年的默认目标(包括不支持的 c 扩展)在 pypy 上工作),并且已经广泛部署。

Pypy 可能永远不会成为参考实现,但它是一个很好的工具。

【讨论】:

根据pypy.org/download.html,PyPy 需要 4 GB 的 RAM 来编译(在 64 位系统上),而不是 8。如果需要,该页面上有一个选项可以在 3 GB 以下进行。 @knite 1:这是 2015 年的新内容,文档历来读取 8 GB。 2:在 2015 年的实践中你仍然需要至少 8 个,其中 6-7 个是免费的。 如果你使用build or distribution,编译的内存需求就不是那么重要了。至于“移动目标,高度实验性”,你能举几个破坏的例子吗?同样,如果人们使用的是发布版本而不是夜间版本或源代码,那么他们对功能没有合理的期望吗? @smci 这是一个基于古代数据的古老问题,有古老的答案。考虑这个问题和每个答案都是 4 年前 pypy 状态的历史。 @Tritium21:我只对当前答案感兴趣。它是什么?您可能想编辑您的答案,说 “截至 2013 年,比较 pypy 与 Python 的 2.x 版本是......” 此外,如果问题中的“6.3x 几何平均”声明是过时(as of 4/2017 they claim 7.5x, but even then depends on the benchmarks...),那么也需要编辑(版本号,最新数据等)我认为基准套件不是很相关,现在几乎没有人会在 CPU 上使用脚本语言运行光线追踪.我确实找到了pybenchmarks.org【参考方案7】:

问:与 CPython 相比,如果 PyPy 能够解决这些巨大挑战(速度、内存消耗、并行性),那么它的哪些弱点阻碍了更广泛的采用?

A:首先,几乎没有证据表明 PyPy 团队可以总体上解决速度问题。长期证据表明,PyPy 运行某些 Python 代码比 CPython 慢,而这个缺点似乎深深植根于 PyPy。

其次,在相当多的情况下,当前版本的 PyPy 比 CPython 消耗更多的内存。所以 PyPy 还没有解决内存消耗的问题。

PyPy 是否解决了上述巨大挑战,并且总体而言是否会比 CPython 更快、更少内存占用以及对并行性更友好,这是一个短期内无法解决的悬而未决的问题。有些人认为 PyPy 永远无法提供一种通用解决方案,使其能够在所有情况下主宰 CPython 2.7 和 3.3。

如果 PyPy 总体上优于 CPython(这是值得怀疑的),那么影响其更广泛采用的主要弱点将是它与 CPython 的兼容性。还有一些问题,例如 CPython 可以在更广泛的 CPU 和操作系统上运行,但与 PyPy 的性能和 CPython 兼容性目标相比,这些问题的重要性要小得多。


问:为什么我现在不能用 PyPy 替换 CPython?

答:PyPy 不是 100% 与 CPython 兼容,因为它没有在底层模拟 CPython。有些程序可能仍然依赖于 CPython 的独特功能,而 PyPy 中没有这些功能,例如 C 绑定、Python 对象和方法的 C 实现,或者 CPython 垃圾收集器的增量特性。

【讨论】:

此答案未引用任何基准或提供参考。【参考方案8】:

对于很多项目来说,不同的python在速度方面实际上存在0%的差异。那是那些受工程时间支配的那些,并且所有 python 都具有相同数量的库支持。

【讨论】:

如果你的项目那么简单,那么显然没关系,但任何语言的任何实现都可以这样说:如果你所做的只是通过相对高性能的 ABI 聚合其他库的功能,那么一切都无关紧要。 它与简单无关。在工程时间里,反馈回路很重要。有时比运行时间更重要。 嗯,你说的很含糊(工程时间没有提到正在设计的东西,约束是什么等;反馈循环没有提到反馈给谁,等等.),所以我将退出这次谈话,而不是交换神秘的参考资料。 这里没什么含糊的。看看 OODA 循环,即 PDCA。 @user 好吧,任何运行一次的项目需要一个月的时间编写,一分钟的运行时间,使用 PyPy 的总体速度将提高 0.0%(1 个月 + 1 分钟对比 1 个月),即使PyPy 快了一千倍。 Stephan 并没有声称所有项目都会有 0% 的加速。【参考方案9】:

CPython 有引用计数和垃圾收集,PyPy 只有垃圾收集。

所以对象往往会更早地被删除,__del__ 在 CPython 中以更可预测的方式调用。一些软件依赖于这种行为,因此它们还没有准备好迁移到 PyPy。

其他一些软件可以同时使用这两种方法,但使用 CPython 使用的内存更少,因为未使用的对象会更早地被释放。 (我没有任何测量结果表明这有多重要以及其他哪些实现细节会影响内存使用。)

【讨论】:

应该强调的是,即使在 CPython 中,依赖__del__ 被提前调用或根本不调用也是错误的。正如您所说,它通常有效,有些人认为这意味着它是有保证的。如果引用该对象的任何内容都陷入了引用循环中(这很容易 - 你知道以某种非人为的方式检查当前异常会创建一个引用循环吗?)最终确定会无限期延迟,直到下一个循环 GC (可能从不)。如果对象本身是引用循环的一部分,则根本不会调用__del__(在 Python 3.4 之前)。 CPython 中每个对象的开销更高,一旦您开始创建大量对象,这很重要。我相信 PyPy 默认情况下相当于 slots,一方面。【参考方案10】:

该网站确实声称 PyPy 比 CPython 快 6.3 倍。引用:

所有基准测试的几何平均值比 CPython 快 0.16 或 6.3 倍

这是一个非常与您所做的一揽子声明不同的声明,当您了解其中的区别时,您将理解至少一组您不能只说“使用 PyPy”的原因”。这听起来像是我在吹毛求疵,但理解为什么这两个陈述完全不同是至关重要的。

分解:

他们所做的声明仅适用于他们使用的基准测试。它完全没有说明您的程序(除非您的程序与他们的基准测试之一完全相同)。

该陈述是关于一组基准的平均值。没有人声称运行 PyPy 会带来 6.3 倍的改进,即使对于他们测试过的程序也是如此。

没有人声称 PyPy 甚至会运行 CPython 运行的所有程序,更不用说更快了。

【讨论】:

当然,没有人声称 PyPy 会更快地运行所有 Python 代码。但是,如果您使用所有纯 Python 应用程序,我敢打赌,它们中的绝大多数在 PyPy 上运行速度会比在 CPython 上快得多(> 3 倍)。 你的前两个要点都没有意义。你怎么能说基准测试说“你的程序绝对没有”。很明显,基准测试并不是所有实际应用程序的完美指标,但它们绝对可以作为一个有用的指标。另外,我不明白您对他们报告一组基准的平均值有什么误导。他们说得很清楚,这是一个平均值。如果程序员不了解平均值是什么,那么他们所关心的问题比语言性能要严重得多。 @SeanGeoffreyPietz - 我并没有声称 PyPy 的网站有任何误导性 - 他们准确地展示了他们的结果。但最初的问题错误地引用了它们,并表明作者不理解“平均”这个词的重要性。许多单独的基准测试速度不是快 6.3 倍。如果你使用不同类型的平均值,你会得到不同的值,所以“6.3 倍快”并不是“几何平均值快 6.3 倍”的充分总结。 “A 组比 B 组快 Z 倍”太模糊,没有意义。 -1: @spookylukey 您似乎暗示基准套件存在偏见,但没有提供支持该主张的证据。批评应始终有证据支持! @EvgeniSergeev - 不,我的意思是所有的基准测试都有偏差!当然,不一定是故意的。可能有用的程序的空间是无限的,而且变化莫测,一组基准只衡量这些基准的性能。问“PyPy 比 CPython 快多少?”就像问“如果 Fred 比 Joe 快多少?”,这是 OP 似乎想知道的。【参考方案11】:

我在这个主题上做了一个小基准测试。虽然许多其他海报都对兼容性提出了很好的观点,但我的经验是 PyPy 仅在位移动方面并没有那么快。对于 Python 的许多用途,它实际上只存在于在两个或多个服务之间转换位。例如,没有多少 Web 应用程序对数据集执行 CPU 密集型分析。相反,它们从客户端获取一些字节,将它们存储在某种数据库中,然后将它们返回给其他客户端。有时数据的格式会发生变化。

BDFL 和 CPython 开发人员是一群非常聪明的人,他们设法帮助 CPython 在这种情况下表现出色。这是一个无耻的博客插件:http://www.hydrogen18.com/blog/unpickling-buffers.html。我正在使用 Stackless,它源自 CPython,并保留了完整的 C 模块接口。在这种情况下,我没有发现使用 PyPy 有什么好处。

【讨论】:

PyPy 有很多,小心运行benchmarks(不幸的是,与 CPython 不同,它目前还没有面向用户的基准测试套件)。当然,对于网络流量,PyPy 不能神奇地让任何事情变得更快。 Julian,值得注意的是,PyPy 人员多年来一直致力于改进特定基准套件的运行时。在某种程度上,他们似乎对这组基准“过度拟合”了他们的优化,并且根据我的经验,除了纯数值计算(无论如何在 Fortran 或 C99 中更好),我从来没有让 PyPy 变得更多比 CPython 快约 2 倍。 @AlexRubinsteyn 但是那些从事 PyPy 工作的人的观点一直是,如果你发现 PyPy 比 CPython 慢的情况,你可以把它变成一个合理的基准,它很有可能被添加到套件中。 我检查了你的博客。在您的结果中,纯 python 对 (pickle, StringIO) 表明 pypy 比 cpython 快约 6.8 倍。我认为这是一个有用的结果。在你的结论中,你(正确地)指出 pypy 代码(它是纯 python!)比 C 代码(cPickle、cStringIO)慢,而不是 cpython 代码。 @gsnedders 我在multiple occasions 上提供了一个基于rinohtype 的基准。他们尚未将其添加到套件中。【参考方案12】:

第二个问题更容易回答:如果您的所有代码都是纯 Python,那么您基本上可以使用 PyPy 作为替代品。但是,许多广泛使用的库(包括一些标准库)是用 C 编写的,并编译为 Python 扩展。其中一些可以与 PyPy 一起使用,有些则不能。 PyPy 提供与 Python 相同的“前向”工具 —— 也就是说,它是 Python —— 但它的内部结构不同,因此与这些内部结构交互的工具将不起作用。

至于第一个问题,我想它有点像第一个问题的 Catch-22:PyPy 一直在迅速发展,以提高速度并增强与其他代码的互操作性。这使得它比官方更具实验性。

我认为如果 PyPy 进入稳定状态,它可能会开始得到更广泛的使用。我也认为 Python 摆脱它的 C 基础会很棒。但暂时不会发生。 PyPy 尚未达到几乎足够有用的临界质量,它可以自行完成所有你想做的事情,这将激励人们填补空白。

【讨论】:

我不认为 C 是一种很快就会流行的语言(我愿意说,它不会在我们的有生之年消失)。除非有另一种可以在任何地方运行的语言,否则我们将拥有 C。(注意,JVM 是用 C 编写的。即使是 java,这种“到处运行”的语言也需要 C 来实现它的无处不在。)否则,我同意这篇文章的大部分内容它的点。 @Tritium21:是的,我只是在那里进行社论。我对现有的 C 感到满意,但我认为 Python 对 C 的依赖是非常有害的,PyPy 就是一个很好的例子:现在我们有机会获得更快的 Python,但多年来依赖 C 却让我们绊倒了. 要是让Python自己两只脚站立会好很多。如果 Python 本身是用 C 编写的,那也没关系,但问题是存在一种扩展机制,它鼓励人们以依赖 C 的方式扩展 Python。 双刃剑——python如此受欢迎的部分原因在于它能够扩展其他应用程序并被其他应用程序扩展。如果你把它拿走,我认为我们不会谈论 python。 @BrenBarn 声称 Python 对 C 的依赖是有害的,这完全是愚蠢的。如果没有 Python 的 C-API,大多数真正强大的库和 Python 在其青少年时期(90 年代后期)获得的大多数真正强大的库和出色的互操作性,包括整个数字/科学生态系统和 GUI 界面,都是不可能的。在做出这样的笼统陈述之前,环顾四周,了解一下 Python 的整个用法。 @PeterWang 所有这些库都可以用 Python 编写,但是它们不会像现在这样快。 BrenBarn 的意思是,现在我们有机会让 python 足够快,以便可以用 python 编写这些库,但我们拒绝抓住这个机会,因为抓住它意味着失去使用 C 库的能力。我相信这就是他所说的有害的意思,并不是说 C 库的存在是一件坏事,而是制作快速库的唯一方法是使用 C。

以上是关于如果 PyPy 快 6.3 倍,为啥我不应该使用 PyPy 而不是 CPython?的主要内容,如果未能解决你的问题,请参考以下文章

纯 C++ 代码比内联汇编程序快 10 倍。为啥?

为啥在这里 numba 比 numpy 快?

哈希表 - 为啥它比数组快?

停止使用 CSV 进行存储,这种文件格式快 150 倍

pypy真的能让python比c还快?

如何禁用 pypy 断言语句?