PyPy:以最少的努力更快的 Python

网友投稿 1115 2022-05-30

目录

Python 和 PyPy

安装

PyPy 在行动

PyPy 及其特性

即时 (JIT) 编译器

垃圾收集

PyPy 的局限性

它不适用于 C 扩展

它只适用于长时间运行的程序

它不进行提前编译

结论

Python 是开发人员中最流行的编程语言之一,但它有一定的局限性。例如,根据应用程序的不同,它的速度可能是某些低级语言的100 倍。这就是为什么一旦 Python 的速度成为用户的瓶颈,许多公司就会用另一种语言重写他们的应用程序。但是,如果有一种方法可以保留 Python 的出色功能并提高其速度呢?输入PyPy。

PyPy是一个非常兼容的 Python 解释器,是 CPython 2.7、3.6 和即将推出的 3.7 的一个有价值的替代品。通过使用它安装和运行您的应用程序,您可以获得显着的速度改进。您将看到多少改进取决于您正在运行的应用程序。

在本教程中,您将学习:

如何使用PyPy安装和运行代码

PyPy 与CPython在速度方面的比较

PyPy 的功能是什么以及它们如何使您的 Python 代码运行得更快

PyPy 的局限性是什么

本教程中的示例使用 Python 3.6,因为这是 PyPy 兼容的最新 Python 版本。

Python 和 PyPy

Python语言规范用于许多实现,例如CPython(用C编写)、Jython(用Java编写)、IronPython(为 .NET 编写)和 PyPy(用 Python 编写)。

CPython 是 Python 的原始实现,是迄今为止最受欢迎和维护最多的。当人们提到 Python 时,他们通常指的是 CPython。您现在可能正在使用 CPython!

但是,由于它是一种高级解释语言,因此 CPython 有一定的局限性,不会因速度而获得任何奖牌。这就是 PyPy 可以派上用场的地方。由于它遵循 Python 语言规范,因此 PyPy 不需要更改您的代码库,并且由于您将在下面看到的功能,它可以提供显着的速度改进。

现在,您可能想知道,如果 CPython 使用相同的语法,为什么不实现 PyPy 的出色功能。原因是实现这些功能需要对源代码进行大量更改,并且将是一项重大任务。

在不深入研究理论的情况下,让我们看看 PyPy 的实际应用。

安装

您的操作系统可能已经提供了 PyPy 包。例如,在 macOS 上,您可以借助Homebrew安装它:

$ brew install pypy3

如果没有,您可以为您的操作系统和体系结构下载预构建的二进制文件。完成下载后,只需解压 tarball 或 ZIP 文件即可。然后你就可以执行 PyPy 而不需要在任何地方安装它:

$ tar xf pypy3.6-v7.3.1-osx64.tar.bz2 $ ./pypy3.6-v7.3.1-osx64/bin/pypy3 Python 3.6.9 (?, Jul 19 2020, 21:37:06) [PyPy 7.3.1 with GCC 4.2.1] Type "help", "copyright", "credits" or "license" for more information.

在执行上面的代码之前,您需要位于下载二进制文件的文件夹中。有关完整说明,请参阅安装文档。

PyPy 在行动

你现在已经安装了 PyPy,你可以看到它的运行了!为此,请创建一个名为的 Python 文件script.py并将以下代码放入其中:

1total = 0 2for i in range(1, 10000): 3 for j in range(1, 10000): 4 total += i + j 5 6print(f"The result is {total}")

这是一个脚本,在两个嵌套for循环中,将数字从1to相加9,999并打印结果。

要查看运行此脚本需要多长时间,请编辑它以添加突出显示的行:

1import time 2 3start_time = time.time() 4 5total = 0 6for i in range(1, 10000): 7 for j in range(1, 10000): 8 total += i + j 9 10print(f"The result is {total}") 11 12end_time = time.time() 13print(f"It took {end_time-start_time:.2f} seconds to compute")

该代码现在执行以下操作:

第 3行将当前时间保存到变量start_time。

第 5 到 8 行运行循环。

第 10 行打印结果。

第 12行将当前时间保存到end_time.

13个线打印之间的区别start_time,并end_time显示它花了多长时间来运行的脚本。

尝试用 Python 运行它。这是我在 2015 年 MacBook Pro 上得到的:

$ python3.6 script.py The result is 999800010000 It took 20.66 seconds to compute

现在用 PyPy 运行它:

$ pypy3 script.py The result is 999800010000 It took 0.22 seconds to compute

在这个小型综合基准测试中,PyPy 的速度大约是 Python 的 94 倍!

对于更严格的基准测试,您可以查看 PyPy速度中心,其中开发人员使用不同的可执行文件每晚运行基准测试。

请记住,PyPy 如何影响您的代码性能取决于您的代码在做什么。在某些情况下,PyPy 实际上更慢,稍后您将看到。然而,在几何平均上,它的速度是 Python 的 4.3 倍。

PyPy 及其特性

从历史上看,PyPy 提到了两件事:

一个动态语言的框架,用于产生口译动态语言

一个Python实现使用框架

通过安装 PyPy 并使用它运行一个小脚本,您已经看到了第二种含义。您使用的 Python 实现是使用名为RPython的动态语言框架编写的,就像 CPython 是用 C 编写的,而 Jython 是用 Java 编写的。

但是你之前不是被告知 PyPy 是用 Python 编写的吗?嗯,这有点简化。PyPy 被称为用 Python(而不是用 RPython)编写的 Python 解释器的原因是 RPython 使用与 Python 相同的语法。

为了澄清一切,以下是 PyPy 的生成方式:

源代码是用 RPython 编写的。

所述RPython翻译工具链被施加到代码,这基本上使得代码更高效。它还会将代码编译成机器码,这就是 Mac、Windows 和 Linux 用户必须下载不同版本的原因。

生成二进制可执行文件。这是您用来运行小脚本的 Python 解释器。

请记住,您无需执行所有这些步骤即可使用 PyPy。可执行文件已经可供您安装和使用。

此外,由于框架和实现使用同一个词非常令人困惑,PyPy 背后的团队决定摆脱这种双重用法。现在,PyPy 仅指 Python 实现。该框架被称为RPython 翻译工具链。

接下来,您将了解在某些情况下使 PyPy 比 Python 更好更快的功能。

即时 (JIT) 编译器

在深入了解什么是 JIT 编译之前,让我们先退后一步,回顾一下C 等编译型语言和JavaScript等解释型语言的特性。

编译型编程语言的性能更高,但更难移植到不同的 CPU 架构和操作系统。解释型编程语言具有更强的可移植性,但其性能比编译型语言差很多。这是光谱的两个极端。

然后是诸如 Python 之类的编程语言,它们混合了编译和解释。具体来说,Python首先被编译成一个中间字节码,然后由CPython解释。这使得代码比用纯解释性编程语言编写的代码性能更好,并且保持了可移植性优势。

但是,性能仍然远不及编译版本。原因是编译后的代码可以进行很多字节码无法实现的优化。

这就是即时 (JIT) 编译器的用武之地。它试图通过将一些真正的编译成机器代码和一些解释来获得两个世界中更好的部分。简而言之,以下是 JIT 编译提供更快性能所需的步骤:

确定代码中最常用的组件,例如循环中的函数。

在运行时将这些部分转换为机器代码。

优化生成的机器码。

用优化的机器码版本交换之前的实现。

还记得教程开头的两个嵌套循环吗?PyPy 检测到重复执行相同的操作,将其编译为机器代码,优化机器代码,然后交换实现。这就是为什么你看到速度有如此大的提升。

垃圾收集

每当您创建变量、函数或任何其他对象时,您的计算机都会为它们分配内存。最终,将不再需要其中一些对象。如果您不清理它们,那么您的计算机可能会耗尽内存并使程序崩溃。

在 C 和 C++ 等编程语言中,您通常必须手动处理此问题。其他编程语言(例如 Python 和 Java)会自动为您完成。这称为自动垃圾回收,有多种技术可以实现它。

CPython 使用一种称为引用计数的技术。本质上,Python 对象的引用计数在对象被引用时递增,在对象取消引用时递减。当引用计数为零时,CPython 会自动调用该对象的内存释放函数。这是一种简单而有效的技术,但有一个问题。

当一棵大对象树的引用计数变为零时,所有相关对象都被释放。因此,您可能会暂停很长时间,在此期间您的程序根本没有进展。

此外,还有一个用例,其中引用计数根本不起作用。考虑以下代码:

1class A(object): 2 pass 3 4a = A() 5a.some_property = a 6del a

PyPy:以最少的努力更快的 Python

在上面的代码中,您定义了新类。然后,您创建该类的一个实例并将其分配为自身的一个属性。最后,删除实例。

此时,该实例不再可访问。但是,引用计数不会从内存中删除该实例,因为它具有对自身的引用,因此引用计数不为零。这个问题叫做引用循环,不能用引用计数来解决。

这就是 CPython 使用另一个称为循环垃圾收集器的工具的地方。它从像type对象这样的已知根开始遍历内存中的所有对象。然后它识别所有可达的对象并释放不可达的对象,因为它们不再存在。这解决了参考循环问题。但是,当内存中有大量对象时,它会产生更明显的暂停。

另一方面,PyPy 不使用引用计数。相反,它仅使用第二种技术,即循环查找器。也就是说,它周期性地从根开始遍历活着的对象。这使 PyPy 比 CPython 有一些优势,因为它不打扰引用计数,使得在内存管理上花费的总时间比在 CPython 中少。

此外,PyPy 不是在像 CPython 这样的一项主要任务中完成所有工作,而是将工作分成可变数量的部分并运行每个部分,直到没有剩余。这种方法在每个次要集合后只增加几毫秒,而不是像 CPython 那样一次性增加数百毫秒。

垃圾收集很复杂,并且有更多超出本教程范围的详细信息。您可以在文档 中找到有关 PyPy 垃圾收集的更多信息。

PyPy 的局限性

PyPy 不是灵丹妙药,可能并不总是最适合您的任务的工具。它甚至可能使您的应用程序执行得比 CPython 慢得多。这就是为什么记住以下限制很重要的原因。

它不适用于 C 扩展

PyPy 最适合纯 Python 应用程序。每当您使用C 扩展模块时,它的运行速度都比在 CPython 中慢得多。原因是 PyPy 不能优化 C 扩展模块,因为它们没有得到完全支持。此外,PyPy 必须模拟该部分代码的引用计数,使其更慢。

在这种情况下,PyPy 团队建议删除 CPython 扩展并将其替换为纯 Python 版本,以便 JIT 可以看到它并进行优化。如果这不是一个选项,那么您将不得不使用 CPython。

话虽如此,核心团队正在研究 C 扩展。一些包已经被移植到 PyPy 并且工作得同样快。

它只适用于长时间运行的程序

想象一下,你想去一家离你家很近的商店。你可以步行或开车。

你的车显然比你的脚快得多。但是,请考虑一下它需要您做什么:

去你的车库。

启动你的车。

稍微暖一下车。

开车去商店。

找个停车位。

在回来的路上重复这个过程。

开车有很多开销,如果你想去的地方就在附近,那并不总是值得的!

现在想想如果你想去五十英里外的邻近城市会发生什么。开车去那里肯定是值得的,而不是步行。

尽管速度上的差异并不像上面的类比那样明显,但 PyPy 和 CPython 也是如此。

当您使用 PyPy 运行脚本时,它会做很多事情来使您的代码运行得更快。如果脚本太小,那么开销将导致您的脚本运行速度比在 CPython 中慢。另一方面,如果您有一个长时间运行的脚本,那么该开销可以带来显着的性能红利。

要亲自查看,请在 CPython 和 PyPy 中运行以下小脚本:

1import time 2 3start_time = time.time() 4 5for i in range(100): 6 print(i) 7 8end_time = time.time() 9print(f"It took {end_time-start_time:.10f} seconds to compute")

当您使用 PyPy 运行它时,一开始会有一个小的延迟,而 CPython 会立即运行它。确切地说,0.0004873276在带有 CPython 的 2015 MacBook Pro 上运行它需要几秒钟,而在0.0019447803PyPy上运行它需要几秒钟。

它不进行提前编译

正如您在本教程开头所见,PyPy 不是完全编译的 Python 实现。它编译Python 代码,但它不是Python 代码的编译器。由于 Python 固有的动态性,将 Python 编译成独立的二进制文件并重用它是不可能的。

PyPy 是一种运行时解释器,它比完全解释的语言快,但比完全编译的语言(如 C)慢。

结论

PyPy 是 CPython 的快速且功能强大的替代品。通过使用它运行您的脚本,您可以在不更改代码的情况下获得重大的速度提升。但这不是灵丹妙药。它有一些限制,您需要测试您的程序以查看 PyPy 是否有帮助。

在本教程中,您学习了:

什么PyPy是

如何安装PyPy 并用它运行你的脚本

PyPy 与CPython在速度方面的比较

PyPy 具有哪些功能以及它如何提高程序速度

PyPy 有哪些限制可能使其不适用于某些情况

如果您的 Python 脚本需要稍微提高速度,请尝试使用 PyPy。根据您的程序,您可能会获得一些明显的速度提升!

Python 机器学习

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:高质量通信gRPC入门,有了它,谁还用Socket
下一篇:MindSpore21天实战营(5)使用PyCharm Kit进行基于Wide&Deep实现CTR预估实战
相关文章