大部门人使用python是因为它非常方便,而不是因为它速度快。过多的第三方库使得python相比于Java和C的性能差距较大。但也是可以理解的,因为在大部分情况下,开发速度优先于执行速度。
但也不要过于担心python的速度,这并不一定是一个非此即彼的命题。经过适当优化,Python应用程序可以以惊人的速度运行——也许还不能达到Java或C语言的速度,但是对于Web应用程序、数据分析、管理和自动化工具以及大多数其他用途来说,速度已经足够快了。快到你可能会忘记了你是在用应用程序性能换取开发人员的生产力。
优化Python性能不能单从一个角度上看。而是应用所有可用的优化方法,并选择最适合当前场景的多种方法的集合。(Dropbox的员工有一个最令人瞠目的例子,展示了python优化的强大功能,点击链接查看。)
在本文中,我将简单讲述许多常见的python优化方法。有些是临时措施,只需要简单地将一项转换为另一项(例如更换Python解释器),但是那些带来最大收益的方法将需要更详细的工作。
1. 算速度、算速度、算速度!
如果你不能够找出速度慢的原因所在,你就不能确定你的python应用程序为什么运行地不够理想。
计算的方法有很多,你可以尝试python内置的 cProfile模块 进行简单的计算分析,如果需要更高的精度(计算每行语句运行时间),可以使用 line_profiler 第三方工具。通常而言,从计算程序的函数运行时间进行分析就能够给你提供改进方案,所以推荐使用 profilehooks 第三方库,它能计算单个函数的运行时间。
你可能需要更多的挖掘才能发现为什么你的程序某个地方这么慢、怎么修复它。重点在于缩小你的排查范围,逐渐细化到某条语句上。
2.缓存需要重复使用的数据
当你可以把需要计算出来的数据保存下来的时候,千万不要重复上千次去计算它。如果你有一个经常需要使用的函数,而且返回的是可预测的结果,Python已经给你提供了一个选项,能将其缓存到内存中。后续的函数调用如果是一样的,将立即返回结果。
有许多方法都可以做到,比如说:使用python的一个本地库:functools,拥有一个装饰器,叫@functools.lru_cache
,它能够缓存函数最近的N个调用,当缓存的值在特定时间内保持不变的时候这个非常好用,比如说列出最近一天使用的物品。
3.将数学计算重构为NumPy
如果你的Python程序中有基于矩阵或数组的数学运算,并且希望更高效地对它们进行计算,那么你就应该使用NumPy,因为它通过使用C库来完成繁重的工作,比原生python解释器能更快得处理数组,而且能比Python内置数据结构更有效地存储数字数据。
NumPy还可以极大地加速相对普通的数学运算。该包为许多常见的Python数学操作(如min和max)提供了替换,这些操作的速度比原始Python快很多倍。
NumPy的另一个优点是对大型对象(比如包含数百万项的列表)能更有效地使用内存。一般来说,如果用传统的Python表示类似于NumPy中的大型对象,那么它们将占用大约四分之一的内存。
重写Python算法以使用NumPy需要做一些工作,因为需要使用NumPy的语法重新声明数组对象。但是NumPy在实际的数学操作中使用Python现有的习惯用法(+、-等等),所以切换到NumPy并不会让人太迷惑。
4.使用C库
NumPy使用C编写的库是一种很好的方法。如果现有的C库能够满足你的需求,那么Python及其生态系统将提供几个选项来连接到该库并利用其提高速度。
最常用的方法是Python的ctypes库。因为ctypes与其他Python应用程序广泛兼容,所以它是最好的起点,但也并不是唯一的,CFFI项目为C. Cython提供了一个更优雅的接口(参见下面第五点),也可以用来包装外部库,代价是你必须学习Cython的标记方法。
5.转换为Cython
如果你非常追求速度,应该用C而不是python,但是对于我这种有python依赖症的人来说,对C天生就有种畏惧。现在有一个很好的解决办法出来了。
Cython允许Python用户方便地访问C的速度。现有的Python代码可以逐步转换为C :首先通过Cython将所述代码编译为C,然后通过添加类型注释以获得更快的速度。
不过,Cython不能变魔术。按原样转换为Cython的代码通常运行速度通常不会加快超过15%到50%,因为该级别的大多数优化都集中在减少Python解释器的开销上。只有在为Cython模块提供类型注释时才允许将相关代码转换为纯C,这时候的速度提升才最大。
6.使用多线程
由于全局解释器锁(GIL)的存在,Python规定一次只执行一个线程,以避免在使用多个线程时出现状态问题。它的存在有充分的理由,但依然很讨厌。
随着时间的推移,GIL的效率显著提高(这是为什么你应该用python3的其中一个原因),但是核心问题仍然存在。为了解决这个问题,Python提供了多处理模块(multiprocessing)来在单独的内核上运行Python解释器的多个进程。状态可以通过共享内存或服务器进程共享,数据可以通过队列或管道在进程实例之间传递。
您仍然必须手动管理进程之间的状态。此外,启动多个Python实例并在它们之间传递对象也会涉及不少开销。尽管如此,多处理库还是很有用的。另外,使用了C库的Python模块和包(如NumPy)也是完全避免GIL的。这也是推荐它们提高速度的另一个原因。
7.知道你的库正在干嘛
简单地输入import xyz是多么方便啊!但是你知道,第三方库虽然可以改变应用程序的性能,但并不总是向好的方向发展。
有时,你加了某个模块的时候,应用程序反而变慢了,这就是来自特定库的模块构成瓶颈。同样,仔细计算运行时间也会有所帮助,有时则不那么明显。示例:Pyglet是一个用于创建窗口图形化应用程序的库,它自动启用调试模式,这将极大地影响性能,直到显式禁用为止。除非阅读文档,否则你可能永远不会意识到这一点。多读书,多了解情况。
8.意识到平台间的速度差异
Python的运行是跨平台的,但这并不意味着每个操作系统(Windows、Linux、OS X)的特性都可以在Python下抽象出来。大多数情况下,你需要了解平台的细节,比如路径命名约定等等。
但在性能方面,理解平台的差异也很重要。例如,有些python脚本需要使用Windows的api去访问一些特定的应用,这些应用也可能会减慢运行速度。
9.使用pypy运行程序
CPython是Python最常用的优化方案,因为它优先考虑兼容性而不是原始速度。对于那些想把速度放在首位的程序员来说,PyPy是一个Python更好的方案,它配备了一个JIT编译器来加速代码的执行(编译为C代码)。
因为PyPy被设计为CPython的一个临时替代品,所以它是获得快速性能提升的最简单方法之一。大多数Python应用程序将完全按原样运行在PyPy上。然而,充分利用PyPy可能需要不断地测试。你将会发现,长时间运行的应用程序更有可能从PyPy中获得了最大的性能收益,因为编译器会随着时间分析执行情况。对于运行和退出的简短脚本,最好使用CPython,因为性能的提高不足以克服JIT的开销。
10.升级到python3
如果你用的是python2。而且没有压倒一切的理由(比如一个不兼容的模块)坚持使用它,你应该跳到python3。
Python 3中还有许多Python 2.x中没有的构造和优化。例如,Python 3.5使异步变得不那么棘手,async和await关键字成为语言语法的一部分。Python 3.2对全局解释器锁进行了重大升级,显著改进了Python处理多线程的方式。
以上就是全部十点的改进方案啦,尽管使用了这些方法可能运行速度还是无法超过C和Java,但是代码跑得快不快,不取决于语言,而是取决于人,况且Python本身不必是最快的,只要足够快就行。
如果你希望我们今天的Python 教程,请持续关注我们,如果对你有帮助,麻烦在下面点一个赞/在看哦有任何问题都可以在下方留言区留言,我们都会耐心解答的!
Python实用宝典 (pythondict.com)
不只是一个宝典
欢迎关注公众号:Python实用宝典
评论(0)