- 内容提要
- 前言
- 作者简介
- 封面简介
- 第1章 理解高性能 Python
- 第2章 通过性能分析找到瓶颈
- 2.1 高效地分析性能
- 2.2 Julia 集合的介绍
- 2.3 计算完整的 Julia 集合
- 2.4 计时的简单方法——打印和修饰
- 2.5 用 UNIX 的 time 命令进行简单的计时
- 2.6 使用 cProfile 模块
- 2.7 用 runsnakerun 对 cProfile 的输出进行可视化
- 2.8 用 line_profiler 进行逐行分析
- 2.9 用 memory_profiler 诊断内存的用量
- 2.10 用 heapy 调查堆上的对象
- 2.11 用 dowser 实时画出变量的实例
- 2.12 用 dis 模块检查 CPython 字节码
- 2.13 在优化期间进行单元测试保持代码的正确性
- 2.14 确保性能分析成功的策略
- 2.15 小结
- 第3章 列表和元组
- 第4章 字典和集合
- 第5章 迭代器和生成器
- 第6章 矩阵和矢量计算
- 第7章 编译成 C
- 第8章 并发
- 第9章 multiprocessing 模块
- 第10章 集群和工作队列
- 第11章 使用更少的 RAM
- 第12章 现场教训
7.11 PyPy
PyPy是一个Python语言的替代实现,包含了一个可跟踪的即时编译器,它兼容Python2.7,也有实验性的Python3.2版本。
PyPy是一个普适性的CPython替代品,提供了几乎所有的内置模块。该项目包含了RPython翻译工具链,被用来构建PyPy(也可用来构建其他解释器)。PyPy中的JIT编译器很高效,能够看到良好的速度提升而几乎或完全不需要你这边的任何工作。看看12.5节,这是一个大规模PyPy部署成功的故事。
PyPy运行我们的纯Python版的Julia演示而不做任何改动。使用CPython,它用了11秒,而使用PyPy,它用了0.3秒。这意味着PyPy取得了很接近于例7-7中的Cython的成果,而完全没有任何工作量——令人印象很深刻!就如我们在讨论Numba时观察到的那样,如果在同一会话中再次运行计算,那么第2次(以及接下来每次)会比第1次刚编译完那会儿要跑得快。
PyPy支持所有内置模块的事实是很有趣的——这意味着可以像在CPython中一样用多进程方式工作。如果你在运行内置模块时遇到问题,你能用多进程方式并行地去运行,除此之外,还会得到你所希望的速度提升。
PyPy的速度随着时间而演进。图7-6中的图表来自speed.pypy.ord,会告诉你它的成熟度。这些速度测试反映了多种多样的用户案例,不只是数学运算。很清楚PyPy提供了比CPython更快的体验。
图7-6 每一个新版本PyPy提供了速度提升
7.11.1 垃圾收集的差异
PyPy使用了不同于CPython的垃圾收集器,这能导致你的代码行为产生不明显的改变。尽管CPython使用引用计数,PyPy却使用了修改后的标记和清理方法来推迟清除不用的对象。这两者都是对Python规范的具体实现,你只需注意当交换时,需要做一些代码改动。
一些在CPython中见到的编码方式依赖于引用计数的行为——尤其是冲刷文件(如果这些文件打开后被写入了,而不去显式地关闭文件)。使用PyPy,相同的代码可以运行,但是对文件的更新可能会推迟冲刷到磁盘上,直到垃圾收集器下次运行时才会被冲刷。一种既能使用PyPy,又能使用Python工作的替代方式就是用with来使用上下文管理器去打开和自动关闭文件。在PyPy的网站上的“PyPy与CPython的差异”那页列出了细节,垃圾收集器的实现细节在网站上也有。
7.11.2 运行PyPy并安装模块
如果你从没有运行过一种替代的Python解释器,你可能会受益于一个短小的例子。假设你已经下载并解压出PyPy,你现在会得到一个包含bin目录的文件夹结构。按例7-18所示运行它来启动PyPy。
例7-18 运行PyPy来看看它实现了Python 2.7.3
$ ./bin/pypy Python 2.7.3 (84efb3ba05f1, Feb 18 2014, 23:00:21) [PyPy 2.3.0-alpha0 with GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. And now for something completely different: <arigato> (not thread-safe, but well, nothing is)''
注意到PyPy作为Python 2.7.3运行。现在我们需要设置pip,并想要安装ipython(注意IPython以我们曾见过的相同的Python 2.7.3的编译包启动)。如果你不求助于现成的发布包或包管理器来安装pip,这些显示在例7-19中的步骤和你可能已经用CPython做过的步骤一模一样。
例7-19 为PyPy安装pip用以安装第三方模块,比如IPython
$ mkdir sources # make a local download directory $ cd sources # fetch distribute and pip $ curl -O http://python-distribute.org/distribute_setup.py $ curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py # now run the setup files for these using pypy $ ../bin/pypy ./distribute_setup.py ... $ ../bin/pypy get-pip.py ... $ ../bin/pip install ipython ... $ ../bin/ipython Python 2.7.3 (84efb3ba05f1, Feb 18 2014, 23:00:21) Type "copyright", "credits" or "license" for more information. IPython 2.0.0—An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. Help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details.
注意PyPy对像numpy之类的项目的支持非同寻常(有一个经由cpyext的桥接层,但是它太慢了,以致于对numpy没什么用),所以不要期待PyPy对numpy做强力支持。PyPy的确有一个实验性的numpy移植版本,称为“numpypy”(安装指导在Ian的博客上),但是当前它没有提供任何有用的速度优势[1]。
如果你需要其他包,只要是纯Python的,就可能安装上,而任何依赖于C扩展库的就可能无法有效工作。PyPy没有引用计数的垃圾收集器,任何为CPython编译的包会使用支持CPython垃圾收集器的库调用。PyPy有一个变通方法,但是会增加很多开销。在实践中,去尝试强迫让更老的扩展库直接和PyPy一起工作是无效的。PyPy的建议就是如果有可能就尝试移除任何C扩展代码(它或许只是用来让Python代码运行更快,现在那是PyPy的工作了)。PyPy的维基维护了一串可兼容的模块列表。
PyPy的另一个缺点是它使用了很多内存。每个发布版在这方面都有改良,但在实践中,它可能还是使用了比CPython更多的内存。然而内存相对便宜,所以用来为性能提升做交换还是有意义的。一些用户也已报道了在使用PyPy时有更少的内存占用。如果这仍然对你很重要,那就用具有代表性的数据去做实验。
PyPy受限于全局解释器锁,但是开发团队正在搞一个叫作软件事务内存(STM)的项目来企图移除GIL的必要性。STM有一点像数据库事务。它是一个并发控制机制,应用于内存访问。如果在相同的内存空间上发生冲突,它能够回滚改动。集成STM的目标是能够让高并发系统有一种并发控制的方式,在运算上丧失了一些效率,但是通过不强迫用户处理并发存取控制的所有方方面面来提高程序员的生产率。
推荐的剖析器工具是jitviewer和logparser。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论