本文转载自公众号“读芯术”(ID:AI_Discovery)。python
当咱们说起Python时,经常指的是CPython,即C语言实现的Python,这就是PyPy发挥做用的地方啦。实话讲,Python很慢,而用Python(!)编写的Python在运行时,执行速度比CPython快4.4倍。这是如何实现的呢?git
正如Python之父吉多·范罗苏姆(Guido van Rossum)所说:“若是想让代码运行得更快,应该使用PyPy。”程序员
第一批使用PyPy的程序员竞争力很强。有时候,就算花了不少时间用Python编写解决方案,最后也会失败,可是在PyPy中运行一样的代码就会成功。这是为何?github
不一样的方法算法
Python是一种解释性语言,CPython逐行读取并执行源代码。解释性语言(包括JavaScript)有不少优势:编程
易于编写c#
元编程能力强大后端
编译不会失败数据结构
固然,它也有一些缺点:多线程
在解析源代码时,性能消耗巨大
编译不会失败
能够看到,其优缺点都包括了“编译不会失败”。这有时候基于不一样的行为(好比原型设计或生产),但我仍然倾向于将其视为一个缺点。而PyPy稍有不一样,它不是一个纯粹的解释器,而是实现了跟踪即时(JIT)编译。
即时编译
即时编译介于解释和传统提早编译之间。即时编译器并不执行源代码自己,而是生成一组可当即执行的低层指令(一般是汇编)。
这个插图能够帮助咱们理解其中的区别。在编译语言(C、C++、Rust)中,编译阶段严格按照开发环境划分。它生成一个可运行的二进制文件,而后将其发送到生产环境中。
在解释性语言中,状况正好相反:源代码(在*化以后,hello JS)被所有推到生产环境中,由解释器执行。即时编译语言也运送源代码(或字节码,如Java或C#),但它是做为一种常规编译语言编译和运行,而不是逐行解释。
并非说一种方法比另外一种好,每一个用例都将根据其独特的需求指定正确的选择。可是,若是性能很是关键,使用Python解释器感受很舒服,那么你能够选择PyPy。
跟踪即时编译
与编译或解释同样,实现即时编译也有不一样的方法。传统的方法是方法/函数做用域。当代码调用一个函数时,即时编译器将得到它的源代码,进行编译并提供可执行的二进制文件。而PyPy采用的方法稍微不一样,这是由Python的特性和用例决定的。
PyPy的编译器不是按方法调用,而是计算循环。因为Python在数据科学、机器学习、高级算法以及数据结构中使用普遍,这是最有意义的。简而言之,PyPy是Python之上的一个优化层。
PyPy并不像所理解的那样严格处理循环。除了常规的for和while结构外,若是PyPy检测到编译工做有价值,它会优化任意代码块。
缺点
图源:unsplash
固然,PyPy也有缺点。即便它能大大提升性能,你也须要知道如下几点:
不支持全部的Python。它支持大部分代码,可是若是处理底层的CPython实现细节或者有Cython绑定,它就不起做用了。
回溯将来。PyPy当前版本是3.4,而Python目前稳定在3.8,可是回溯法是Python开发者擅长的技术。
优化是好事,但不是编写糟糕代码的借口。若是代码没法被人读懂, PyPy又怎能理解呢?
全局解释器锁还在。若是执行繁重的多线程操做,请选择其余实现方法。
与任何工具同样,在采用以前,咱们应该考虑全部的细节。不过,下次登陆到Codeforces接受挑战时,不妨试用一下PyPy。有可能O(n^3)错误代码会经过,而在纯Python中,只有O(n log n)会经过。
源代码
虽然本文没有涉及CPython和PyPy的源代码,可是这些文件在CPython (C代码)和PyPy (Python代码)中实现了阶乘函数。
CPython:https://github.com/python/cpython/blob/master/Modules/mathmodule.c
PyPy:https://github.com/mozillazg/pypy/blob/50d6bf76ef1f93c234ab42d4dd4a3b974f9665d6/pypy/module/math/app_math.py
其余实现
除了CPython和PyPy,还有其余值得注意的Python实现:
StacklessPython。它和Python相同,但没有全局解释器锁,《星战前夜》游戏后端用的就是它。
IronPython是一种在 NET上实现的 Python 语言,它为Python和c#代码提供了很是简单的交互操做。
JPython同理,可是有Java。
去试试使用PyPy来体验代码加速度吧!
【责任编辑:赵宁宁 TEL:(010)68476606】