用 PyPy 让你的 Python 代码运行得更快( 二 )


PyPy是怎么来的?需要解释以下几点:
1、它的源代码是用RPython编写 。
2、RPython转换工具应用到了代码中 , 从根本上提高了代码效率 , 还可以将代码编译为机器代码 , 这就是Mac , Windows和Linux用户必须下载不同版本的原因 。
3、用上述方式生成的二进制可执行文件 , 就是你运行的Python解释器 。
你不需要执行上述所有这些步骤来使用PyPy 。 因为已经有提供您安装和使用的可执行文件 。
此外 , 由于在框架和实现中使用同一个词非常令人困惑 , PyPy背后的团队决定放弃这种双重用法 。 现在 , PyPy仅指Python解释器 , 而框架被称为RPython转换工具 。
接下来 , 您将了解在什么情况下使用PyPy比Python更好、更快 。
Just-In-Time (JIT) 编译器
在了解JIT编译器的内容之前 , 让我们先回顾一下已编译语言(如C)和解释语言(如JavaScript)的特性 。
在编译型语言写的程序执行之前 , 需要一个专门的编译过程 , 把源代码编译成机器语言的文件 , 如exe格式的文件 , 以后要再运行时 , 直接使用编译结果即可 , 如直接运行exe文件 。 因为只需编译一次 , 以后运行时不需要编译 , 所以编译型语言执行效率高 。 与特定平台相关 , 一般无法移植到其他平台 。 如C、C++、Objective等都属于编译型语言 。
解释型语言不需要事先编译 , 其直接将源代码解释成机器码并立即执行 , 所以只要某一平台提供了相应的解释器即可运行该程序 。 解释型语言每次运行都需要将源代码解释称机器码并执行 , 效率较低;只要平台提供相应的解释器 , 就可以运行源代码 , 所以可以方便源程序移植 。
然后还有一些编程语言 , 例如Python , 它混合了编译和解释 。 具体来说 , Python首先编译为字节码 , 然后由CPython解释 。 这使代码的性能优于用纯解释型语言编写的代码 , 并保持可移植性优势 。
但是它的性能仍然远远低于编译型语言 。 其原因是 , 编译后的代码可以执行许多优化 , 而字节码是不可能的 。
这就是JIT编译器的来源 。 它试图通过对机器代码进行一些编译和一些解释来同时获得两种优势 。 简而言之 , 以下是JIT编译为提供更快性能所采取的步骤:
1、识别代码中最常用的组件 , 如循环中的函数 。
2、运行时将这些部件转换为机器代码 。
3、优化生成的机器代码 。
4、用优化的机器代码版本取代之前的实现 。
还记得教程开头的两个嵌套循环吗?PyPy检测到重复执行相同操作时 , 将其编译为机器代码 , 优化机器代码 , 然后转换实现 。 这也是为什么您会看到这样的结果 。
垃圾回收机制
【用 PyPy 让你的 Python 代码运行得更快】无论何时创建变量、函数或任何其他对象 , 您的计算机都会给它们分配内存 。 最终 , 其中一些对象将不再需要 。 如果不及时清理 , 计算机可能会耗尽内存并使程序崩溃 。
在C和C++等编程语言中 , 通常必须手动处理此问题 。 其他编程语言(如Python和Java)会自动为您执行此操作 。 这被称为自动垃圾回收机制 。
CPython使用一种称为引用计数的技术 。 实质上 , 每当引用对象时 , Python对象的引用计数都会增加 , 而在取消引用该对象时则递减计数 。 当引用计数为零时 , CPython会自动为该对象调用内存释放函数 。 这是一种简单有效的技术 , 但有一个陷阱 。
当大型对象树的引用计数变为零时 , 所有相关对象将被释放 。 因此 , 您可能有很长的暂停时间 , 在此期间您的程序根本无法执行 。
此外 , 还有一个例子 , 其中引用计数根本不起作用 。 如下所示:
class A(object):passa = A()a.some_property = adel a在上面的代码中 , 定义了新的类 , 然后 , 创建一个实例 , 并将其指定为其自身的属性 。 最后 , 删除实例 。
此时 , 实例将不再可访问 。 但是 , 引用计数不会从内存中删除实例 , 因为它具有对自身的引用 , 因此引用计数不是零 。 此问题被称为引用循环 , 无法使用引用计数解决 。
这是CPython使用的另一个工具 , 称为循环垃圾回收器 。 它从已知根(如类型对象)开始遍历内存中的所有对象 。 然后 , 它标识所有可访问的对象 , 并释放不可访问的对象 , 因为它们不再存在 。 这样就解决了引用循环问题 。 但是 , 当内存中存在大量对象时 , 它可能会创建更明显的暂停 。
另一方面 , PyPy不使用引用计数 。 相反 , 它只使用第二种技术 , 即循环查找器 。 也就是说 , 它会定期从根开始遍历活动对象 。 这使PyPy比CPython具有一些优势 , 因为它不需要考虑引用计数 , 从而使内存管理花费的总时间少于CPython 。