常见问题解答¶
内容
- 常见问题解答
- 什么是 PyPy?
- PyPy 是 CPython 的直接替代品吗?
- 模块 xyz 不适用于 PyPy:ImportError
- 模块 xyz 在沙箱化的 PyPy 中不起作用?
- C 扩展模块是否适用于 PyPy?
- PyPy 在哪些平台上运行?
- PyPy 实现哪些 Python 版本?
- PyPy 有 GIL 吗?为什么?
- numpy、numpypy、micronumpy 怎么样?
- 我应该安装 numpy 还是 numpypy?
- PyPy 在尾调用方面比 CPython 更聪明吗?
- 如何为 PyPy 编写扩展模块?
- PyPy 的速度有多快?
- 我写了一个 3 行的基准测试,它比 CPython 慢。为什么?
- JIT 不能将已编译的机器代码转储并重新加载吗?
- 类型注释会帮助 PyPy 的性能吗?
- 我可以将 PyPy 的翻译工具链用于除 Python 之外的其他语言吗?
- 如何参与 PyPy 开发?我可以参加冲刺吗?
- OSError: … cannot restore segment prot after reloc… 帮助?
- 我应该如何报告错误?
- PyPy 为什么切换到 Git 并迁移到 GitHub?
- PyPy 在 Windows 64 上获得更好的支持需要什么?
- PyPy 将支持 Python2 多长时间?
另请参见:关于 RPython 的常见问题解答。
什么是 PyPy?¶
PyPy 是使用 RPython 翻译工具链用 Python 重写的 Python。
PyPy 试图找到关于语言实现的创建、灵活性和可维护性以及速度权衡的新答案。有关更多详细信息,请参阅我们的 目标和架构文档.
PyPy 是 CPython 的直接替代品吗?¶
几乎是!
任何给定项目的最大障碍可能是对 扩展模块 的支持。PyPy 支持不断增长的扩展模块数量,但到目前为止,主要只支持标准库中的那些模块。
语言特性(包括内置类型和函数)非常完善且经过良好测试,因此,如果您的项目没有使用太多扩展模块,那么它很有可能与 PyPy 兼容。
我们在 cpython 区别 中列出了已知的区别。
模块 xyz 不适用于 PyPy:ImportError¶
为 CPython 安装的模块不会自动适用于 PyPy - 就像为 CPython 3.6 安装的模块不会自动适用于 CPython 3.7 一样,即使您安装了这两个版本。换句话说,您需要专门为 PyPy 安装模块 xyz。
在 Linux 上,这意味着您不能使用 apt-get
或类似的包管理器:这些工具仅适用于同一包管理器提供的 CPython 版本。所以现在先忘掉它们,继续往下读。
如今,xyz 通常在 PyPI 上可用,并且可以使用 <pypy> -mpip install xyz
安装。最简单的解决方案是 使用 virtualenv(如本文档中所述)。然后进入(激活)virtualenv 并输入:pypy -mpip install xyz
。如果您不了解或不想使用 virtualenv,您也可以在 pypy -m ensurepip
之后使用 pip
本地安装。我们提供的 PyPy 下载中内置了 ensurepip 模块。使用 pip
的最佳实践是始终将其称为 <python> -mpip ...
,但如果您希望能够从命令行直接调用 pip
,则必须调用 pypy -mensurepip --default-pip
。
如果您从 C 编译器收到错误,则该模块是使用不受支持功能的 CPython C 扩展模块。 见下文。
或者,如果模块 xyz 在 PyPI 上不可用,或者您不想使用 virtualenv,则下载 xyz 的源代码,解压缩 zip/tarball,然后运行标准命令: pypy setup.py install
。 (注意:这里使用 pypy 而不是 python。)像往常一样,您可能需要使用 sudo 运行命令以进行全局安装。 setup.py
的其他命令也可用,例如 build
。
模块 xyz 在沙箱化的 PyPy 中不起作用?¶
您无法在 沙箱化的 PyPy 中导入任何扩展模块,抱歉。即使可用的内置模块也非常有限。PyPy 中的沙箱是一个很好的概念验证,毫无疑问是安全的,但它只是一个概念验证。目前,它需要来自有动力的开发人员的一些工作。但是,在此之前,它只能用于“纯 Python”示例:程序几乎不导入任何内容(或仅递归地导入纯 Python 模块)。
C 扩展模块是否适用于 PyPy?¶
首先要注意,一些 Linux 发行版(例如 Ubuntu、Debian)将 PyPy 分成多个包。如果您安装了名为“pypy”的包,那么您可能还需要安装“pypy-dev”才能使以下操作正常工作。
我们支持 c 扩展模块(使用 C-API 编写的模块),因此它们无需修改即可运行。这自 PyPy 1.4 版本以来一直是 PyPy 的一部分,并且支持几乎已完成。由于需要模拟引用计数,因此 PyPy 中的 CPython 扩展模块通常比 CPython 中的慢得多。将您的 c 扩展替换为 JIT 可以优化的纯 python 或 CFFI 版本通常更快。如果尝试安装模块 xyz,并且该模块具有相同代码的 C 和 Python 版本,请先尝试禁用 C 版本;这通常可以通过更改 setup.py
中的某些行来轻松完成。
我们完全支持基于 ctypes 的扩展。但为了获得最佳性能,我们建议您使用 cffi 模块与 C 代码进行交互。
有关我们如何管理引用计数语义的更多信息,请参阅 rawrefcount
PyPy 在哪些平台上运行?¶
PyPy 目前支持
- 大多数常见操作系统(Linux 32/64 位、Mac OS X 64 位、Windows 32/64 位、OpenBSD、FreeBSD)上的x86机器,
- 64 位AArch,也称为 ARM64,
- 运行 Linux 的ARM硬件(ARMv6 或 ARMv7,带 VFPv3)(我们不再提供这些硬件的预构建二进制文件),
- 运行 Linux 的PPC64的大端和小端变体,
- 运行 Linux 的s390x
PyPy 在 Linux 机器上定期且广泛地测试。它在 Mac 和 Windows 上运行:它在那里经过测试,但我们大多数人都在运行 Linux,因此修复可能取决于第三方贡献。
要从源代码引导,PyPy 可以使用 CPython 2.7 或 PyPy 2.7。交叉翻译实际上不受支持:例如,要构建 32 位 PyPy,您需要拥有 32 位环境。
PyPy 实现哪些 Python 版本?¶
PyPy 将始终支持 2.7,因为 RPython 是为它编写的。此外,PyPy 支持各种 Python3 版本,请参阅 发行说明以了解最新版本。通常,我们将支持一个或两个 Python3 版本。
PyPy 有 GIL 吗?为什么?¶
是的,PyPy 有 GIL。删除 GIL 非常困难。在 CPython 之上,您有两个问题:(1)GC,在本例中为引用计数;(2)整个 Python 语言。
对于 PyPy,难题是(2):我的意思是如果一个可变对象从一个线程更改并在另一个线程中并发读取会发生什么。这对任何可变类型都是一个问题:它需要仔细审查和修复(主要是细粒度锁)才能贯穿整个 Python 解释器。这是一项重大工作,虽然并非完全不可能,但正如 Jython/IronPython 所示。这包括关于某些效果对用户(即 Python 程序员)是否可以接受的微妙决定。
CPython 还有问题(1)引用计数。对于 PyPy,这个问题比较简单:我们需要使我们的 GC 具有多线程感知能力。这在 PyPy 中比在 CPython 中更容易高效地完成。但这并不能解决问题(2)。
请注意,曾经有工作支持 软件事务内存 (STM) 版本的 PyPy。这应该提供一个无需 GIL 的替代 PyPy,同时继续为 Python 程序员提供拥有一个 GIL 的完整错觉。这项工作目前由于其自身的技术困难而停滞不前。
numpy、numpypy 和 micronumpy 怎么样?¶
早在 2011 年,PyPy 团队就 开始在 PyPy 中重新实现 numpy。它有两个部分
- 内置模块 pypy/module/micronumpy:它用 RPython 编写,大致涵盖了
numpy.core.multiarray
模块的内容。令人困惑的是,它在 PyPy 中以_numpypy
的名称提供。它默认包含在所有官方版本的 PyPy 中(但将来可能会被删除)。- 我们维护的官方 numpy 存储库的 分支,非正式地称为
numpypy
:与上游 numpy 的主要区别在于,它基于用 RPython 编写的 micronumpy 模块,而不是用 C 编写的numpy.core.multiarray
。
我应该安装 numpy 还是 numpypy?¶
简而言之:你应该使用 numpy。你可以通过执行 pypy -m pip install numpy
来安装它。
上游 numpy
用 C 编写,在 cpyext 兼容层下运行。如今,cpyext 已经足够成熟,你可以直接使用上游 numpy
,因为它通过了测试套件。numpy
的主要缺点是 cpyext 非常慢,因此它的性能比 numpypy
差。但是,我们正在积极努力改进它,因为我们预计当 HPy 可以使用时,它将达到相同的速度。
另一方面,numpypy
更适合 JIT,并且调用速度非常快,因为它用 RPython 编写:但它是一个重新实现,很难完全兼容:多年来,该项目慢慢成熟,最终它能够调用 LAPACK 和 BLAS 库来加速矩阵计算,并达到了与上游 numpy 大约 80% 的一致性。然而,80% 远不及 100%。由于 cpyext/numpy 兼容性已经完成,我们已经停止了对 numpypy
的支持。
PyPy 在尾调用方面比 CPython 更聪明吗?¶
不。PyPy 遵循 Python 语言设计,包括内置的调试器功能。这阻止了尾调用,正如 Guido van Rossum 在 两篇 博客 文章中总结的那样。此外,JIT 和 Stackless 都没有改变这一点。
PyPy 的速度有多快?¶
这实际上取决于你的代码。对于纯 Python 算法代码,它非常快。对于更典型的 Python 程序,我们通常是 CPython 2.7 的 3 倍速度。你可能对我们的 基准测试网站 和我们的 jit 文档 感兴趣。
您的测试不是基准测试:测试在 PyPy 下往往很慢,因为它们只运行一次;如果它们是好的测试,它们会测试代码中的各种极端情况。这对 JIT 编译器来说是一个糟糕的情况。还要注意,我们的 JIT 具有非常高的预热成本,这意味着任何程序在开始时都很慢。如果您想将计时与 CPython 进行比较,即使是相对简单的程序也需要运行至少一秒钟,最好至少运行几秒钟。大型复杂的程序需要更多时间来预热 JIT。
我写了一个 3 行的基准测试,它比 CPython 慢。为什么?¶
三行基准测试是既不执行任何操作(在这种情况下,PyPy 可能比 CPython 快得多),也不太可能执行大部分时间都在 C 中执行的操作的基准测试。
例如,一个循环,它重复执行一个复杂的 SQL 操作,只会衡量 SQL 数据库的性能。类似地,从斐波那契数列计算许多元素会构建非常大的整数,因此它只衡量长整数库的性能。这个库在 CPython 中是用 C 编写的,在 PyPy 中是用 RPython 编写的,但这归结为同一件事。
PyPy 加速了用 *Python* 编写的代码。
JIT 不能将已编译的机器代码转储并重新加载吗?¶
不,我们没有找到任何方法可以做到这一点。JIT 生成的机器代码包含大量常量地址 - 在生成机器代码时是常量。绝大多数可能根本不是您在可执行文件中找到的常量,并且具有一个不错的链接名称。例如,Python 类地址一直被使用,但 Python 类不是从可执行文件中静态获取的;每次重新启动程序时,它们都会被重新创建。这使得保存和重新加载机器代码变得完全不可能,除非采用一些非常先进的方法来将旧(现在已死)进程中的地址映射到新进程中的地址,包括检查对(现在已死)对象的所有先前假设是否仍然适用于新对象。
类型注释会提高 PyPy 的性能吗?¶
正在为提高性能而提出的类型注释的两个示例是 Cython 类型 和 PEP 484 - 类型提示。
Cython 类型在构造上类似于 C 声明。例如,局部变量或实例属性可以声明为 "cdef" int"
以强制使用机器字。这改变了通常的 Python 语义(例如,没有溢出检查,并且在尝试写入其他类型的对象时会出错)。它提供了一些额外的性能,但确切的好处尚不清楚:现在(2015 年 1 月)例如,我们正在研究一种技术,该技术将直接在实例上存储机器字整数,从而在没有用户提供的 "cdef" int"
的情况下提供部分好处。
PEP 484 - 类型提示,另一方面,如果您关注性能,则几乎完全没有用。首先,顾名思义,它们是 *提示*:它们仍然必须在运行时进行检查,就像 PEP 484 所说的一样。或者,也许您对一种模式感到满意,在这种模式下,当类型注释错误时,您会遇到非常模糊的崩溃;但即使在这种情况下,速度优势也会非常小。
为什么会有几个原因。其中一个原因是注释处于错误的级别(例如,PEP 484 的“int”对应于 Python 3 的 int 类型,它不一定适合一个机器字;更糟糕的是,“int”注释允许任意 int 子类)。另一个原因是生成好的代码需要更多信息(例如,“这里调用的这个 f()
实际上是指那里的这个函数,并且永远不会被猴子补丁” - 同样适用于 len()
或 list()
,顺便说一下)。第三个原因是 PyPy 的 JIT 跟踪中的一些“保护”实际上没有明显的对应类型(例如,“这个字典到目前为止使用的是没有覆盖 __hash__
的键,因此使用了更有效的实现”)。许多保护甚至与类型没有任何对应关系(“这个类属性没有被修改”;“循环计数器没有达到零,所以我们不需要释放 GIL”;等等)。
正如 PyPy 目前的工作方式,它能够推导出比 PEP 484 能够提供的更有用的信息,并且它可以自动工作。据我们所知,即使我们向 PyPy 添加其他技术,比如快速的第一遍 JIT,情况也是如此。
除了 Python 之外,我还能使用 PyPy 的翻译工具链来处理其他语言吗?¶
是的。翻译 PyPy 解释器的工具套件非常通用,可以用来创建针对任何语言的优化版本的解释器,而不仅仅是 Python。当然,这些解释器可以使用 PyPy 带给 Python 的相同功能:翻译成各种语言、无栈特性、垃圾收集、实现各种东西,比如任意长的整数等等。
目前,我们有 Topaz,一个 Ruby 解释器;Hippy,一个 PHP 解释器;一个 JavaScript 解释器 的初步版本(Leonardo Santagada 作为他的 PyPy 之夏项目);一个 Prolog 解释器(Carl Friedrich Bolz 作为他的学士论文);以及一个 SmallTalk 解释器(在一次冲刺中产生)。还有一个未完成的 Scheme 实现。
我如何参与 PyPy 开发?我可以参加冲刺吗?¶
当然可以参加冲刺!我们始终欢迎新人,并尽力帮助他们尽快开始参与项目。我们提供教程,并将他们与经验丰富的 PyPy 开发人员配对。新人应该具备一些 Python 经验,并在参加冲刺之前阅读一些 PyPy 文档。
参加冲刺通常是参与 PyPy 开发的最佳方式。如果你遇到困难或需要建议,请 联系我们。IRC 是获得反馈的最直接方式(至少在一天中的某些时间段内;大多数 PyPy 开发人员都在欧洲),而 邮件列表 更适合进行长时间的讨论。
我们还鼓励通过 GitHub 仓库进行参与,地址为 https://github.com/pypy/pypy。可以在 问题跟踪器 中提交和讨论问题,我们欢迎 拉取请求。
OSError: … 重新定位后无法恢复段保护… 帮助?¶
在 Linux 上,如果启用了 SELinux,你可能会遇到类似“OSError: externmod.so: 重新定位后无法恢复段保护:权限被拒绝”的错误。这是由配置期间对 C 编译器略微滥用造成的,可以通过以 root 权限运行以下命令来禁用
# setenforce 0
这将禁用 SELinux 的保护,并允许 PyPy 正确配置。如果你需要它,请务必重新启用它!
我应该如何报告错误?¶
我们的错误跟踪器在这里:https://github.com/pypy/pypy/issues/
缺少的功能或与 CPython 的不兼容性被认为是错误,并且我们欢迎它们。(另请参阅我们的 已知不兼容性 列表。)
对于“我遇到了 PyPy 崩溃或奇怪的异常”这类错误,请注意:**如果没有自己复现错误,我们就无能为力**。我们无法处理来自 gdb 的跟踪信息或核心转储。这不仅是因为标准 PyPy 是在没有调试符号的情况下编译的。真正的原因是,在 PyPy 中,C 级别的跟踪信息通常毫无帮助。调试 PyPy 可能很烦人。
这是一个清晰且有用的错误报告。(诚然,有时问题确实很难复现,但请尽量尝试。)
更详细地说明
- 首先,请提供确切的 PyPy 版本和操作系统。
- 如果我们知道错误是否可以在“
pypy --jit off
”上复现,这可能有助于我们集中搜索。如果“pypy --jit off
”始终有效,那么问题可能出在 JIT 上。否则,我们知道可以忽略这部分。 - 如果您只使用开源组件遇到了错误,请提供一个我们可以遵循的逐步指南,以便自己复现问题。不要假设我们了解 PyPy 以外的任何程序。我们希望得到一个可以逐点遵循的指南(无需猜测或自行摸索),在与您的机器类似的机器上,从一个裸机 PyPy 开始,直到我们看到相同的问题。(如果您能做到,可以尝试减少步骤数量和运行所需的时间,但这并非强制要求。)
- 如果错误涉及闭源组件,或者只是太多开源组件以至于我们无法自行安装它们,那么也许您可以提供一些临时 ssh 访问权限,以便我们访问可以复现错误的机器。或者,也许我们可以下载一个出现问题的 VirtualBox 或 VMWare 虚拟机。
- 如果提供访问权限需要我们使用 ssh 以外的工具、预约或签署 NDA,那么我们可以考虑以少量资金签订商业支持合同。
- 如果即使这样也不可能,那么抱歉,我们无能为力。
当然,您可以尝试自己调试问题,如果您在 #pypy IRC 频道上询问,我们可以帮助您入门,但请做好准备:调试一个恼人的 PyPy 问题通常涉及大量的 gdb 在自动生成的 C 代码中,以及至少对相关组件的一些了解,从 PyPy 自己的 RPython 源代码到 GC,以及可能还有 JIT。
为什么 PyPy 切换到 Git 并迁移到 GitHub?¶
PyPy 在 2010 年从 SVN 迁移到 Mercurial 和 bitbucket。在 2020 年,当 bitbucket 突然停止支持 Mercurial 时,我们讨论了是否要迁移到 Git/GitHub。当时我们得出结论,(1) Git 工作流并不像 Mercurial 工作流那样适合我们的风格,(2) 仅仅因为“每个人都在使用”而迁移到 github 是一种站不住脚的理由。
对于 (1),有一些问题,但也许最重要的是,PyPy 仓库有数千个命名分支。Git 没有等效的概念。Git 当然有分支,在 Mercurial 中称为书签。我们不是在谈论书签。
git 分支和命名分支之间的区别在一个只有 10 个分支的仓库中并不重要(无论大小)。但在 PyPy 的情况下,我们当时有 1840 个分支。当然,大多数分支现在已经关闭了。但我们确实希望保留(现在和将来)查看过去提交的能力,并知道它是在哪个分支中创建的。Git 分支和 Mercurial 分支之间存在差异,这在 Git 中并不总是可能的——我们仔细研究了,没有内置的方法可以获得这种工作流。
仍然不相信?考虑这个有三个提交的 git 仓库:提交 #2 的父节点是 #1,并且是 git 分支“A”的头部;提交 #3 的父节点也是 #1,但它是 git 分支“B”的头部。当提交 #1 创建时,它是在分支“A”还是“B”中?(它也可能是在另一个分支中,该分支的头部也被向前移动,甚至完全删除。)
在这段讨论之后不久,我们发现了 git notes,它允许为每个 Git 提交添加注释,以指示其来源分支。虽然这不是一个完美的解决方案,但它确实在一定程度上减轻了影响。
我们的开发工作转向将 PyPy 整合到开源项目空间:PyPy 开始在许多流行的 Python 项目上进行测试,包括在 conda-forge 上可用。这意味着降低与其他开发团队之间交互的摩擦变得很重要。事实证明,他们中的大多数使用 GitHub,而 GitHub 仅使用 Git。因此,在 2023 年底,我们 迁移到 Git/GitHub 用于我们的主要开发。那些没有太多公共交互的仓库仍然保留在 https://foss.heptapod.net/pypy/。
PyPy 的 Windows 64 位支持需要什么?¶
从 PyPy 7.3.5 开始,PyPy 支持 Windows 64 位。由于只有在该平台上 sizeof(long) != sizeof(void *)
,而 RPython 的底层数据类型是 long
,这被证明是一个挑战。看来我们已经克服了这个障碍,并欢迎帮助将 Windows 版本与 CPython 达到一致。特别是,我们仍然不支持 Windows 特定的功能,例如 winconsoleio
、Windows 审计事件和 Windows faulthandler
。性能可能落后于 Linux64,并且 wininstaller
分支仍未完成。
欢迎您的帮助!
PyPy 将支持 Python2 多长时间?¶
由于 RPython 是建立在 Python2 之上的,而且这种情况极不可能改变,因此 PyPy 的 Python2 版本将“永远”存在,即只要 PyPy 本身存在,它就会存在。