PyPy入门

0 股票
0
0
0
0

介绍

Python编程语言是一个接口,可以用多种不同的方式实现。例如,CPython使用C语言实现,Jython使用Java实现等等。.

尽管 CPython 是最流行的 Python 实现,但它的速度并非最快。PyPy 是另一种兼容且速度很快的 Python 实现。PyPy 依赖于即时编译 (JIT),这可以显著缩短长时间运行操作的执行时间。.

本教程将为初学者介绍 PyPy,并重点介绍它与 CPython 的区别。我们还将讨论它的优势和局限性。然后,我们将了解如何下载和使用 PyPy 来运行一个简单的 Python 脚本。.

具体而言,本次培训涵盖以下内容:

  • CPython 简介
  • PyPy及其特性简介
  • PyPy 的局限性
  • 在 Ubuntu 上运行 PyPy
  • PyPy 与 CPython 运行时对比

CPython 简介

在讨论 PyPy 之前,了解 CPython 的工作原理至关重要。下图展示了使用 CPython 实现的 Python 脚本的执行流程。.

给定一个 Python 脚本文件 .py,首先使用 CPython 编译器将源代码编译成字节码。生成的字节码存储在扩展名为 .pyc 的文件中。然后,使用 CPython 解释器在虚拟环境中执行该字节码。.

使用编译器将源代码转换为字节码有很多优势。如果不使用编译器,解释器会直接处理源代码,逐行将其翻译成机器代码。这样做的缺点是,将每一行源代码翻译成机器代码都需要执行一些处理过程,而且这些过程会针对每一行重复执行。例如,语法分析是独立于其他行对每一行进行的,因此解释器会花费大量时间来翻译代码。编译器解决了这个问题,因为它能够一次性处理所有代码,因此语法分析只需执行一次,而不是针对每一行代码。所以,编译器生成的字节码更容易解释。需要注意的是,在某些情况下,编译整个源代码可能并非必要,我们将在讨论 PyPy 时看到一个清晰的例子。.

字节码生成后,由运行在虚拟机中的解释器执行。虚拟环境的优势在于它将 CPython 字节码与机器解耦,从而使 Python 成为跨平台程序。.

遗憾的是,仅仅使用编译器生成字节码并不足以加速 CPython 的执行。解释器的工作原理是每次执行时都将代码翻译成机器码。因此,如果一行代码执行需要 LX 秒,那么执行 10 次就需要 X * 10 秒。对于耗时较长的操作来说,这会造成非常大的执行时间开销。.

基于 CPython 的 bug,现在让我们来看看 PyPy。.

PyPy及其特性简介

PyPy 是一个类似于 CPython 的 Python 实现,它既兼容又快速。“兼容”意味着 PyPy 与 CPython 兼容,也就是说,你可以在 PyPy 中使用几乎所有 CPython 命令。正如这里提到的,两者之间也存在一些兼容性差异。PyPy 最强大的优势在于其速度。PyPy 比 CPython 快得多。我们稍后会看到一些测试,其中 PyPy 的速度大约是 CPython 的 7 倍。在某些情况下,它的速度甚至可能是 CPython 的几十倍或几百倍。那么,PyPy 是如何实现如此高的速度的呢?

速度

PyPy 使用即时 (JIT) 编译器,可以显著提升 Python 脚本的运行速度。CPython 使用的是提前编译 (AOT) 方式,这意味着所有代码在执行前都会被编译成字节码。而 JIT 编译器只在运行时需要时才进行编译。.

源代码中可能包含一些根本不会执行的代码块,但这些代码块仍然会被AOT编译器编译。这会导致处理速度变慢。当源代码很大,包含数千行代码时,使用JIT编译器会带来显著的性能提升。AOT编译器会编译整个源代码,因此耗时很长。而JIT编译器只执行所需的代码部分,速度要快得多。.

PyPy 翻译完一段代码后,会将其缓存起来。这意味着代码只需翻译一次,之后就可以直接使用翻译结果。而 CPython 解释器每次执行代码时都会重复翻译,这也是它速度慢的原因之一。.

毫不费力

PyPy并非提升Python脚本性能的唯一途径,但却是最简单的方法。例如,Cython可以用来加速将C类型赋值给变量。问题在于,Cython需要开发者手动审查源代码并进行优化。这既繁琐又复杂,而且随着代码规模的增大,优化工作量也会增加。而使用PyPy,你只需毫不费力地运行普通的Python代码,就能显著提升运行速度。.

无堆叠

标准 Python 使用 C 语言的栈。这个栈存储着函数之间相互调用的顺序(返回值)。由于栈的大小有限,函数调用的次数也受到限制。.

PyPy 使用 Stackless Python,这是一种不使用 C 栈的 Python 实现。它将函数调用与对象一起存储在栈上。栈的大小大于堆的大小,因此可以进行更多的函数调用。.

无栈 Python 还支持微线程,这比普通的 Python 线程更胜一筹。在无栈 Python 线程中,您可以运行数千个称为«任务片段»(tasklet)的任务,所有这些任务都在单个线程上运行。.

使用任务组(tasklet)可以实现任务的并发执行。并发是指两个任务同时运行,共享相同的资源。一个任务运行一段时间后会停止,以便为第二个任务的运行腾出空间。请注意,这与并行不同,并行是指运行两个独立但同时执行的任务。.

使用 Tasklet 可以减少创建的线程数量,从而降低操作系统管理所有这些线程的开销。因此,通过在两个线程之间切换来加速执行所需的时间比在两个任务之间切换要长。.

使用 Stackless Python 也为延续(Continuation)功能铺平了道路。延续允许我们保存任务状态,并在之后恢复该状态以继续执行任务。请注意,Stackless Python 与标准 Python 并无本质区别,它只是增加了更多功能。标准 Python 中提供的所有功能,在 Stackless Python 中也同样可用。.

在讨论了 PyPy 的优点之后,让我们在下一节中讨论它的局限性。.

PyPy 的局限性

虽然 CPython 可以在任何机器和任何 CPU 架构上使用,但 PyPy 的支持相对有限。.

以下是 PyPy 支持和维护的 CPU 架构(来源):

  • x86 (IA-32) 和 x86_64
  • ARM平台(ARMv6或ARMv7,带VFPv3)
  • AArch64
  • PowerPC 64 位,小端和大端均可
  • System Z (s390x)

PyPy并非在所有Linux发行版上都能运行,因此请务必使用受支持的发行版。在不受支持的发行版上运行PyPy Linux二进制文件将会报错。PyPy仅支持一个版本的Python 2和Python 3,分别是PyPy 2.7和PyPy 3.6。.

如果 PyPy 运行的代码是纯 Python 代码,那么它带来的速度提升通常非常显著。但是,如果代码包含 NumPy 等 C 扩展,PyPy 实际上可能会增加运行时间。PyPy 项目目前仍在积极开发中,因此未来可能会提供对 C 扩展更好的支持。.

PyPy 不受许多流行的 Python 框架(例如 Kivy)的支持。Kivy 允许 CPython 在包括 Android 和 iOS 在内的所有平台上运行。这意味着 PyPy 无法在移动设备上运行。.

现在我们已经了解了 PyPy 的优点和局限性,接下来让我们解释一下如何在 Ubuntu 上运行 PyPy。.

在 Ubuntu 上运行 PyPy

PyPy 可以在 Mac、Linux 或 Windows 上运行,但我们将讨论如何在 Ubuntu 上运行它。需要注意的是,PyPy 的 Linux 二进制文件仅支持特定的 Linux 发行版。您可以在此页面查看可用的 PyPy 二进制文件及其支持的发行版。例如,PyPy(或 Python 2.7 或 Python 3.6)仅支持三个版本的 Ubuntu:18.04、16.04 和 14.04。如果您使用的是截至目前为止的最新版本 Ubuntu(19.10),则无法在其上运行 PyPy。尝试在不受支持的发行版上运行 PyPy 将返回以下错误:

PyPy 二进制文件以压缩文件的形式提供。您只需解压缩下载的文件即可。解压缩后的文件夹中有一个名为 bin 的文件夹,PyPy 可执行文件就位于其中。我使用的是 Python 3.6,所以文件名为 pypy3。对于 Python 2.7,文件名为 pypy。.

对于 CPython,如果想从终端运行 Python 3,只需输入命令 python3。要运行 PyPy,只需发出命令 pypy3。.

如下图所示,在终端输入 `pypy3` 命令可能会返回‘找不到 'pypy3'’的错误信息。这是因为 PyPy 的路径尚未添加到 PATH 环境变量中。正确的命令是 `./pypy3`,前提是终端当前路径位于 PyPy 的 bin 目录内。`.` 指的是当前目录,`/` 用于访问当前目录中的某个文件。执行 `./pypy3` 命令可以成功运行 Python 程序。.

现在你可以像往常一样使用 Python,并利用 PyPy 的优势。例如,我们可以创建一个简单的 Python 脚本来计算 1000 个数字的和,并使用 PyPy 运行它。代码如下。.

nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)

如果该脚本名为 test.py,您可以直接使用以下命令运行它(假设 Python 文件位于 PyPy bin 文件夹中,该文件夹与 pypy3 命令位于同一位置)。.

./pypy3 test.py

下图显示了执行前面代码的结果。.

PyPy 与 CPython 运行时对比

为了比较 PyPy 和 CPython 计算 1000 个数字之和的执行时间,测量时间的代码如下。.

import time
t1 = time.time()
nums = range(1000)
sum = 0
for k in nums:
sum = sum + k
print("Sum of 1,000 numbers is : ", sum)
t2 = time.time()
t = t2 - t1
print("Elapsed time is : ", t, " seconds")

PyPy 的运行时间接近 0.00045 秒,而 CPython 则为 0.0002 秒(我的机器配置为 2.5 GHz 的 Core i7-6500U)。在这种情况下,CPython 的运行时间比 PyPy 短,这也在意料之中,因为这并非一个耗时较长的任务。如果代码需要计算 100 万个数字而不是 1000 个,那么 PyPy 最终会胜出。在这种情况下,PyPy 的运行时间为 0.00035 秒,而 CPython 则需要 0.1 秒。PyPy 的优势显而易见。这应该能让你明白 CPython 在处理长时间运行的任务时速度有多么慢。.

结果

本教程将介绍 PyPy,它是速度最快的 Python 实现。PyPy 的主要优势在于其即时 (JIT) 编译,它会将编译后的机器代码缓存起来,防止重复执行。教程也会重点介绍 PyPy 的局限性,其中最主要的一点是它虽然对纯 Python 代码表现出色,但对 C 扩展的效率不高。.

我们还了解了 PyPy 在 Ubuntu 上的运行情况,并比较了 CPython 和 PyPy 的执行时间,重点展示了 PyPy 在处理长时间运行任务方面的性能优势。同时,CPython 在处理短时间运行任务方面可能仍然优于 PyPy。我们将在后续文章中进一步探讨 PyPy、CPython 和 Cython 之间的比较。.

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

您可能也喜欢