尽管Python近年来备受欢迎且极具便利性,但广大Python用户都明白,在涉及CPU密集型任务时,其执行速度相较于C、Java或JavaScript等语言会慢得多,速度差异甚至可以达到几个数量级。
但是有几个项目吸纳Python的所有优点的同时,决定从内到外提升它的性能。
如果你想让Python在相同的硬件上运行得更快,你有两个基本选项,每个选项都有一个缺点。
- 你可以创建一个语言所使用的默认运行时的替代品 (例如,CPython)--这是个大工程
- 你可以重写现有的Python代码,对其进行速度优化,这意味着程序员要做更多的工作
以下是六种提高Python性能的方法。每一种都使用了这两种方法中的一种,或者是两种方法的结合。
PyPy
在替代CPython的候选者中,PyPy是其中较为突出的一款。
它也最有可能成为默认版本,因为它与现有的 Python 代码高度兼容。
PyPy使用的是及时编译(JIT),谷歌Chrome的V8 JavaScript引擎也使用同样的技术来加快该语言的速度。
虽然PyPy曾经偏爱于Python 2而不是Python 3,但PyPy的最新版本支持Python 3.6和Python 3.7以及Python 2.7。
但,PyPy有一个明显的缺点,PyPy没有很好地集成用于加速Python性能的常用库,如NumPy。
然而,最近的版本在很大程度上解决了这个问题。
PyPy仍有其他限制,它最适合像服务器这样的长期运行程序,而临时运行的脚本,因为它的性能优势要在一些预热时间之后才会真正体现出来。而且,它的可执行文件比CPython占用的空间更大。
Pyston
Pyston项目最初是由Dropbox创建的,但后来被重写,也使用JIT来加快Python的速度。
它最初的化身使用了LLVM编译器基础架构来实现这一目的,但重写后放弃了LLVM,而采用了开销更低的hand-rolled汇编器。
重写也使用CPython代码作为项目的基础,所以它与传统Python的开箱即用兼容性更高。
Pyston的速度还不是很显著--平均快了 20% 左右--但这个项目还处于起步阶段。
Nuitka
还有一些思路并不是为了优化和提升Python的运行时间,而是完全放弃对于它的优化。
进而寻求将Python代码移植到本地高速运行的语言中。
Nuitka就利用了这一思路,它可以将Python转换为C++代码,并且可以自动打包所有CPython运行时所需的文件。
Nuitka的长期计划包括允许Nuitka编译的Python直接与C代码接口,从而实现更高的速度。
Cython
Cython(Python的C扩展)是一种将代码编译成C语言并提供与C/C++代码接口的版本,这是为Python编写C扩展的一种方式,它将C或C++代码包装起来,并赋予它一个简单的Python接口,Cython也可以用来增量加速Python函数。
它的缺点是,Cython使用自己特有的语法来发挥它的作用,这也给移植代码带来一定困难。
尽管如此,为了速度,Cython 提供了前几个Python版本中没有的优势,例如,利用C语言中的变量类型。
许多Python的科学软件包,如Scikit-learn,都借鉴了Cython的这种特性,以保持操作的精简和快速。
Numba
Numba结合了前面两种方法。
像Cython一样,它加快了语言中最需要的部分(通常是CPU绑定的数学运算);
像PyPy和Pyston一样,它使用JIT编译。
用Numba编译的函数可以用装饰器指定,使用非常简单。此外,Numba与它我们熟悉的库配合得很好,比如NumPy。
typed_python
typed_python 项目是 A Priori 投资公司支持的一个新生事物,它采用了不同于上述任何一个项目的方法。
它为Python提供了一个强类型数据结构的集合,这些数据结构的类型受到限制。
例如,我们可以创建一个只接受整数的列表。
有了它,我们就可以生成高度优化的代码,这些代码运行得更快,并尽可能地利用处理器的并行性。
我们可以用传统的 Python 编写大部分程序,然后在一个特定的函数中使用 typed_python 来加速其操作,这就像 Cython 可以用来选择性的加速那些可能成为瓶颈的部分一样。
结语
Python之父Guido van Rossum 坚定地认为,Python 的许多性能问题都可以追溯到对该语言的不当使用。
例如,CPU负荷大的处理可以通过这里提到的一些方法来加速:
- 使用NumPy (用于数学与运算)
- 使用多处理扩展
- 调用外部C代码,从而避免全局解释器锁(GIL)
GIL是Python慢的根源,但由于目前还没有可行的方法来替代 Python 中的 GIL,所以就需要其他人提出短期的解决方案--也许还有长期的解决方案。